]> git.proxmox.com Git - cargo.git/commitdiff
feat(install): Support `foo@version` like cargo-add
authorEd Page <eopage@gmail.com>
Wed, 20 Apr 2022 14:53:35 +0000 (09:53 -0500)
committerEd Page <eopage@gmail.com>
Thu, 28 Apr 2022 01:44:26 +0000 (20:44 -0500)
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).

src/bin/cargo/commands/install.rs
tests/testsuite/install.rs

index f8410e6251782c90a3315e230003b52fd1adf397..82dc31fc158deb2c94ce5845e05189767e56b0e9 100644 (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))
+}
index e09e88a70b5313f705a1696f83d1fc812f84da0b..113e45a9c85c750ec201351a3365a5dbdcb1954e 100644 (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");