diff --git a/src/bin/cargo/commands/install.rs b/src/bin/cargo/commands/install.rs index f8410e625178..82dc31fc158d 100644 --- a/src/bin/cargo/commands/install.rs +++ b/src/bin/cargo/commands/install.rs @@ -101,8 +101,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let krates = args .values_of("crate") .unwrap_or_default() - .map(|k| (k, version)) - .collect::>(); + .map(|k| resolve_crate(k, version)) + .collect::>>()?; let mut from_cwd = false; @@ -174,3 +174,21 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } Ok(()) } + +fn resolve_crate<'k>( + mut krate: &'k str, + mut version: Option<&'k str>, +) -> crate::CargoResult<(&'k str, Option<&'k str>)> { + if let Some((k, v)) = krate.split_once('@') { + if version.is_some() { + anyhow::bail!("cannot specify both `@{v}` and `--version`"); + } + if k.is_empty() { + // by convention, arguments starting with `@` are response files + anyhow::bail!("missing crate name for `@{v}`"); + } + krate = k; + version = Some(v); + } + Ok((krate, version)) +} diff --git a/tests/testsuite/install.rs b/tests/testsuite/install.rs index c23d9ba5357a..eee7c7a563bc 100644 --- a/tests/testsuite/install.rs +++ b/tests/testsuite/install.rs @@ -1382,7 +1382,7 @@ fn vers_precise() { } #[cargo_test] -fn version_too() { +fn version_precise() { pkg("foo", "0.1.1"); pkg("foo", "0.1.2"); @@ -1391,6 +1391,53 @@ fn version_too() { .run(); } +#[cargo_test] +fn inline_version_precise() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo@0.1.1") + .with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])") + .run(); +} + +#[cargo_test] +fn inline_version_multiple() { + pkg("foo", "0.1.0"); + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + pkg("bar", "0.2.0"); + pkg("bar", "0.2.1"); + pkg("bar", "0.2.2"); + + cargo_process("install foo@0.1.1 bar@0.2.1") + .with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])") + .with_stderr_contains("[DOWNLOADED] bar v0.2.1 (registry [..])") + .run(); +} + +#[cargo_test] +fn inline_version_without_name() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install @0.1.1") + .with_status(101) + .with_stderr("error: missing crate name for `@0.1.1`") + .run(); +} + +#[cargo_test] +fn inline_and_explicit_version() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo@0.1.1 --version 0.1.1") + .with_status(101) + .with_stderr("error: cannot specify both `@0.1.1` and `--version`") + .run(); +} + #[cargo_test] fn not_both_vers_and_version() { pkg("foo", "0.1.1");