diff --git a/src/bin/cargo/commands/yank.rs b/src/bin/cargo/commands/yank.rs index fea6cbf31b66..ceaffb915ca2 100644 --- a/src/bin/cargo/commands/yank.rs +++ b/src/bin/cargo/commands/yank.rs @@ -10,8 +10,7 @@ pub fn cli() -> App { .arg( opt("version", "The version to yank or un-yank") .alias("vers") - .value_name("VERSION") - .required(true), + .value_name("VERSION"), ) .arg(opt( "undo", @@ -28,10 +27,15 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let registry = args.registry(config)?; + let (krate, version) = resolve_crate(args.value_of("crate"), args.value_of("version"))?; + if version.is_none() { + return Err(anyhow::format_err!("`--version` is required").into()); + } + ops::yank( config, - args.value_of("crate").map(|s| s.to_string()), - args.value_of("version").map(|s| s.to_string()), + krate.map(|s| s.to_string()), + version.map(|s| s.to_string()), args.value_of("token").map(|s| s.to_string()), args.value_of("index").map(|s| s.to_string()), args.is_present("undo"), @@ -39,3 +43,21 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { )?; Ok(()) } + +fn resolve_crate<'k>( + mut krate: Option<&'k str>, + mut version: Option<&'k str>, +) -> crate::CargoResult<(Option<&'k str>, Option<&'k str>)> { + if let Some((k, v)) = krate.and_then(|k| k.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 = Some(k); + version = Some(v); + } + Ok((krate, version)) +} diff --git a/tests/testsuite/yank.rs b/tests/testsuite/yank.rs index 259c5946566f..3447aac28853 100644 --- a/tests/testsuite/yank.rs +++ b/tests/testsuite/yank.rs @@ -13,7 +13,7 @@ fn setup(name: &str, version: &str) { } #[cargo_test] -fn simple() { +fn explicit_version() { registry::init(); setup("foo", "0.0.1"); @@ -46,3 +46,116 @@ Caused by: ) .run(); } + +#[cargo_test] +fn inline_version() { + registry::init(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank foo@0.0.1 --token sekrit").run(); + + p.cargo("yank --undo foo@0.0.1 --token sekrit") + .with_status(101) + .with_stderr( + " Updating `[..]` index + Unyank foo@0.0.1 +error: failed to undo a yank from the registry at file:///[..] + +Caused by: + EOF while parsing a value at line 1 column 0", + ) + .run(); +} + +#[cargo_test] +fn version_required() { + registry::init(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank foo --token sekrit") + .with_status(101) + .with_stderr("error: `--version` is required") + .run(); +} + +#[cargo_test] +fn inline_version_without_name() { + registry::init(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank @0.0.1 --token sekrit") + .with_status(101) + .with_stderr("error: missing crate name for `@0.0.1`") + .run(); +} + +#[cargo_test] +fn inline_and_explicit_version() { + registry::init(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank foo@0.0.1 --version 0.0.1 --token sekrit") + .with_status(101) + .with_stderr("error: cannot specify both `@0.0.1` and `--version`") + .run(); +}