Skip to content

Commit

Permalink
feat(install): Support foo@version like cargo-add
Browse files Browse the repository at this point in the history
In rust-lang#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).
  • Loading branch information
epage authored and Hezuikn committed Sep 22, 2022
1 parent 8c5fe7e commit 4ceec15
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 3 deletions.
22 changes: 20 additions & 2 deletions src/bin/cargo/commands/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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))
}
49 changes: 48 additions & 1 deletion tests/testsuite/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

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

0 comments on commit 4ceec15

Please # to comment.