From 55f8a260e0fb29725c80ec7af896e6b8c60503df Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 9 Jun 2022 16:35:08 -0500 Subject: [PATCH 1/3] refactor(cli): Centralize reading of verbose --- src/bin/cargo/cli.rs | 6 +++--- src/bin/cargo/commands/tree.rs | 2 +- src/bin/cargo/commands/version.rs | 2 +- src/cargo/util/command_prelude.rs | 10 ++++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index 19e40d588e8..2311b42095b 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -112,7 +112,7 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'", return Ok(()); } - let is_verbose = expanded_args.occurrences_of("verbose") > 0; + let is_verbose = expanded_args.verbose() > 0; if expanded_args.is_present("version") { let version = get_version_string(is_verbose); drop_print!(config, "{}", version); @@ -331,7 +331,7 @@ fn config_configure( ._is_valid_arg("target-dir") .then(|| subcommand_args.value_of_path("target-dir", config)) .flatten(); - let verbose = global_args.verbose + args.occurrences_of("verbose") as u32; + let verbose = global_args.verbose + args.verbose(); // quiet is unusual because it is redefined in some subcommands in order // to provide custom help text. let quiet = args.is_present("quiet") @@ -389,7 +389,7 @@ struct GlobalArgs { impl GlobalArgs { fn new(args: &ArgMatches) -> GlobalArgs { GlobalArgs { - verbose: args.occurrences_of("verbose") as u32, + verbose: args.verbose(), quiet: args.is_present("quiet"), color: args.value_of("color").map(|s| s.to_string()), frozen: args.is_present("frozen"), diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index c1d47cbe5cb..7b13a58fcc3 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -104,7 +104,7 @@ pub fn cli() -> App { pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { if args.is_present("version") { - let verbose = args.occurrences_of("verbose") > 0; + let verbose = args.verbose() > 0; let version = cli::get_version_string(verbose); cargo::drop_print!(config, "{}", version); return Ok(()); diff --git a/src/bin/cargo/commands/version.rs b/src/bin/cargo/commands/version.rs index 1e59f136954..6a611bfb76e 100644 --- a/src/bin/cargo/commands/version.rs +++ b/src/bin/cargo/commands/version.rs @@ -9,7 +9,7 @@ pub fn cli() -> App { } pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { - let verbose = args.occurrences_of("verbose") > 0; + let verbose = args.verbose() > 0; let version = cli::get_version_string(verbose); cargo::drop_print!(config, "{}", version); Ok(()) diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 325e765f29c..1432ee94291 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -360,6 +360,10 @@ pub trait ArgMatchesExt { self.value_of_u32("jobs") } + fn verbose(&self) -> u32 { + self._occurrences("verbose") + } + fn keep_going(&self) -> bool { self._is_present("keep-going") } @@ -729,6 +733,8 @@ pub trait ArgMatchesExt { fn _values_of_os(&self, name: &str) -> Vec; + fn _occurrences(&self, name: &str) -> u32; + fn _is_present(&self, name: &str) -> bool; fn _is_valid_arg(&self, name: &str) -> bool; @@ -757,6 +763,10 @@ impl<'a> ArgMatchesExt for ArgMatches { .collect() } + fn _occurrences(&self, name: &str) -> u32 { + self.occurrences_of(name) as u32 + } + fn _is_present(&self, name: &str) -> bool { self.is_present(name) } From 6f475f090ec8bc4dda5ccbb0853744acdc6bb486 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 13 Jun 2022 10:02:07 -0500 Subject: [PATCH 2/3] chore: Upgrade to clap 3.2 --- Cargo.toml | 2 +- tests/testsuite/cargo_add/invalid_target_empty/stderr.log | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42fe3165a73..b1efa4c6323 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ toml_edit = { version = "0.14.3", features = ["serde", "easy", "perf"] } unicode-xid = "0.2.0" url = "2.2.2" walkdir = "2.2" -clap = "3.1.0" +clap = "3.2.1" unicode-width = "0.1.5" openssl = { version = '0.10.11', optional = true } im-rc = "15.0.0" diff --git a/tests/testsuite/cargo_add/invalid_target_empty/stderr.log b/tests/testsuite/cargo_add/invalid_target_empty/stderr.log index 44aad058cbc..5bc29bef9b4 100644 --- a/tests/testsuite/cargo_add/invalid_target_empty/stderr.log +++ b/tests/testsuite/cargo_add/invalid_target_empty/stderr.log @@ -1,8 +1,3 @@ error: The argument '--target ' requires a value but none was supplied -USAGE: - cargo add [OPTIONS] [@] ... - cargo add [OPTIONS] --path ... - cargo add [OPTIONS] --git ... - For more information try --help From fc0ca1e17897f7038907b8ab9532c36da4db97e9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 8 Jun 2022 15:16:51 -0500 Subject: [PATCH 3/3] refactor: Resolve clap 3.2 deprecations --- src/bin/cargo/cli.rs | 91 ++++++----- src/bin/cargo/commands/add.rs | 72 ++++----- src/bin/cargo/commands/bench.rs | 14 +- src/bin/cargo/commands/check.rs | 5 +- src/bin/cargo/commands/clean.rs | 4 +- src/bin/cargo/commands/config.rs | 14 +- src/bin/cargo/commands/doc.rs | 15 +- src/bin/cargo/commands/fix.rs | 68 ++++---- src/bin/cargo/commands/install.rs | 38 ++--- src/bin/cargo/commands/locate_project.rs | 6 +- src/bin/cargo/commands/login.rs | 4 +- src/bin/cargo/commands/logout.rs | 2 +- src/bin/cargo/commands/metadata.rs | 8 +- src/bin/cargo/commands/new.rs | 4 +- src/bin/cargo/commands/owner.rs | 18 +-- src/bin/cargo/commands/package.rs | 16 +- src/bin/cargo/commands/pkgid.rs | 5 +- src/bin/cargo/commands/publish.rs | 12 +- src/bin/cargo/commands/report.rs | 2 +- src/bin/cargo/commands/run.rs | 4 +- src/bin/cargo/commands/rustc.rs | 4 +- src/bin/cargo/commands/rustdoc.rs | 4 +- src/bin/cargo/commands/search.rs | 6 +- src/bin/cargo/commands/test.rs | 22 +-- src/bin/cargo/commands/tree.rs | 61 ++++--- src/bin/cargo/commands/uninstall.rs | 7 +- src/bin/cargo/commands/update.rs | 12 +- src/bin/cargo/commands/vendor.rs | 76 ++++----- src/bin/cargo/commands/yank.rs | 13 +- src/cargo/core/features.rs | 2 +- src/cargo/ops/cargo_config.rs | 2 +- src/cargo/util/command_prelude.rs | 194 ++++++++++++----------- 32 files changed, 399 insertions(+), 406 deletions(-) diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index 2311b42095b..4da36ab6d94 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -60,7 +60,11 @@ pub fn main(config: &mut Config) -> CliResult { // (appearing before the subcommand). let (expanded_args, global_args) = expand_aliases(config, args, vec![])?; - if expanded_args.value_of("unstable-features") == Some("help") { + if expanded_args + .get_one::("unstable-features") + .map(String::as_str) + == Some("help") + { let options = CliUnstable::help(); let non_hidden_options: Vec<(String, String)> = options .iter() @@ -113,19 +117,19 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'", } let is_verbose = expanded_args.verbose() > 0; - if expanded_args.is_present("version") { + if expanded_args.flag("version") { let version = get_version_string(is_verbose); drop_print!(config, "{}", version); return Ok(()); } - if let Some(code) = expanded_args.value_of("explain") { + if let Some(code) = expanded_args.get_one::("explain") { let mut procss = config.load_global_rustc(None)?.process(); procss.arg("--explain").arg(code).exec()?; return Ok(()); } - if expanded_args.is_present("list") { + if expanded_args.flag("list") { drop_println!(config, "Installed Commands:"); for (name, command) in list_commands(config) { let known_external_desc = KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS.get(name.as_str()); @@ -262,7 +266,7 @@ fn expand_aliases( } (Some(_), None) => { // Command is built-in and is not conflicting with alias, but contains ignored values. - if let Some(mut values) = args.values_of("") { + if let Some(mut values) = args.get_many::("") { config.shell().warn(format!( "trailing arguments after built-in command `{}` are ignored: `{}`", cmd, @@ -287,11 +291,7 @@ For more information, see issue #10049 ("").unwrap_or_default().cloned()); // new_args strips out everything before the subcommand, so // capture those global options now. // Note that an alias to an external command will not receive @@ -327,28 +327,26 @@ fn config_configure( subcommand_args: &ArgMatches, global_args: GlobalArgs, ) -> CliResult { - let arg_target_dir = &subcommand_args - ._is_valid_arg("target-dir") - .then(|| subcommand_args.value_of_path("target-dir", config)) - .flatten(); + let arg_target_dir = &subcommand_args.value_of_path("target-dir", config); let verbose = global_args.verbose + args.verbose(); // quiet is unusual because it is redefined in some subcommands in order // to provide custom help text. - let quiet = args.is_present("quiet") - || subcommand_args.is_valid_and_present("quiet") - || global_args.quiet; + let quiet = args.flag("quiet") || subcommand_args.flag("quiet") || global_args.quiet; let global_color = global_args.color; // Extract so it can take reference. - let color = args.value_of("color").or_else(|| global_color.as_deref()); - let frozen = args.is_present("frozen") || global_args.frozen; - let locked = args.is_present("locked") || global_args.locked; - let offline = args.is_present("offline") || global_args.offline; + let color = args + .get_one::("color") + .map(String::as_str) + .or_else(|| global_color.as_deref()); + let frozen = args.flag("frozen") || global_args.frozen; + let locked = args.flag("locked") || global_args.locked; + let offline = args.flag("offline") || global_args.offline; let mut unstable_flags = global_args.unstable_flags; - if let Some(values) = args.values_of("unstable-features") { - unstable_flags.extend(values.map(|s| s.to_string())); + if let Some(values) = args.get_many::("unstable-features") { + unstable_flags.extend(values.cloned()); } let mut config_args = global_args.config_args; - if let Some(values) = args.values_of("config") { - config_args.extend(values.map(|s| s.to_string())); + if let Some(values) = args.get_many::("config") { + config_args.extend(values.cloned()); } config.configure( verbose, @@ -370,7 +368,12 @@ fn execute_subcommand(config: &mut Config, cmd: &str, subcommand_args: &ArgMatch } let mut ext_args: Vec<&str> = vec![cmd]; - ext_args.extend(subcommand_args.values_of("").unwrap_or_default()); + ext_args.extend( + subcommand_args + .get_many::("") + .unwrap_or_default() + .map(String::as_str), + ); super::execute_external_subcommand(config, cmd, &ext_args) } @@ -390,18 +393,20 @@ impl GlobalArgs { fn new(args: &ArgMatches) -> GlobalArgs { GlobalArgs { verbose: args.verbose(), - quiet: args.is_present("quiet"), - color: args.value_of("color").map(|s| s.to_string()), - frozen: args.is_present("frozen"), - locked: args.is_present("locked"), - offline: args.is_present("offline"), + quiet: args.flag("quiet"), + color: args.get_one::("color").cloned(), + frozen: args.flag("frozen"), + locked: args.flag("locked"), + offline: args.flag("offline"), unstable_flags: args - .values_of_lossy("unstable-features") - .unwrap_or_default(), + .get_many::("unstable-features") + .unwrap_or_default() + .cloned() + .collect(), config_args: args - .values_of("config") + .get_many::("config") .unwrap_or_default() - .map(|s| s.to_string()) + .cloned() .collect(), } } @@ -416,7 +421,7 @@ fn cli() -> App { }; App::new("cargo") .allow_external_subcommands(true) - .setting(AppSettings::DeriveDisplayOrder | AppSettings::NoAutoVersion) + .setting(AppSettings::DeriveDisplayOrder) // Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for // opening clap up to allow us to style our help template .disable_colored_help(true) @@ -450,8 +455,8 @@ Some common cargo commands are (see all commands with --list): See 'cargo help ' for more information on a specific command.\n", ) - .arg(opt("version", "Print version info and exit").short('V')) - .arg(opt("list", "List installed commands")) + .arg(flag("version", "Print version info and exit").short('V')) + .arg(flag("list", "List installed commands")) .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE")) .arg( opt( @@ -459,7 +464,7 @@ See 'cargo help ' for more information on a specific command.\n", "Use verbose output (-vv very verbose/build.rs output)", ) .short('v') - .multiple_occurrences(true) + .action(ArgAction::Count) .global(true), ) .arg_quiet() @@ -468,9 +473,9 @@ See 'cargo help ' for more information on a specific command.\n", .value_name("WHEN") .global(true), ) - .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true)) - .arg(opt("locked", "Require Cargo.lock is up to date").global(true)) - .arg(opt("offline", "Run without accessing the network").global(true)) + .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true)) + .arg(flag("locked", "Require Cargo.lock is up to date").global(true)) + .arg(flag("offline", "Run without accessing the network").global(true)) .arg( multi_opt( "config", @@ -484,7 +489,7 @@ See 'cargo help ' for more information on a specific command.\n", .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details") .short('Z') .value_name("FLAG") - .multiple_occurrences(true) + .action(ArgAction::Append) .global(true), ) .subcommands(commands::builtin()) diff --git a/src/bin/cargo/commands/add.rs b/src/bin/cargo/commands/add.rs index 8ed6ea1fa29..01d178add7b 100644 --- a/src/bin/cargo/commands/add.rs +++ b/src/bin/cargo/commands/add.rs @@ -27,7 +27,7 @@ pub fn cli() -> clap::Command<'static> { clap::Arg::new("crates") .takes_value(true) .value_name("DEP_ID") - .multiple_occurrences(true) + .multiple_values(true) .help("Reference to a package to add as a dependency") .long_help( "Reference to a package to add as a dependency @@ -37,30 +37,26 @@ You can reference a package by: - `@`, like `cargo add serde@1` or `cargo add serde@=1.0.38`" ) .group("selected"), - clap::Arg::new("no-default-features") - .long("no-default-features") - .help("Disable the default features"), - clap::Arg::new("default-features") - .long("default-features") - .help("Re-enable the default features") + flag("no-default-features", + "Disable the default features"), + flag("default-features", + "Re-enable the default features") .overrides_with("no-default-features"), clap::Arg::new("features") .short('F') .long("features") .takes_value(true) .value_name("FEATURES") - .multiple_occurrences(true) + .action(ArgAction::Append) .help("Space or comma separated list of features to activate"), - clap::Arg::new("optional") - .long("optional") - .help("Mark the dependency as optional") + flag("optional", + "Mark the dependency as optional") .long_help("Mark the dependency as optional The package name will be exposed as feature of your crate.") .conflicts_with("dev"), - clap::Arg::new("no-optional") - .long("no-optional") - .help("Mark the dependency as required") + flag("no-optional", + "Mark the dependency as required") .long_help("Mark the dependency as required The package will be removed from your features.") @@ -141,18 +137,16 @@ This is the catch all, handling hashes to named references in remote repositorie ]) .next_help_heading("SECTION") .args([ - clap::Arg::new("dev") - .long("dev") - .help("Add as development dependency") + flag("dev", + "Add as development dependency") .long_help("Add as development dependency Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks. These dependencies are not propagated to other packages which depend on this package.") .group("section"), - clap::Arg::new("build") - .long("build") - .help("Add as build dependency") + flag("build", + "Add as build dependency") .long_help("Add as build dependency Build-dependencies are the only dependencies available for use by build scripts (`build.rs` files).") @@ -161,13 +155,13 @@ Build-dependencies are the only dependencies available for use by build scripts .long("target") .takes_value(true) .value_name("TARGET") - .forbid_empty_values(true) + .value_parser(clap::builder::NonEmptyStringValueParser::new()) .help("Add as dependency to the given target platform") ]) } pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { - let dry_run = args.is_present("dry-run"); + let dry_run = args.dry_run(); let section = parse_section(args); let ws = args.workspace(config)?; @@ -206,21 +200,21 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult> { - let path = matches.value_of("path"); - let git = matches.value_of("git"); - let branch = matches.value_of("branch"); - let rev = matches.value_of("rev"); - let tag = matches.value_of("tag"); - let rename = matches.value_of("rename"); + let path = matches.get_one::("path"); + let git = matches.get_one::("git"); + let branch = matches.get_one::("branch"); + let rev = matches.get_one::("rev"); + let tag = matches.get_one::("tag"); + let rename = matches.get_one::("rename"); let registry = matches.registry(config)?; let default_features = default_features(matches); let optional = optional(matches); let mut crates = matches - .values_of("crates") + .get_many::("crates") .into_iter() .flatten() - .map(|c| (Some(String::from(c)), None)) + .map(|c| (Some(c.clone()), None)) .collect::>(); let mut infer_crate_name = false; if crates.is_empty() { @@ -232,9 +226,10 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult("features") .into_iter() .flatten() + .map(String::as_str) .flat_map(parse_feature) { let parsed_value = FeatureValue::new(InternedString::new(feature)); @@ -310,16 +305,13 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult Option { resolve_bool_arg( - matches.is_present("default-features"), - matches.is_present("no-default-features"), + matches.flag("default-features"), + matches.flag("no-default-features"), ) } fn optional(matches: &ArgMatches) -> Option { - resolve_bool_arg( - matches.is_present("optional"), - matches.is_present("no-optional"), - ) + resolve_bool_arg(matches.flag("optional"), matches.flag("no-optional")) } fn resolve_bool_arg(yes: bool, no: bool) -> Option { @@ -332,9 +324,9 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option { } fn parse_section(matches: &ArgMatches) -> DepTable { - let kind = if matches.is_present("dev") { + let kind = if matches.flag("dev") { DepKind::Development - } else if matches.is_present("build") { + } else if matches.flag("build") { DepKind::Build } else { DepKind::Normal @@ -342,7 +334,7 @@ fn parse_section(matches: &ArgMatches) -> DepTable { let mut table = DepTable::new().set_kind(kind); - if let Some(target) = matches.value_of("target") { + if let Some(target) = matches.get_one::("target") { assert!(!target.is_empty(), "Target specification may not be empty"); table = table.set_target(target); } diff --git a/src/bin/cargo/commands/bench.rs b/src/bin/cargo/commands/bench.rs index 1e2fe37fcc3..f66f40f3c12 100644 --- a/src/bin/cargo/commands/bench.rs +++ b/src/bin/cargo/commands/bench.rs @@ -28,7 +28,7 @@ pub fn cli() -> App { "Benchmark all benches", "Benchmark all targets", ) - .arg(opt("no-run", "Compile, but don't run benchmarks")) + .arg(flag("no-run", "Compile, but don't run benchmarks")) .arg_package_spec( "Package to run benchmarks for", "Benchmark all packages in the workspace", @@ -42,7 +42,7 @@ pub fn cli() -> App { .arg_manifest_path() .arg_ignore_rust_version() .arg_message_format() - .arg(opt( + .arg(flag( "no-fail-fast", "Run all benchmarks regardless of failure", )) @@ -64,14 +64,14 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { args.get_profile_name(config, "bench", ProfileChecking::Custom)?; let ops = TestOptions { - no_run: args.is_present("no-run"), - no_fail_fast: args.is_present("no-fail-fast"), + no_run: args.flag("no-run"), + no_fail_fast: args.flag("no-fail-fast"), compile_opts, }; - let bench_args = args.value_of("BENCHNAME").into_iter(); - let bench_args = bench_args.chain(args.values_of("args").unwrap_or_default()); - let bench_args = bench_args.collect::>(); + let bench_args = args.get_one::("BENCHNAME").into_iter(); + let bench_args = bench_args.chain(args.get_many::("args").unwrap_or_default()); + let bench_args = bench_args.map(String::as_str).collect::>(); let err = ops::run_benches(&ws, &ops, &bench_args)?; match err { diff --git a/src/bin/cargo/commands/check.rs b/src/bin/cargo/commands/check.rs index 6d6a1446418..0196fb5f652 100644 --- a/src/bin/cargo/commands/check.rs +++ b/src/bin/cargo/commands/check.rs @@ -43,7 +43,10 @@ pub fn cli() -> App { pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let ws = args.workspace(config)?; // This is a legacy behavior that causes `cargo check` to pass `--test`. - let test = matches!(args.value_of("profile"), Some("test")); + let test = matches!( + args.get_one::("profile").map(String::as_str), + Some("test") + ); let mode = CompileMode::Check { test }; let compile_opts = args.compile_options(config, mode, Some(&ws), ProfileChecking::LegacyTestOnly)?; diff --git a/src/bin/cargo/commands/clean.rs b/src/bin/cargo/commands/clean.rs index f758981b1ba..18f4cdbfb70 100644 --- a/src/bin/cargo/commands/clean.rs +++ b/src/bin/cargo/commands/clean.rs @@ -29,8 +29,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { spec: values(args, "package"), targets: args.targets(), requested_profile: args.get_profile_name(config, "dev", ProfileChecking::Custom)?, - profile_specified: args.is_present("profile") || args.is_present("release"), - doc: args.is_present("doc"), + profile_specified: args.contains_id("profile") || args.flag("release"), + doc: args.flag("doc"), }; ops::clean(&ws, &opts)?; Ok(()) diff --git a/src/bin/cargo/commands/config.rs b/src/bin/cargo/commands/config.rs index 2f204f18fa1..f5a74f39a51 100644 --- a/src/bin/cargo/commands/config.rs +++ b/src/bin/cargo/commands/config.rs @@ -12,16 +12,16 @@ pub fn cli() -> App { .arg(Arg::new("key").help("The config key to display")) .arg( opt("format", "Display format") - .possible_values(cargo_config::ConfigFormat::POSSIBLE_VALUES) + .value_parser(cargo_config::ConfigFormat::POSSIBLE_VALUES) .default_value("toml"), ) - .arg(opt( + .arg(flag( "show-origin", "Display where the config value is defined", )) .arg( opt("merged", "Whether or not to merge config values") - .possible_values(&["yes", "no"]) + .value_parser(["yes", "no"]) .default_value("yes"), ), ) @@ -34,10 +34,10 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { match args.subcommand() { Some(("get", args)) => { let opts = cargo_config::GetOptions { - key: args.value_of("key"), - format: args.value_of("format").unwrap().parse()?, - show_origin: args.is_present("show-origin"), - merged: args.value_of("merged") == Some("yes"), + key: args.get_one::("key").map(String::as_str), + format: args.get_one::("format").unwrap().parse()?, + show_origin: args.flag("show-origin"), + merged: args.get_one::("merged").map(String::as_str) == Some("yes"), }; cargo_config::get(config, &opts)?; } diff --git a/src/bin/cargo/commands/doc.rs b/src/bin/cargo/commands/doc.rs index 27d6a5b24be..429662c5f7f 100644 --- a/src/bin/cargo/commands/doc.rs +++ b/src/bin/cargo/commands/doc.rs @@ -8,7 +8,7 @@ pub fn cli() -> App { // .alias("d") .about("Build a package's documentation") .arg_quiet() - .arg(opt( + .arg(flag( "open", "Opens the docs in a browser after the operation", )) @@ -17,8 +17,11 @@ pub fn cli() -> App { "Document all packages in the workspace", "Exclude packages from the build", ) - .arg(opt("no-deps", "Don't build documentation for dependencies")) - .arg(opt("document-private-items", "Document private items")) + .arg(flag( + "no-deps", + "Don't build documentation for dependencies", + )) + .arg(flag("document-private-items", "Document private items")) .arg_jobs() .arg_targets_lib_bin_example( "Document only this package's library", @@ -43,14 +46,14 @@ pub fn cli() -> App { pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let ws = args.workspace(config)?; let mode = CompileMode::Doc { - deps: !args.is_present("no-deps"), + deps: !args.flag("no-deps"), }; let mut compile_opts = args.compile_options(config, mode, Some(&ws), ProfileChecking::Custom)?; - compile_opts.rustdoc_document_private_items = args.is_present("document-private-items"); + compile_opts.rustdoc_document_private_items = args.flag("document-private-items"); let doc_opts = DocOptions { - open_result: args.is_present("open"), + open_result: args.flag("open"), compile_opts, }; ops::doc(&ws, &doc_opts)?; diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index 153f5eb03e7..55bba92c0cf 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -31,36 +31,27 @@ pub fn cli() -> App { .arg_target_dir() .arg_manifest_path() .arg_message_format() - .arg( - Arg::new("broken-code") - .long("broken-code") - .help("Fix code even if it already has compiler errors"), - ) - .arg( - Arg::new("edition") - .long("edition") - .help("Fix in preparation for the next edition"), - ) - .arg( - Arg::new("idioms") - .long("edition-idioms") - .help("Fix warnings to migrate to the idioms of an edition"), - ) - .arg( - Arg::new("allow-no-vcs") - .long("allow-no-vcs") - .help("Fix code even if a VCS was not detected"), - ) - .arg( - Arg::new("allow-dirty") - .long("allow-dirty") - .help("Fix code even if the working directory is dirty"), - ) - .arg( - Arg::new("allow-staged") - .long("allow-staged") - .help("Fix code even if the working directory has staged changes"), - ) + .arg(flag( + "broken-code", + "Fix code even if it already has compiler errors", + )) + .arg(flag("edition", "Fix in preparation for the next edition")) + .arg(flag( + "edition-idioms", + "Fix warnings to migrate to the idioms of an edition", + )) + .arg(flag( + "allow-no-vcs", + "Fix code even if a VCS was not detected", + )) + .arg(flag( + "allow-dirty", + "Fix code even if the working directory is dirty", + )) + .arg(flag( + "allow-staged", + "Fix code even if the working directory has staged changes", + )) .arg_ignore_rust_version() .arg_timings() .after_help("Run `cargo help fix` for more detailed information.\n") @@ -69,7 +60,10 @@ pub fn cli() -> App { pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let ws = args.workspace(config)?; // This is a legacy behavior that causes `cargo fix` to pass `--test`. - let test = matches!(args.value_of("profile"), Some("test")); + let test = matches!( + args.get_one::("profile").map(String::as_str), + Some("test") + ); let mode = CompileMode::Check { test }; // Unlike other commands default `cargo fix` to all targets to fix as much @@ -85,13 +79,13 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { ops::fix( &ws, &mut ops::FixOptions { - edition: args.is_present("edition"), - idioms: args.is_present("idioms"), + edition: args.flag("edition"), + idioms: args.flag("edition-idioms"), compile_opts: opts, - allow_dirty: args.is_present("allow-dirty"), - allow_no_vcs: args.is_present("allow-no-vcs"), - allow_staged: args.is_present("allow-staged"), - broken_code: args.is_present("broken-code"), + allow_dirty: args.flag("allow-dirty"), + allow_no_vcs: args.flag("allow-no-vcs"), + allow_staged: args.flag("allow-staged"), + broken_code: args.flag("broken-code"), }, )?; Ok(()) diff --git a/src/bin/cargo/commands/install.rs b/src/bin/cargo/commands/install.rs index 82dc31fc158..e409d85fc86 100644 --- a/src/bin/cargo/commands/install.rs +++ b/src/bin/cargo/commands/install.rs @@ -12,7 +12,7 @@ pub fn cli() -> App { .arg_quiet() .arg( Arg::new("crate") - .forbid_empty_values(true) + .value_parser(clap::builder::NonEmptyStringValueParser::new()) .multiple_values(true), ) .arg( @@ -46,16 +46,16 @@ pub fn cli() -> App { .value_name("PATH") .conflicts_with_all(&["git", "index", "registry"]), ) - .arg(opt( + .arg(flag( "list", "list all installed packages and their versions", )) .arg_jobs() - .arg(opt("force", "Force overwriting existing crates or binaries").short('f')) - .arg(opt("no-track", "Do not save tracking information")) + .arg(flag("force", "Force overwriting existing crates or binaries").short('f')) + .arg(flag("no-track", "Do not save tracking information")) .arg_features() .arg_profile("Install artifacts with the specified profile") - .arg(opt("debug", "Build in debug mode instead of release mode")) + .arg(flag("debug", "Build in debug mode instead of release mode")) .arg_targets_bins_examples( "Install only the specified binary", "Install all binaries", @@ -97,23 +97,23 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { // but not `Config::reload_rooted_at` which is always cwd) let path = path.map(|p| paths::normalize_path(&p)); - let version = args.value_of("version"); + let version = args.get_one::("version").map(String::as_str); let krates = args - .values_of("crate") + .get_many::("crate") .unwrap_or_default() .map(|k| resolve_crate(k, version)) .collect::>>()?; let mut from_cwd = false; - let source = if let Some(url) = args.value_of("git") { + let source = if let Some(url) = args.get_one::("git") { let url = url.into_url()?; - let gitref = if let Some(branch) = args.value_of("branch") { - GitReference::Branch(branch.to_string()) - } else if let Some(tag) = args.value_of("tag") { - GitReference::Tag(tag.to_string()) - } else if let Some(rev) = args.value_of("rev") { - GitReference::Rev(rev.to_string()) + let gitref = if let Some(branch) = args.get_one::("branch") { + GitReference::Branch(branch.clone()) + } else if let Some(tag) = args.get_one::("tag") { + GitReference::Tag(tag.clone()) + } else if let Some(rev) = args.get_one::("rev") { + GitReference::Rev(rev.clone()) } else { GitReference::DefaultBranch }; @@ -125,13 +125,13 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { SourceId::for_path(config.cwd())? } else if let Some(registry) = args.registry(config)? { SourceId::alt_registry(config, ®istry)? - } else if let Some(index) = args.value_of("index") { + } else if let Some(index) = args.get_one::("index") { SourceId::for_registry(&index.into_url()?)? } else { SourceId::crates_io(config)? }; - let root = args.value_of("root"); + let root = args.get_one::("root").map(String::as_str); // We only provide workspace information for local crate installation from // one of the following sources: @@ -158,7 +158,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { compile_opts.build_config.requested_profile = args.get_profile_name(config, "release", ProfileChecking::Custom)?; - if args.is_present("list") { + if args.flag("list") { ops::install_list(root, config)?; } else { ops::install( @@ -168,8 +168,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { source, from_cwd, &compile_opts, - args.is_present("force"), - args.is_present("no-track"), + args.flag("force"), + args.flag("no-track"), )?; } Ok(()) diff --git a/src/bin/cargo/commands/locate_project.rs b/src/bin/cargo/commands/locate_project.rs index 673f69903c2..560e2b0491f 100644 --- a/src/bin/cargo/commands/locate_project.rs +++ b/src/bin/cargo/commands/locate_project.rs @@ -15,7 +15,7 @@ pub fn cli() -> App { ) .value_name("FMT"), ) - .arg(opt("workspace", "Locate Cargo.toml of the workspace root")) + .arg(flag("workspace", "Locate Cargo.toml of the workspace root")) .after_help("Run `cargo help locate-project` for more detailed information.\n") } @@ -65,7 +65,7 @@ enum WhatToFind { impl WhatToFind { fn parse(args: &ArgMatches) -> Self { - if args.is_present("workspace") { + if args.flag("workspace") { WhatToFind::Workspace } else { WhatToFind::CurrentManifest @@ -80,7 +80,7 @@ enum MessageFormat { impl MessageFormat { fn parse(args: &ArgMatches) -> CargoResult { - let fmt = match args.value_of("message-format") { + let fmt = match args.get_one::("message-format") { Some(fmt) => fmt, None => return Ok(MessageFormat::Json), }; diff --git a/src/bin/cargo/commands/login.rs b/src/bin/cargo/commands/login.rs index 773fd9e3e8f..9c7519df194 100644 --- a/src/bin/cargo/commands/login.rs +++ b/src/bin/cargo/commands/login.rs @@ -17,8 +17,8 @@ pub fn cli() -> App { pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { ops::registry_login( config, - args.value_of("token").map(String::from), - args.value_of("registry").map(String::from), + args.get_one::("token").cloned(), + args.get_one::("registry").cloned(), )?; Ok(()) } diff --git a/src/bin/cargo/commands/logout.rs b/src/bin/cargo/commands/logout.rs index 518247e8b85..2fe98a9adfa 100644 --- a/src/bin/cargo/commands/logout.rs +++ b/src/bin/cargo/commands/logout.rs @@ -16,6 +16,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { .fail_if_stable_command(config, "logout", 8933)?; } config.load_credentials()?; - ops::registry_logout(config, args.value_of("registry").map(String::from))?; + ops::registry_logout(config, args.get_one::("registry").cloned())?; Ok(()) } diff --git a/src/bin/cargo/commands/metadata.rs b/src/bin/cargo/commands/metadata.rs index 6caa7ab946a..80eaedd57d7 100644 --- a/src/bin/cargo/commands/metadata.rs +++ b/src/bin/cargo/commands/metadata.rs @@ -15,7 +15,7 @@ pub fn cli() -> App { "TRIPLE", "Only include resolve dependencies matching the given target-triple", )) - .arg(opt( + .arg(flag( "no-deps", "Output information only about the workspace members \ and don't fetch dependencies", @@ -24,7 +24,7 @@ pub fn cli() -> App { .arg( opt("format-version", "Format version") .value_name("VERSION") - .possible_value("1"), + .value_parser(["1"]), ) .after_help("Run `cargo help metadata` for more detailed information.\n") } @@ -32,7 +32,7 @@ pub fn cli() -> App { pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let ws = args.workspace(config)?; - let version = match args.value_of("format-version") { + let version = match args.get_one::("format-version") { None => { config.shell().warn( "please specify `--format-version` flag explicitly \ @@ -45,7 +45,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let options = OutputMetadataOptions { cli_features: args.cli_features()?, - no_deps: args.is_present("no-deps"), + no_deps: args.flag("no-deps"), filter_platforms: args._values_of("filter-platform"), version, }; diff --git a/src/bin/cargo/commands/new.rs b/src/bin/cargo/commands/new.rs index b8c1ba05a73..a1bc67a6c7b 100644 --- a/src/bin/cargo/commands/new.rs +++ b/src/bin/cargo/commands/new.rs @@ -16,8 +16,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let opts = args.new_options(config)?; ops::new(&opts, config)?; - let path = args.value_of("path").unwrap(); - let package_name = if let Some(name) = args.value_of("name") { + let path = args.get_one::("path").unwrap(); + let package_name = if let Some(name) = args.get_one::("name") { name } else { path diff --git a/src/bin/cargo/commands/owner.rs b/src/bin/cargo/commands/owner.rs index e3fe2d6c720..428334929ab 100644 --- a/src/bin/cargo/commands/owner.rs +++ b/src/bin/cargo/commands/owner.rs @@ -23,7 +23,7 @@ pub fn cli() -> App { ) .short('r'), ) - .arg(opt("list", "List owners of a crate").short('l')) + .arg(flag("list", "List owners of a crate").short('l')) .arg(opt("index", "Registry index to modify owners for").value_name("INDEX")) .arg(opt("token", "API token to use when authenticating").value_name("TOKEN")) .arg(opt("registry", "Registry to use").value_name("REGISTRY")) @@ -35,16 +35,16 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let registry = args.registry(config)?; let opts = OwnersOptions { - krate: args.value_of("crate").map(|s| s.to_string()), - token: args.value_of("token").map(|s| s.to_string()), - index: args.value_of("index").map(|s| s.to_string()), + krate: args.get_one::("crate").cloned(), + token: args.get_one::("token").cloned(), + index: args.get_one::("index").cloned(), to_add: args - .values_of("add") - .map(|xs| xs.map(|s| s.to_string()).collect()), + .get_many::("add") + .map(|xs| xs.cloned().collect()), to_remove: args - .values_of("remove") - .map(|xs| xs.map(|s| s.to_string()).collect()), - list: args.is_present("list"), + .get_many::("remove") + .map(|xs| xs.cloned().collect()), + list: args.flag("list"), registry, }; ops::modify_owners(config, &opts)?; diff --git a/src/bin/cargo/commands/package.rs b/src/bin/cargo/commands/package.rs index 1209edc8f2e..d820bc1e050 100644 --- a/src/bin/cargo/commands/package.rs +++ b/src/bin/cargo/commands/package.rs @@ -7,21 +7,21 @@ pub fn cli() -> App { .about("Assemble the local package into a distributable tarball") .arg_quiet() .arg( - opt( + flag( "list", "Print files included in a package without making one", ) .short('l'), ) - .arg(opt( + .arg(flag( "no-verify", "Don't verify the contents by building them", )) - .arg(opt( + .arg(flag( "no-metadata", "Ignore warnings about a lack of human-usable metadata", )) - .arg(opt( + .arg(flag( "allow-dirty", "Allow dirty working directories to be packaged", )) @@ -46,10 +46,10 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { &ws, &PackageOpts { config, - verify: !args.is_present("no-verify"), - list: args.is_present("list"), - check_metadata: !args.is_present("no-metadata"), - allow_dirty: args.is_present("allow-dirty"), + verify: !args.flag("no-verify"), + list: args.flag("list"), + check_metadata: !args.flag("no-metadata"), + allow_dirty: args.flag("allow-dirty"), to_package: specs, targets: args.targets(), jobs: args.jobs()?, diff --git a/src/bin/cargo/commands/pkgid.rs b/src/bin/cargo/commands/pkgid.rs index 8d23d78336c..e96f1ad22fe 100644 --- a/src/bin/cargo/commands/pkgid.rs +++ b/src/bin/cargo/commands/pkgid.rs @@ -18,7 +18,10 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { if args.is_present_with_zero_values("package") { print_available_packages(&ws)? } - let spec = args.value_of("spec").or_else(|| args.value_of("package")); + let spec = args + .get_one::("spec") + .or_else(|| args.get_one::("package")) + .map(String::as_str); let spec = ops::pkgid(&ws, spec)?; cargo::drop_println!(config, "{}", spec); Ok(()) diff --git a/src/bin/cargo/commands/publish.rs b/src/bin/cargo/commands/publish.rs index 8db737504d1..5ae187327e5 100644 --- a/src/bin/cargo/commands/publish.rs +++ b/src/bin/cargo/commands/publish.rs @@ -8,11 +8,11 @@ pub fn cli() -> App { .arg_quiet() .arg_index() .arg(opt("token", "Token to use when uploading").value_name("TOKEN")) - .arg(opt( + .arg(flag( "no-verify", "Don't verify the contents by building them", )) - .arg(opt( + .arg(flag( "allow-dirty", "Allow dirty working directories to be packaged", )) @@ -38,15 +38,15 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { &ws, &PublishOpts { config, - token: args.value_of("token").map(|s| s.to_string()), + token: args.get_one::("token").map(|s| s.to_string()), index, - verify: !args.is_present("no-verify"), - allow_dirty: args.is_present("allow-dirty"), + verify: !args.flag("no-verify"), + allow_dirty: args.flag("allow-dirty"), to_publish: args.packages_from_flags()?, targets: args.targets(), jobs: args.jobs()?, keep_going: args.keep_going(), - dry_run: args.is_present("dry-run"), + dry_run: args.dry_run(), registry, cli_features: args.cli_features()?, }, diff --git a/src/bin/cargo/commands/report.rs b/src/bin/cargo/commands/report.rs index 767849c38f9..298cff6c33c 100644 --- a/src/bin/cargo/commands/report.rs +++ b/src/bin/cargo/commands/report.rs @@ -41,7 +41,7 @@ fn report_future_incompatibilities(config: &Config, args: &ArgMatches) -> CliRes let id = args .value_of_u32("id")? .unwrap_or_else(|| reports.last_id()); - let krate = args.value_of("package"); + let krate = args.get_one::("package").map(String::as_str); let report = reports.get_report(id, config, krate)?; drop_println!(config, "{}", REPORT_PREAMBLE); drop(config.shell().print_ansi_stdout(report.as_bytes())); diff --git a/src/bin/cargo/commands/run.rs b/src/bin/cargo/commands/run.rs index 29e8656e3f0..e9e7f253a9c 100644 --- a/src/bin/cargo/commands/run.rs +++ b/src/bin/cargo/commands/run.rs @@ -13,7 +13,7 @@ pub fn cli() -> App { .arg_quiet() .arg( Arg::new("args") - .allow_invalid_utf8(true) + .value_parser(value_parser!(std::ffi::OsString)) .multiple_values(true), ) .arg_targets_bin_example( @@ -56,7 +56,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } } - if !args.is_present("example") && !args.is_present("bin") { + if !args.contains_id("example") && !args.contains_id("bin") { let default_runs: Vec<_> = compile_opts .spec .get_packages(&ws)? diff --git a/src/bin/cargo/commands/rustc.rs b/src/bin/cargo/commands/rustc.rs index a893fb35d70..84970185e5c 100644 --- a/src/bin/cargo/commands/rustc.rs +++ b/src/bin/cargo/commands/rustc.rs @@ -56,7 +56,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { // This is a legacy behavior that changes the behavior based on the profile. // If we want to support this more formally, I think adding a --mode flag // would be warranted. - let mode = match args.value_of("profile") { + let mode = match args.get_one::("profile").map(String::as_str) { Some("test") => CompileMode::Test, Some("bench") => CompileMode::Bench, Some("check") => CompileMode::Check { test: false }, @@ -77,7 +77,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } else { Some(target_args) }; - if let Some(opt_value) = args.value_of(PRINT_ARG_NAME) { + if let Some(opt_value) = args.get_one::(PRINT_ARG_NAME) { config .cli_unstable() .fail_if_stable_opt(PRINT_ARG_NAME, 9357)?; diff --git a/src/bin/cargo/commands/rustdoc.rs b/src/bin/cargo/commands/rustdoc.rs index 304b1a42b2f..df01280a1ce 100644 --- a/src/bin/cargo/commands/rustdoc.rs +++ b/src/bin/cargo/commands/rustdoc.rs @@ -8,7 +8,7 @@ pub fn cli() -> App { .about("Build a package's documentation, using specified custom flags.") .arg_quiet() .arg(Arg::new("args").multiple_values(true)) - .arg(opt( + .arg(flag( "open", "Opens the docs in a browser after the operation", )) @@ -54,7 +54,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { Some(target_args) }; let doc_opts = DocOptions { - open_result: args.is_present("open"), + open_result: args.flag("open"), compile_opts, }; ops::doc(&ws, &doc_opts)?; diff --git a/src/bin/cargo/commands/search.rs b/src/bin/cargo/commands/search.rs index 14d6fba3b54..dc66e04dd1e 100644 --- a/src/bin/cargo/commands/search.rs +++ b/src/bin/cargo/commands/search.rs @@ -26,7 +26,11 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let index = args.index()?; let limit = args.value_of_u32("limit")?; let limit = min(100, limit.unwrap_or(10)); - let query: Vec<&str> = args.values_of("query").unwrap_or_default().collect(); + let query: Vec<&str> = args + .get_many::("query") + .unwrap_or_default() + .map(String::as_str) + .collect(); let query: String = query.join("+"); ops::search(&query, config, index, limit, registry)?; Ok(()) diff --git a/src/bin/cargo/commands/test.rs b/src/bin/cargo/commands/test.rs index 7c8030803f8..2f374903e6a 100644 --- a/src/bin/cargo/commands/test.rs +++ b/src/bin/cargo/commands/test.rs @@ -19,7 +19,7 @@ pub fn cli() -> App { .last(true), ) .arg( - opt( + flag( "quiet", "Display one character per test instead of one line", ) @@ -37,9 +37,9 @@ pub fn cli() -> App { "Test all benches", "Test all targets", ) - .arg(opt("doc", "Test only this library's documentation")) - .arg(opt("no-run", "Compile, but don't run tests")) - .arg(opt("no-fail-fast", "Run all tests regardless of failure")) + .arg(flag("doc", "Test only this library's documentation")) + .arg(flag("no-run", "Compile, but don't run tests")) + .arg(flag("no-fail-fast", "Run all tests regardless of failure")) .arg_package_spec( "Package to run tests for", "Test all packages in the workspace", @@ -78,13 +78,13 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { // `TESTNAME` is actually an argument of the test binary, but it's // important, so we explicitly mention it and reconfigure. - let test_name = args.value_of("TESTNAME"); - let test_args = args.value_of("TESTNAME").into_iter(); - let test_args = test_args.chain(args.values_of("args").unwrap_or_default()); - let test_args = test_args.collect::>(); + let test_name = args.get_one::("TESTNAME"); + let test_args = args.get_one::("TESTNAME").into_iter(); + let test_args = test_args.chain(args.get_many::("args").unwrap_or_default()); + let test_args = test_args.map(String::as_str).collect::>(); - let no_run = args.is_present("no-run"); - let doc = args.is_present("doc"); + let no_run = args.flag("no-run"); + let doc = args.flag("doc"); if doc { if compile_opts.filter.is_specific() { return Err( @@ -106,7 +106,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let ops = ops::TestOptions { no_run, - no_fail_fast: args.is_present("no-fail-fast"), + no_fail_fast: args.flag("no-fail-fast"), compile_opts, }; diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index 7b13a58fcc3..4b12ad8b64e 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -19,21 +19,18 @@ pub fn cli() -> App { "Display the tree for all packages in the workspace", "Exclude specific workspace members", ) - // Deprecated, use --no-dedupe instead. - .arg(Arg::new("all").long("all").short('a').hide(true)) - // Deprecated, use --target=all instead. - .arg(Arg::new("all-targets").long("all-targets").hide(true)) + .arg( + flag("all", "Deprecated, use --no-dedupe instead") + .short('a') + .hide(true), + ) + .arg(flag("all-targets", "Deprecated, use --target=all instead").hide(true)) .arg_features() .arg_target_triple( "Filter dependencies matching the given target-triple (default host platform). \ Pass `all` to include all targets.", ) - // Deprecated, use -e=no-dev instead. - .arg( - Arg::new("no-dev-dependencies") - .long("no-dev-dependencies") - .hide(true), - ) + .arg(flag("no-dev-dependencies", "Deprecated, use -e=no-dev instead").hide(true)) .arg( multi_opt( "edges", @@ -58,25 +55,23 @@ pub fn cli() -> App { "Prune the given package from the display of the dependency tree", )) .arg(opt("depth", "Maximum display depth of the dependency tree").value_name("DEPTH")) - // Deprecated, use --prefix=none instead. - .arg(Arg::new("no-indent").long("no-indent").hide(true)) - // Deprecated, use --prefix=depth instead. - .arg(Arg::new("prefix-depth").long("prefix-depth").hide(true)) + .arg(flag("no-indent", "Deprecated, use --prefix=none instead").hide(true)) + .arg(flag("prefix-depth", "Deprecated, use --prefix=depth instead").hide(true)) .arg( opt( "prefix", "Change the prefix (indentation) of how each entry is displayed", ) .value_name("PREFIX") - .possible_values(&["depth", "indent", "none"]) + .value_parser(["depth", "indent", "none"]) .default_value("indent"), ) - .arg(opt( + .arg(flag( "no-dedupe", "Do not de-duplicate (repeats all shared dependencies)", )) .arg( - opt( + flag( "duplicates", "Show only dependencies which come in multiple versions (implies -i)", ) @@ -86,7 +81,7 @@ pub fn cli() -> App { .arg( opt("charset", "Character set to use in output: utf8, ascii") .value_name("CHARSET") - .possible_values(&["utf8", "ascii"]) + .value_parser(["utf8", "ascii"]) .default_value("utf8"), ) .arg( @@ -97,35 +92,37 @@ pub fn cli() -> App { ) .arg( // Backwards compatibility with old cargo-tree. - Arg::new("version").long("version").short('V').hide(true), + flag("version", "Print version info and exit") + .short('V') + .hide(true), ) .after_help("Run `cargo help tree` for more detailed information.\n") } pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { - if args.is_present("version") { + if args.flag("version") { let verbose = args.verbose() > 0; let version = cli::get_version_string(verbose); cargo::drop_print!(config, "{}", version); return Ok(()); } - let prefix = if args.is_present("no-indent") { + let prefix = if args.flag("no-indent") { config .shell() .warn("the --no-indent flag has been changed to --prefix=none")?; "none" - } else if args.is_present("prefix-depth") { + } else if args.flag("prefix-depth") { config .shell() .warn("the --prefix-depth flag has been changed to --prefix=depth")?; "depth" } else { - args.value_of("prefix").unwrap() + args.get_one::("prefix").unwrap().as_str() }; let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?; - let no_dedupe = args.is_present("no-dedupe") || args.is_present("all"); - if args.is_present("all") { + let no_dedupe = args.flag("no-dedupe") || args.flag("all"); + if args.flag("all") { config.shell().warn( "The `cargo tree` --all flag has been changed to --no-dedupe, \ and may be removed in a future version.\n\ @@ -133,7 +130,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { )?; } - let targets = if args.is_present("all-targets") { + let targets = if args.flag("all-targets") { config .shell() .warn("the --all-targets flag has been changed to --target=all")?; @@ -150,7 +147,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let packages = args.packages_from_flags()?; let mut invert = args - .values_of("invert") + .get_many::("invert") .map_or_else(|| Vec::new(), |is| is.map(|s| s.to_string()).collect()); if args.is_present_with_zero_values("invert") { match &packages { @@ -185,7 +182,7 @@ subtree of the package given to -p.\n\ print_available_packages(&ws)?; } - let charset = tree::Charset::from_str(args.value_of("charset").unwrap()) + let charset = tree::Charset::from_str(args.get_one::("charset").unwrap()) .map_err(|e| anyhow::anyhow!("{}", e))?; let opts = tree::TreeOptions { cli_features: args.cli_features()?, @@ -196,9 +193,9 @@ subtree of the package given to -p.\n\ pkgs_to_prune, prefix, no_dedupe, - duplicates: args.is_present("duplicates"), + duplicates: args.flag("duplicates"), charset, - format: args.value_of("format").unwrap().to_string(), + format: args.get_one::("format").cloned().unwrap(), graph_features, max_display_depth: args.value_of_u32("depth")?.unwrap_or(u32::MAX), no_proc_macro, @@ -218,7 +215,7 @@ subtree of the package given to -p.\n\ fn parse_edge_kinds(config: &Config, args: &ArgMatches) -> CargoResult<(HashSet, bool)> { let (kinds, no_proc_macro) = { let mut no_proc_macro = false; - let mut kinds = args.values_of("edges").map_or_else( + let mut kinds = args.get_many::("edges").map_or_else( || Vec::new(), |es| { es.flat_map(|e| e.split(',')) @@ -230,7 +227,7 @@ fn parse_edge_kinds(config: &Config, args: &ArgMatches) -> CargoResult<(HashSet< }, ); - if args.is_present("no-dev-dependencies") { + if args.flag("no-dev-dependencies") { config .shell() .warn("the --no-dev-dependencies flag has changed to -e=no-dev")?; diff --git a/src/bin/cargo/commands/uninstall.rs b/src/bin/cargo/commands/uninstall.rs index eaec9ecc3f6..1554e276de2 100644 --- a/src/bin/cargo/commands/uninstall.rs +++ b/src/bin/cargo/commands/uninstall.rs @@ -14,7 +14,7 @@ pub fn cli() -> App { } pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { - let root = args.value_of("root"); + let root = args.get_one::("root").map(String::as_str); if args.is_present_with_zero_values("package") { return Err(anyhow::anyhow!( @@ -25,8 +25,9 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } let specs = args - .values_of("spec") - .unwrap_or_else(|| args.values_of("package").unwrap_or_default()) + .get_many::("spec") + .unwrap_or_else(|| args.get_many::("package").unwrap_or_default()) + .map(String::as_str) .collect(); ops::uninstall(root, specs, &values(args, "bin"), config)?; Ok(()) diff --git a/src/bin/cargo/commands/update.rs b/src/bin/cargo/commands/update.rs index 92be87ed990..0ffb37e603a 100644 --- a/src/bin/cargo/commands/update.rs +++ b/src/bin/cargo/commands/update.rs @@ -7,9 +7,9 @@ pub fn cli() -> App { subcommand("update") .about("Update dependencies as recorded in the local lock file") .arg_quiet() - .arg(opt("workspace", "Only update the workspace packages").short('w')) + .arg(flag("workspace", "Only update the workspace packages").short('w')) .arg_package_spec_simple("Package to update") - .arg(opt( + .arg(flag( "aggressive", "Force updating all dependencies of SPEC as well when used with -p", )) @@ -33,11 +33,11 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } let update_opts = UpdateOptions { - aggressive: args.is_present("aggressive"), - precise: args.value_of("precise"), + aggressive: args.flag("aggressive"), + precise: args.get_one::("precise").map(String::as_str), to_update: values(args, "package"), - dry_run: args.is_present("dry-run"), - workspace: args.is_present("workspace"), + dry_run: args.dry_run(), + workspace: args.flag("workspace"), config, }; ops::update_lockfile(&ws, &update_opts)?; diff --git a/src/bin/cargo/commands/vendor.rs b/src/bin/cargo/commands/vendor.rs index 88726697e22..80656f1d758 100644 --- a/src/bin/cargo/commands/vendor.rs +++ b/src/bin/cargo/commands/vendor.rs @@ -9,50 +9,34 @@ pub fn cli() -> App { .arg_manifest_path() .arg( Arg::new("path") - .allow_invalid_utf8(true) + .value_parser(clap::value_parser!(PathBuf)) .help("Where to vendor crates (`vendor` by default)"), ) - .arg( - Arg::new("no-delete") - .long("no-delete") - .help("Don't delete older crates in the vendor directory"), - ) + .arg(flag( + "no-delete", + "Don't delete older crates in the vendor directory", + )) .arg( Arg::new("tomls") .short('s') .long("sync") .help("Additional `Cargo.toml` to sync and vendor") .value_name("TOML") - .allow_invalid_utf8(true) - .multiple_occurrences(true), - ) - .arg( - Arg::new("respect-source-config") - .long("respect-source-config") - .help("Respect `[source]` config in `.cargo/config`") - .multiple_occurrences(true), - ) - .arg( - Arg::new("versioned-dirs") - .long("versioned-dirs") - .help("Always include version in subdir name"), - ) - // Not supported. - .arg( - Arg::new("no-merge-sources") - .long("no-merge-sources") - .hide(true), - ) - // Not supported. - .arg(Arg::new("relative-path").long("relative-path").hide(true)) - // Not supported. - .arg(Arg::new("only-git-deps").long("only-git-deps").hide(true)) - // Not supported. - .arg( - Arg::new("disallow-duplicates") - .long("disallow-duplicates") - .hide(true), + .value_parser(clap::value_parser!(PathBuf)) + .action(clap::ArgAction::Append), ) + .arg(flag( + "respect-source-config", + "Respect `[source]` config in `.cargo/config`", + )) + .arg(flag( + "versioned-dirs", + "Always include version in subdir name", + )) + .arg(flag("no-merge-sources", "Not supported").hide(true)) + .arg(flag("relative-path", "Not supported").hide(true)) + .arg(flag("only-git-deps", "Not supported").hide(true)) + .arg(flag("disallow-duplicates", "Not supported").hide(true)) .after_help("Run `cargo help vendor` for more detailed information.\n") } @@ -61,20 +45,20 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { // to respect any of the `source` configuration in Cargo itself. That's // intended for other consumers of Cargo, but we want to go straight to the // source, e.g. crates.io, to fetch crates. - if !args.is_present("respect-source-config") { + if !args.flag("respect-source-config") { config.values_mut()?.remove("source"); } // When we moved `cargo vendor` into Cargo itself we didn't stabilize a few // flags, so try to provide a helpful error message in that case to ensure // that users currently using the flag aren't tripped up. - let crates_io_cargo_vendor_flag = if args.is_present("no-merge-sources") { + let crates_io_cargo_vendor_flag = if args.flag("no-merge-sources") { Some("--no-merge-sources") - } else if args.is_present("relative-path") { + } else if args.flag("relative-path") { Some("--relative-path") - } else if args.is_present("only-git-deps") { + } else if args.flag("only-git-deps") { Some("--only-git-deps") - } else if args.is_present("disallow-duplicates") { + } else if args.flag("disallow-duplicates") { Some("--disallow-duplicates") } else { None @@ -95,19 +79,19 @@ https://github.com/rust-lang/cargo/issues/new let ws = args.workspace(config)?; let path = args - .value_of_os("path") - .map(|val| PathBuf::from(val.to_os_string())) + .get_one::("path") + .cloned() .unwrap_or_else(|| PathBuf::from("vendor")); ops::vendor( &ws, &ops::VendorOptions { - no_delete: args.is_present("no-delete"), + no_delete: args.flag("no-delete"), destination: &path, - versioned_dirs: args.is_present("versioned-dirs"), + versioned_dirs: args.flag("versioned-dirs"), extra: args - .values_of_os("tomls") + .get_many::("tomls") .unwrap_or_default() - .map(|s| PathBuf::from(s.to_os_string())) + .cloned() .collect(), }, )?; diff --git a/src/bin/cargo/commands/yank.rs b/src/bin/cargo/commands/yank.rs index ceaffb915ca..871c0b07d92 100644 --- a/src/bin/cargo/commands/yank.rs +++ b/src/bin/cargo/commands/yank.rs @@ -12,7 +12,7 @@ pub fn cli() -> App { .alias("vers") .value_name("VERSION"), ) - .arg(opt( + .arg(flag( "undo", "Undo a yank, putting a version back into the index", )) @@ -27,7 +27,10 @@ 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"))?; + let (krate, version) = resolve_crate( + args.get_one::("crate").map(String::as_str), + args.get_one::("version").map(String::as_str), + )?; if version.is_none() { return Err(anyhow::format_err!("`--version` is required").into()); } @@ -36,9 +39,9 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { config, 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"), + args.get_one::("token").cloned(), + args.get_one::("index").cloned(), + args.flag("undo"), registry, )?; Ok(()) diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index b8f52b4dc65..53786eff3ff 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -158,7 +158,7 @@ impl Edition { /// /// This requires a static value due to the way clap works, otherwise I /// would have built this dynamically. - pub const CLI_VALUES: &'static [&'static str] = &["2015", "2018", "2021"]; + pub const CLI_VALUES: [&'static str; 3] = ["2015", "2018", "2021"]; /// Returns the first version that a particular edition was released on /// stable. diff --git a/src/cargo/ops/cargo_config.rs b/src/cargo/ops/cargo_config.rs index 99d3baf7f50..2ee0e231b41 100644 --- a/src/cargo/ops/cargo_config.rs +++ b/src/cargo/ops/cargo_config.rs @@ -17,7 +17,7 @@ pub enum ConfigFormat { impl ConfigFormat { /// For clap. - pub const POSSIBLE_VALUES: &'static [&'static str] = &["toml", "json", "json-value"]; + pub const POSSIBLE_VALUES: [&'static str; 3] = ["toml", "json", "json-value"]; } impl FromStr for ConfigFormat { diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 1432ee94291..95491c86766 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; pub use crate::core::compiler::CompileMode; pub use crate::{CliError, CliResult, Config}; -pub use clap::{AppSettings, Arg, ArgMatches}; +pub use clap::{value_parser, AppSettings, Arg, ArgAction, ArgMatches}; pub type App = clap::Command<'static>; @@ -36,7 +36,7 @@ pub trait AppExt: Sized { exclude: &'static str, ) -> Self { self.arg_package_spec_no_all(package, all, exclude) - ._arg(opt("all", "Alias for --workspace (deprecated)")) + ._arg(flag("all", "Alias for --workspace (deprecated)")) } /// Variant of arg_package_spec that does not include the `--all` flag @@ -49,7 +49,7 @@ pub trait AppExt: Sized { exclude: &'static str, ) -> Self { self.arg_package_spec_simple(package) - ._arg(opt("workspace", all)) + ._arg(flag("workspace", all)) ._arg(multi_opt("exclude", "SPEC", exclude)) } @@ -71,7 +71,7 @@ pub trait AppExt: Sized { .short('j') .value_name("N"), ) - ._arg(opt( + ._arg(flag( "keep-going", "Do not abort the build as soon as there is an error (unstable)", )) @@ -92,10 +92,10 @@ pub trait AppExt: Sized { ) -> Self { self.arg_targets_lib_bin_example(lib, bin, bins, example, examples) ._arg(optional_multi_opt("test", "NAME", test)) - ._arg(opt("tests", tests)) + ._arg(flag("tests", tests)) ._arg(optional_multi_opt("bench", "NAME", bench)) - ._arg(opt("benches", benches)) - ._arg(opt("all-targets", all)) + ._arg(flag("benches", benches)) + ._arg(flag("all-targets", all)) } fn arg_targets_lib_bin_example( @@ -106,11 +106,11 @@ pub trait AppExt: Sized { example: &'static str, examples: &'static str, ) -> Self { - self._arg(opt("lib", lib)) + self._arg(flag("lib", lib)) ._arg(optional_multi_opt("bin", "NAME", bin)) - ._arg(opt("bins", bins)) + ._arg(flag("bins", bins)) ._arg(optional_multi_opt("example", "NAME", example)) - ._arg(opt("examples", examples)) + ._arg(flag("examples", examples)) } fn arg_targets_bins_examples( @@ -121,9 +121,9 @@ pub trait AppExt: Sized { examples: &'static str, ) -> Self { self._arg(optional_multi_opt("bin", "NAME", bin)) - ._arg(opt("bins", bins)) + ._arg(flag("bins", bins)) ._arg(optional_multi_opt("example", "NAME", example)) - ._arg(opt("examples", examples)) + ._arg(flag("examples", examples)) } fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self { @@ -140,15 +140,15 @@ pub trait AppExt: Sized { ) .short('F'), ) - ._arg(opt("all-features", "Activate all available features")) - ._arg(opt( + ._arg(flag("all-features", "Activate all available features")) + ._arg(flag( "no-default-features", "Do not activate the `default` feature", )) } fn arg_release(self, release: &'static str) -> Self { - self._arg(opt("release", release).short('r')) + self._arg(flag("release", release).short('r')) } fn arg_profile(self, profile: &'static str) -> Self { @@ -156,7 +156,7 @@ pub trait AppExt: Sized { } fn arg_doc(self, doc: &'static str) -> Self { - self._arg(opt("doc", doc)) + self._arg(flag("doc", doc)) } fn arg_target_triple(self, target: &'static str) -> Self { @@ -178,14 +178,14 @@ pub trait AppExt: Sized { } fn arg_build_plan(self) -> Self { - self._arg(opt( + self._arg(flag( "build-plan", "Output the build plan in JSON (unstable)", )) } fn arg_unit_graph(self) -> Self { - self._arg(opt("unit-graph", "Output build graph in JSON (unstable)")) + self._arg(flag("unit-graph", "Output build graph in JSON (unstable)")) } fn arg_new_opts(self) -> Self { @@ -198,13 +198,13 @@ pub trait AppExt: Sized { a global configuration.", ) .value_name("VCS") - .possible_values(&["git", "hg", "pijul", "fossil", "none"]), + .value_parser(["git", "hg", "pijul", "fossil", "none"]), ) - ._arg(opt("bin", "Use a binary (application) template [default]")) - ._arg(opt("lib", "Use a library template")) + ._arg(flag("bin", "Use a binary (application) template [default]")) + ._arg(flag("lib", "Use a library template")) ._arg( opt("edition", "Edition to set for the crate generated") - .possible_values(Edition::CLI_VALUES) + .value_parser(Edition::CLI_VALUES) .value_name("YEAR"), ) ._arg( @@ -221,25 +221,25 @@ pub trait AppExt: Sized { } fn arg_dry_run(self, dry_run: &'static str) -> Self { - self._arg(opt("dry-run", dry_run)) + self._arg(flag("dry-run", dry_run)) } fn arg_ignore_rust_version(self) -> Self { - self._arg(opt( + self._arg(flag( "ignore-rust-version", "Ignore `rust-version` specification in packages", )) } fn arg_future_incompat_report(self) -> Self { - self._arg(opt( + self._arg(flag( "future-incompat-report", "Outputs a future incompatibility report at the end of the build", )) } fn arg_quiet(self) -> Self { - self._arg(opt("quiet", "Do not print cargo log messages").short('q')) + self._arg(flag("quiet", "Do not print cargo log messages").short('q')) } fn arg_timings(self) -> Self { @@ -260,6 +260,13 @@ impl AppExt for App { } } +pub fn flag(name: &'static str, help: &'static str) -> Arg<'static> { + Arg::new(name) + .long(name) + .help(help) + .action(ArgAction::SetTrue) +} + pub fn opt(name: &'static str, help: &'static str) -> Arg<'static> { Arg::new(name).long(name).help(help) } @@ -275,7 +282,7 @@ pub fn optional_multi_opt( ) -> Arg<'static> { opt(name, help) .value_name(value_name) - .multiple_occurrences(true) + .action(ArgAction::Append) .multiple_values(true) .min_values(0) .number_of_values(1) @@ -284,7 +291,7 @@ pub fn optional_multi_opt( pub fn multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg<'static> { opt(name, help) .value_name(value_name) - .multiple_occurrences(true) + .action(ArgAction::Append) } pub fn subcommand(name: &'static str) -> App { @@ -325,11 +332,7 @@ pub trait ArgMatchesExt { } fn root_manifest(&self, config: &Config) -> CargoResult { - if let Some(path) = self - ._is_valid_arg("manifest-path") - .then(|| self.value_of_path("manifest-path", config)) - .flatten() - { + if let Some(path) = self.value_of_path("manifest-path", config) { // In general, we try to avoid normalizing paths in Cargo, // but in this particular case we need it to fix #3586. let path = paths::normalize_path(&path); @@ -361,11 +364,15 @@ pub trait ArgMatchesExt { } fn verbose(&self) -> u32 { - self._occurrences("verbose") + self._count("verbose") + } + + fn dry_run(&self) -> bool { + self.flag("dry-run") } fn keep_going(&self) -> bool { - self._is_present("keep-going") + self.flag("keep-going") } fn targets(&self) -> Vec { @@ -387,7 +394,7 @@ pub trait ArgMatchesExt { (Some(name @ ("dev" | "test" | "bench" | "check")), ProfileChecking::LegacyRustc) // `cargo fix` and `cargo check` has legacy handling of this profile name | (Some(name @ "test"), ProfileChecking::LegacyTestOnly) => { - if self._is_present("release") { + if self.flag("release") { config.shell().warn( "the `--release` flag should not be specified with the `--profile` flag\n\ The `--release` flag will be ignored.\n\ @@ -411,11 +418,7 @@ pub trait ArgMatchesExt { ) }; - let name = match ( - self.is_valid_and_present("release"), - self.is_valid_and_present("debug"), - specified_profile, - ) { + let name = match (self.flag("release"), self.flag("debug"), specified_profile) { (false, false, None) => default, (true, _, None | Some("release")) => "release", (true, _, Some(name)) => return Err(conflict("release", "release", name)), @@ -441,13 +444,9 @@ pub trait ArgMatchesExt { fn packages_from_flags(&self) -> CargoResult { Packages::from_flags( // TODO Integrate into 'workspace' - self.is_valid_and_present("workspace") || self.is_valid_and_present("all"), - self._is_valid_arg("exclude") - .then(|| self._values_of("exclude")) - .unwrap_or_default(), - self._is_valid_arg("package") - .then(|| self._values_of("package")) - .unwrap_or_default(), + self.flag("workspace") || self.flag("all"), + self._values_of("exclude"), + self._values_of("package"), ) } @@ -530,11 +529,11 @@ pub trait ArgMatchesExt { )?; build_config.message_format = message_format.unwrap_or(MessageFormat::Human); build_config.requested_profile = self.get_profile_name(config, "dev", profile_checking)?; - build_config.build_plan = self.is_valid_and_present("build-plan"); - build_config.unit_graph = self.is_valid_and_present("unit-graph"); - build_config.future_incompat_report = self.is_valid_and_present("future-incompat-report"); + build_config.build_plan = self.flag("build-plan"); + build_config.unit_graph = self.flag("unit-graph"); + build_config.future_incompat_report = self.flag("future-incompat-report"); - if self.is_valid_and_present("timings") { + if self._contains("timings") { for timing_output in self._values_of("timings") { for timing_output in timing_output.split(',') { let timing_output = timing_output.to_ascii_lowercase(); @@ -582,32 +581,28 @@ pub trait ArgMatchesExt { cli_features: self.cli_features()?, spec, filter: CompileFilter::from_raw_arguments( - self.is_valid_and_present("lib"), + self.flag("lib"), self._values_of("bin"), - self.is_valid_and_present("bins"), - self._is_valid_arg("test") - .then(|| self._values_of("test")) - .unwrap_or_default(), - self.is_valid_and_present("tests"), + self.flag("bins"), + self._values_of("test"), + self.flag("tests"), self._values_of("example"), - self.is_valid_and_present("examples"), - self._is_valid_arg("bench") - .then(|| self._values_of("bench")) - .unwrap_or_default(), - self.is_valid_and_present("benches"), - self.is_valid_and_present("all-targets"), + self.flag("examples"), + self._values_of("bench"), + self.flag("benches"), + self.flag("all-targets"), ), target_rustdoc_args: None, target_rustc_args: None, target_rustc_crate_types: None, local_rustdoc_args: None, rustdoc_document_private_items: false, - honor_rust_version: !self.is_valid_and_present("ignore-rust-version"), + honor_rust_version: !self.flag("ignore-rust-version"), }; if let Some(ws) = workspace { self.check_optional_opts(ws, &opts)?; - } else if self._is_valid_arg("package") && self.is_present_with_zero_values("package") { + } else if self.is_present_with_zero_values("package") { // As for cargo 0.50.0, this won't occur but if someone sneaks in // we can still provide this informative message for them. anyhow::bail!( @@ -623,8 +618,8 @@ pub trait ArgMatchesExt { fn cli_features(&self) -> CargoResult { CliFeatures::from_command_line( &self._values_of("features"), - self._is_present("all-features"), - !self._is_present("no-default-features"), + self.flag("all-features"), + !self.flag("no-default-features"), ) } @@ -655,8 +650,8 @@ pub trait ArgMatchesExt { }); NewOptions::new( vcs, - self._is_present("bin"), - self._is_present("lib"), + self.flag("bin"), + self.flag("lib"), self.value_of_path("path", config).unwrap(), self._value_of("name").map(|s| s.to_string()), self._value_of("edition").map(|s| s.to_string()), @@ -694,7 +689,7 @@ pub trait ArgMatchesExt { workspace: &Workspace<'_>, compile_opts: &CompileOptions, ) -> CargoResult<()> { - if self._is_valid_arg("package") && self.is_present_with_zero_values("package") { + if self.is_present_with_zero_values("package") { print_available_packages(workspace)? } @@ -706,11 +701,11 @@ pub trait ArgMatchesExt { print_available_binaries(workspace, compile_opts)?; } - if self._is_valid_arg("bench") && self.is_present_with_zero_values("bench") { + if self.is_present_with_zero_values("bench") { print_available_benches(workspace, compile_opts)?; } - if self._is_valid_arg("test") && self.is_present_with_zero_values("test") { + if self.is_present_with_zero_values("test") { print_available_tests(workspace, compile_opts)?; } @@ -718,12 +713,10 @@ pub trait ArgMatchesExt { } fn is_present_with_zero_values(&self, name: &str) -> bool { - self._is_present(name) && self._value_of(name).is_none() + self._contains(name) && self._value_of(name).is_none() } - fn is_valid_and_present(&self, name: &str) -> bool { - self._is_valid_arg(name) && self._is_present(name) - } + fn flag(&self, name: &str) -> bool; fn _value_of(&self, name: &str) -> Option<&str>; @@ -733,46 +726,46 @@ pub trait ArgMatchesExt { fn _values_of_os(&self, name: &str) -> Vec; - fn _occurrences(&self, name: &str) -> u32; - - fn _is_present(&self, name: &str) -> bool; + fn _count(&self, name: &str) -> u32; - fn _is_valid_arg(&self, name: &str) -> bool; + fn _contains(&self, name: &str) -> bool; } impl<'a> ArgMatchesExt for ArgMatches { + fn flag(&self, name: &str) -> bool { + ignore_unknown(self.try_get_one::(name)) + .copied() + .unwrap_or(false) + } + fn _value_of(&self, name: &str) -> Option<&str> { - self.value_of(name) + ignore_unknown(self.try_get_one::(name)).map(String::as_str) } fn _value_of_os(&self, name: &str) -> Option<&OsStr> { - self.value_of_os(name) + ignore_unknown(self.try_get_one::(name)).map(OsString::as_os_str) } fn _values_of(&self, name: &str) -> Vec { - self.values_of(name) + ignore_unknown(self.try_get_many::(name)) .unwrap_or_default() - .map(|s| s.to_string()) + .cloned() .collect() } fn _values_of_os(&self, name: &str) -> Vec { - self.values_of_os(name) + ignore_unknown(self.try_get_many::(name)) .unwrap_or_default() - .map(|s| s.to_os_string()) + .cloned() .collect() } - fn _occurrences(&self, name: &str) -> u32 { - self.occurrences_of(name) as u32 - } - - fn _is_present(&self, name: &str) -> bool { - self.is_present(name) + fn _count(&self, name: &str) -> u32 { + *ignore_unknown(self.try_get_one::(name)).expect("defaulted by clap") as u32 } - fn _is_valid_arg(&self, name: &str) -> bool { - self.is_valid_arg(name) + fn _contains(&self, name: &str) -> bool { + ignore_unknown(self.try_contains_id(name)) } } @@ -784,6 +777,17 @@ pub fn values_os(args: &ArgMatches, name: &str) -> Vec { args._values_of_os(name) } +#[track_caller] +fn ignore_unknown(r: Result) -> T { + match r { + Ok(t) => t, + Err(clap::parser::MatchesError::UnknownArgument { .. }) => Default::default(), + Err(e) => { + panic!("Mismatch between definition and access: {}", e); + } + } +} + #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum CommandInfo { BuiltIn { about: Option },