feat(install): Support foo@version like cargo-add

In #10472, cargo-add was merged with support for an inline version
syntax of `foo@version`.  That also served as the change proposal for
extending that syntax to `cargo install` for convinience and consistency.

While both commands are specifying a version-req, `cargo-install` has an
implicit-but-required `=` operand while `cargo-add` allows any operand.

This doesn't use the full `pkgid` syntax because that allows syntax that
is unsupported here.

This doesn't use `cargo-add`s parser because that is for version reqs.

I held off on reusing the parser from `cargo-yank` because they had
different type system needs and the level of duplication didn't seem
worth it (see Rule of Three).
This commit is contained in:
Ed Page 2022-04-20 09:53:35 -05:00
parent ac25dc7158
commit 07681341da
2 changed files with 68 additions and 3 deletions

View File

@ -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::<Vec<_>>();
.map(|k| resolve_crate(k, version))
.collect::<crate::CargoResult<Vec<_>>>()?;
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))
}

View File

@ -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");