From 98326ac0f554af5db977147c8d912879edde09e4 Mon Sep 17 00:00:00 2001 From: dawe Date: Mon, 21 Apr 2025 18:14:40 +0200 Subject: [PATCH] feat(install): check if given crate argument would be valid with inserted @ symbol, suggest fixed argument --- Cargo.lock | 1 + Cargo.toml | 1 + src/bin/cargo/commands/install.rs | 17 +++++++++++++++++ tests/testsuite/install.rs | 5 +++-- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c63294ade..e00832c40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -376,6 +376,7 @@ dependencies = [ "tracing-subscriber", "unicase", "unicode-width", + "unicode-xid", "url", "walkdir", "windows-sys 0.59.0", diff --git a/Cargo.toml b/Cargo.toml index 8348c65bb..65bb7ff87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -216,6 +216,7 @@ tracing = { workspace = true, features = ["attributes"] } tracing-subscriber.workspace = true unicase.workspace = true unicode-width.workspace = true +unicode-xid.workspace = true url.workspace = true walkdir.workspace = true diff --git a/src/bin/cargo/commands/install.rs b/src/bin/cargo/commands/install.rs index dac5f7394..5fefb7adf 100644 --- a/src/bin/cargo/commands/install.rs +++ b/src/bin/cargo/commands/install.rs @@ -8,6 +8,7 @@ use cargo::ops; use cargo::util::IntoUrl; use cargo::util::VersionExt; use cargo::CargoResult; +use cargo_util_schemas::manifest::PackageName; use itertools::Itertools; use semver::VersionReq; @@ -133,6 +134,22 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { .collect::>>()?; for (crate_name, _) in krates.iter() { + let package_name = PackageName::new(crate_name); + if !crate_name.contains("@") && package_name.is_err() { + for (idx, ch) in crate_name.char_indices() { + if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') { + let mut suggested_crate_name = crate_name.to_string(); + suggested_crate_name.insert_str(idx, "@"); + if let Ok((_, Some(_))) = parse_crate(&suggested_crate_name.as_str()) { + let err = package_name.unwrap_err(); + return Err( + anyhow::format_err!("{err}\n\n\ + help: if this is meant to be a package name followed by a version, insert an `@` like `{suggested_crate_name}`").into()); + } + } + } + } + if let Some(toolchain) = crate_name.strip_prefix("+") { return Err(anyhow!( "invalid character `+` in package name: `+{toolchain}` diff --git a/tests/testsuite/install.rs b/tests/testsuite/install.rs index 123958740..ac181b6fc 100644 --- a/tests/testsuite/install.rs +++ b/tests/testsuite/install.rs @@ -402,8 +402,9 @@ fn missing_at_symbol_before_version() { cargo_process("install foo=0.2.0") .with_status(101) .with_stderr_data(str![[r#" -[UPDATING] `dummy-registry` index -[ERROR] could not find `foo=0.2.0` in registry `crates-io` with version `*` +[ERROR] invalid character `=` in package name: `foo=0.2.0`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) + +[HELP] if this is meant to be a package name followed by a version, insert an `@` like `foo@=0.2.0` "#]]) .run();