diff --git a/clap_bench/benches/03_complex.rs b/clap_bench/benches/03_complex.rs index b4e76020f73..6292472fd74 100644 --- a/clap_bench/benches/03_complex.rs +++ b/clap_bench/benches/03_complex.rs @@ -1,4 +1,4 @@ -use clap::{arg, Arg, Command}; +use clap::{arg, Arg, ArgAction, Command}; use criterion::{criterion_group, criterion_main, Criterion}; static OPT3_VALS: [&str; 2] = ["fast", "slow"]; @@ -59,7 +59,7 @@ pub fn build_from_builder(c: &mut Criterion) { .long("option") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg(Arg::new("positional").help("tests positionals").index(1)) .arg( @@ -68,7 +68,7 @@ pub fn build_from_builder(c: &mut Criterion) { .help("tests flags") .long("flag") .global(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg( Arg::new("flag2") @@ -102,7 +102,6 @@ pub fn build_from_builder(c: &mut Criterion) { Arg::new("positional3") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) .help("tests positionals with specific values") .index(4) .value_parser(POS3_VALS), @@ -118,7 +117,7 @@ pub fn build_from_builder(c: &mut Criterion) { .long("multvalsmo") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .help("Tests multiple values, not mult occs") .value_names(&["one", "two"]), ) @@ -127,7 +126,7 @@ pub fn build_from_builder(c: &mut Criterion) { .long("minvals2") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .help("Tests 2 min vals") .min_values(2), ) @@ -136,7 +135,7 @@ pub fn build_from_builder(c: &mut Criterion) { .long("maxvals3") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .help("Tests 3 max vals") .max_values(3), ) @@ -151,7 +150,7 @@ pub fn build_from_builder(c: &mut Criterion) { .long("option") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .help("tests options"), ) .arg(Arg::new("scpositional").index(1).help("tests positionals")), diff --git a/clap_bench/benches/04_new_help.rs b/clap_bench/benches/04_new_help.rs index 83b14fb7068..7de9dd48727 100644 --- a/clap_bench/benches/04_new_help.rs +++ b/clap_bench/benches/04_new_help.rs @@ -1,5 +1,5 @@ use clap::Command; -use clap::{arg, Arg}; +use clap::{arg, Arg, ArgAction}; use criterion::{criterion_group, criterion_main, Criterion}; use std::io::Cursor; @@ -95,7 +95,7 @@ fn app_example5<'c>() -> Command<'c> { .help("turns up the awesome") .short('a') .long("awesome") - .multiple_occurrences(true), + .action(ArgAction::Count), ) } @@ -120,7 +120,7 @@ fn app_example7<'c>() -> Command<'c> { .help("the input file to use") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .required(true) .short('i') .long("input") @@ -138,7 +138,7 @@ fn app_example8<'c>() -> Command<'c> { .help("the input file to use") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .required(true) .short('i') .long("input") diff --git a/clap_bench/benches/05_ripgrep.rs b/clap_bench/benches/05_ripgrep.rs index 6ed8448d339..10ab52c5245 100644 --- a/clap_bench/benches/05_ripgrep.rs +++ b/clap_bench/benches/05_ripgrep.rs @@ -3,7 +3,7 @@ // // CLI used is adapted from ripgrep 48a8a3a691220f9e5b2b08f4051abe8655ea7e8a -use clap::{value_parser, Arg, Command}; +use clap::{value_parser, Arg, ArgAction, Command}; use criterion::{criterion_group, criterion_main, Criterion}; use std::collections::HashMap; use std::io::Cursor; @@ -323,17 +323,12 @@ where "type-list", "version", ])) - .arg( - arg("path") - .takes_value(true) - .multiple_values(true) - .multiple_occurrences(true), - ) + .arg(arg("path").takes_value(true).multiple_values(true)) .arg( flag("regexp") .short('e') .allow_hyphen_values(true) - .multiple_occurrences(true) + .action(ArgAction::Append) .takes_value(true) .value_name("pattern"), ) @@ -357,14 +352,14 @@ where .arg( flag("colors") .value_name("SPEC") - .multiple_occurrences(true) + .action(ArgAction::Append) .takes_value(true), ) .arg(flag("fixed-strings").short('F')) .arg( flag("glob") .short('g') - .multiple_occurrences(true) + .action(ArgAction::Append) .takes_value(true) .value_name("GLOB"), ) @@ -375,18 +370,18 @@ where .arg( flag("type") .short('t') - .multiple_occurrences(true) + .action(ArgAction::Append) .takes_value(true) .value_name("TYPE"), ) .arg( flag("type-not") .short('T') - .multiple_occurrences(true) + .action(ArgAction::Append) .takes_value(true) .value_name("TYPE"), ) - .arg(flag("unrestricted").short('u').multiple_occurrences(true)) + .arg(flag("unrestricted").short('u').action(ArgAction::Append)) .arg(flag("invert-match").short('v')) .arg(flag("word-regexp").short('w')) // Third, set up less common flags. @@ -415,7 +410,7 @@ where flag("file") .short('f') .value_name("FILE") - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg(flag("files-with-matches").short('l')) .arg(flag("files-without-match")) @@ -427,7 +422,7 @@ where .arg( flag("ignore-file") .value_name("FILE") - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg(flag("follow").short('L')) .arg( @@ -464,12 +459,12 @@ where .arg( flag("type-add") .value_name("TYPE") - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg( flag("type-clear") .value_name("TYPE") - .multiple_occurrences(true), + .action(ArgAction::Append), ) } diff --git a/clap_bench/benches/06_rustup.rs b/clap_bench/benches/06_rustup.rs index 8d9c406e40d..ddc9fb32167 100644 --- a/clap_bench/benches/06_rustup.rs +++ b/clap_bench/benches/06_rustup.rs @@ -218,8 +218,7 @@ fn build_cli() -> Command<'static> { Arg::new("command") .required(true) .takes_value(true) - .multiple_values(true) - .multiple_occurrences(true), + .multiple_values(true), ), ) .subcommand( diff --git a/clap_complete/src/dynamic.rs b/clap_complete/src/dynamic.rs index 9112ac5ee8e..8902ab204ba 100644 --- a/clap_complete/src/dynamic.rs +++ b/clap_complete/src/dynamic.rs @@ -22,7 +22,7 @@ pub mod bash { #[derive(Clone, Debug)] pub struct CompleteArgs { /// Path to write completion-registration to - #[clap(long, required = true, parse(from_os_str))] + #[clap(long, required = true, value_parser)] register: Option, #[clap( @@ -30,34 +30,36 @@ pub mod bash { required = true, value_name = "COMP_CWORD", hide_short_help = true, - group = "complete" + group = "complete", + value_parser )] index: Option, - #[clap(long, hide_short_help = true, group = "complete")] + #[clap(long, hide_short_help = true, group = "complete", value_parser)] ifs: Option, #[clap( long = "type", required = true, - arg_enum, hide_short_help = true, - group = "complete" + group = "complete", + value_parser )] comp_type: Option, - #[clap(long, hide_short_help = true, group = "complete")] + #[clap(long, hide_short_help = true, group = "complete", action)] space: bool, #[clap( long, conflicts_with = "space", hide_short_help = true, - group = "complete" + group = "complete", + action )] no_space: bool, - #[clap(raw = true, hide_short_help = true, group = "complete")] + #[clap(raw = true, hide_short_help = true, group = "complete", value_parser)] comp_words: Vec, } diff --git a/clap_complete/src/shells/zsh.rs b/clap_complete/src/shells/zsh.rs index 2563ad9329b..0ff4703202b 100644 --- a/clap_complete/src/shells/zsh.rs +++ b/clap_complete/src/shells/zsh.rs @@ -451,6 +451,7 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String { let help = o.get_help().map_or(String::new(), escape_help); let conflicts = arg_conflicts(p, o, p_global); + #[allow(deprecated)] let multiple = if o.is_multiple_occurrences_set() { "*" } else { @@ -554,6 +555,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { let help = f.get_help().map_or(String::new(), escape_help); let conflicts = arg_conflicts(p, &f, p_global); + #[allow(deprecated)] let multiple = if f.is_multiple_occurrences_set() { "*" } else { @@ -632,6 +634,7 @@ fn write_positionals_of(p: &Command) -> String { for arg in p.get_positionals() { debug!("write_positionals_of:iter: arg={}", arg.get_id()); + #[allow(deprecated)] let cardinality = if arg.is_multiple_values_set() || arg.is_multiple_occurrences_set() { "*:" } else if !arg.is_required_set() { diff --git a/clap_complete/tests/common.rs b/clap_complete/tests/common.rs index 838ed381e52..f2200cbd8dd 100644 --- a/clap_complete/tests/common.rs +++ b/clap_complete/tests/common.rs @@ -6,7 +6,7 @@ pub fn basic_command(name: &'static str) -> clap::Command<'static> { clap::Command::new("test").about("Subcommand").arg( clap::Arg::new("debug") .short('d') - .multiple_occurrences(true), + .action(clap::ArgAction::Count), ), ) } @@ -23,7 +23,7 @@ pub fn feature_sample_command(name: &'static str) -> clap::Command<'static> { ) .arg( clap::Arg::new("config") - .multiple_occurrences(true) + .action(clap::ArgAction::Count) .help("some config file") .short('c') .visible_short_alias('C') @@ -57,7 +57,7 @@ pub fn special_commands_command(name: &'static str) -> clap::Command<'static> { .arg( clap::Arg::new("path") .takes_value(true) - .multiple_occurrences(true), + .multiple_values(true), ), ) .subcommand(clap::Command::new("some-cmd-with-hyphens").alias("hyphen")) diff --git a/clap_complete_fig/src/fig.rs b/clap_complete_fig/src/fig.rs index f8e0d233a5a..ae6bff9ab3e 100644 --- a/clap_complete_fig/src/fig.rs +++ b/clap_complete_fig/src/fig.rs @@ -216,6 +216,7 @@ fn gen_options(cmd: &Command, indent: usize) -> String { buffer.push_str(&format!("{:indent$}],\n", "", indent = indent + 4)); } + #[allow(deprecated)] if option.is_multiple_occurrences_set() { buffer.push_str(&format!( "{:indent$}isRepeatable: true,\n", @@ -303,6 +304,7 @@ fn gen_options(cmd: &Command, indent: usize) -> String { buffer.push_str(&format!("{:indent$}],\n", "", indent = indent + 4)); } + #[allow(deprecated)] if flag.is_multiple_occurrences_set() { buffer.push_str(&format!( "{:indent$}isRepeatable: true,\n", diff --git a/clap_complete_fig/tests/common.rs b/clap_complete_fig/tests/common.rs index 838ed381e52..f2200cbd8dd 100644 --- a/clap_complete_fig/tests/common.rs +++ b/clap_complete_fig/tests/common.rs @@ -6,7 +6,7 @@ pub fn basic_command(name: &'static str) -> clap::Command<'static> { clap::Command::new("test").about("Subcommand").arg( clap::Arg::new("debug") .short('d') - .multiple_occurrences(true), + .action(clap::ArgAction::Count), ), ) } @@ -23,7 +23,7 @@ pub fn feature_sample_command(name: &'static str) -> clap::Command<'static> { ) .arg( clap::Arg::new("config") - .multiple_occurrences(true) + .action(clap::ArgAction::Count) .help("some config file") .short('c') .visible_short_alias('C') @@ -57,7 +57,7 @@ pub fn special_commands_command(name: &'static str) -> clap::Command<'static> { .arg( clap::Arg::new("path") .takes_value(true) - .multiple_occurrences(true), + .multiple_values(true), ), ) .subcommand(clap::Command::new("some-cmd-with-hyphens").alias("hyphen")) diff --git a/clap_complete_fig/tests/snapshots/special_commands.fig.js b/clap_complete_fig/tests/snapshots/special_commands.fig.js index 5a306ac1bbc..1982cac0070 100644 --- a/clap_complete_fig/tests/snapshots/special_commands.fig.js +++ b/clap_complete_fig/tests/snapshots/special_commands.fig.js @@ -49,6 +49,7 @@ const completion: Fig.Spec = { ], args: { name: "path", + isVariadic: true, isOptional: true, }, }, diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs index 5eb1245073a..ed5c581a182 100644 --- a/clap_derive/src/attrs.rs +++ b/clap_derive/src/attrs.rs @@ -50,6 +50,7 @@ pub struct Attrs { next_help_heading: Option, help_heading: Option, is_enum: bool, + is_positional: bool, kind: Sp, } @@ -436,6 +437,7 @@ impl Attrs { next_help_heading: None, help_heading: None, is_enum: false, + is_positional: true, kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span), } } @@ -448,6 +450,9 @@ impl Attrs { } else if name == "action" { self.action = Some(Action::Explicit(Method::new(name, quote!(#arg)))); } else { + if name == "short" || name == "long" { + self.is_positional = false; + } self.methods.push(Method::new(name, quote!(#arg))); } } @@ -810,6 +815,10 @@ impl Attrs { self.is_enum } + pub fn is_positional(&self) -> bool { + self.is_positional + } + pub fn ignore_case(&self) -> TokenStream { let method = self.find_method("ignore_case"); @@ -828,12 +837,6 @@ impl Attrs { self.env_casing.clone() } - pub fn is_positional(&self) -> bool { - self.methods - .iter() - .all(|m| m.name != "long" && m.name != "short") - } - pub fn has_explicit_methods(&self) -> bool { self.methods .iter() diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index 47565edb177..345a68e73ff 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -304,25 +304,73 @@ pub fn gen_augment( #action }, - Ty::OptionVec => quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - .multiple_occurrences(true) - #possible_values - #validator - #value_parser - #action - }, + Ty::OptionVec => { + if attrs.ignore_parser() { + if attrs.is_positional() { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_values(true) // action won't be sufficient for getting multiple + #possible_values + #validator + #value_parser + #action + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + #possible_values + #validator + #value_parser + #action + } + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_occurrences(true) + #possible_values + #validator + #value_parser + #action + } + } + } Ty::Vec => { - quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - .multiple_occurrences(true) - #possible_values - #validator - #value_parser - #action + if attrs.ignore_parser() { + if attrs.is_positional() { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_values(true) // action won't be sufficient for getting multiple + #possible_values + #validator + #value_parser + #action + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + #possible_values + #validator + #value_parser + #action + } + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_occurrences(true) + #possible_values + #validator + #value_parser + #action + } } } diff --git a/examples/escaped-positional.rs b/examples/escaped-positional.rs index 035c7b3e87c..0043c735cad 100644 --- a/examples/escaped-positional.rs +++ b/examples/escaped-positional.rs @@ -13,7 +13,7 @@ fn main() { .arg( // Indicates that `slop` is only accessible after `--`. arg!(slop: [SLOP]) - .multiple_occurrences(true) + .multiple_values(true) .last(true) .value_parser(value_parser!(String)), ) diff --git a/examples/multicall-busybox.rs b/examples/multicall-busybox.rs index ec0d7600bf5..ec6a58025d6 100644 --- a/examples/multicall-busybox.rs +++ b/examples/multicall-busybox.rs @@ -35,7 +35,7 @@ fn main() { let matches = cmd.get_matches(); let mut subcommand = matches.subcommand(); if let Some(("busybox", cmd)) = subcommand { - if cmd.occurrences_of("install") > 0 { + if cmd.is_present("install") { unimplemented!("Make hardlinks to the executable here"); } subcommand = cmd.subcommand(); diff --git a/examples/tutorial_builder/01_quick.rs b/examples/tutorial_builder/01_quick.rs index 417f738026e..d30b433f4e4 100644 --- a/examples/tutorial_builder/01_quick.rs +++ b/examples/tutorial_builder/01_quick.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; -use clap::{arg, command, value_parser, Command}; +use clap::{arg, command, value_parser, ArgAction, Command}; fn main() { let matches = command!() @@ -15,9 +15,12 @@ fn main() { .required(false) .value_parser(value_parser!(PathBuf)), ) - .arg(arg!( - -d --debug ... "Turn debugging information on" - )) + .arg( + arg!( + -d --debug "Turn debugging information on" + ) + .action(ArgAction::Count), + ) .subcommand( Command::new("test") .about("does testing things") @@ -36,7 +39,10 @@ fn main() { // You can see how many times a particular flag or argument occurred // Note, only flags can have multiple occurrences - match matches.occurrences_of("debug") { + match matches + .get_one::("debug") + .expect("Count's are defaulted") + { 0 => println!("Debug mode is off"), 1 => println!("Debug mode is kind of on"), 2 => println!("Debug mode is on"), diff --git a/examples/tutorial_builder/02_app_settings.rs b/examples/tutorial_builder/02_app_settings.rs index d969f537edf..7bbedd3ebbc 100644 --- a/examples/tutorial_builder/02_app_settings.rs +++ b/examples/tutorial_builder/02_app_settings.rs @@ -1,14 +1,13 @@ // Note: this requires the `cargo` feature -use clap::{arg, command, AppSettings}; +use clap::{arg, command, AppSettings, ArgAction}; fn main() { let matches = command!() - .args_override_self(true) .global_setting(AppSettings::DeriveDisplayOrder) .allow_negative_numbers(true) - .arg(arg!(--two )) - .arg(arg!(--one )) + .arg(arg!(--two ).action(ArgAction::Set)) + .arg(arg!(--one ).action(ArgAction::Set)) .get_matches(); println!( diff --git a/examples/tutorial_builder/03_01_flag_count.rs b/examples/tutorial_builder/03_01_flag_count.rs index c5532c07a44..387ab7ac59c 100644 --- a/examples/tutorial_builder/03_01_flag_count.rs +++ b/examples/tutorial_builder/03_01_flag_count.rs @@ -1,9 +1,16 @@ // Note: this requires the `cargo` feature -use clap::{arg, command}; +use clap::{arg, command, ArgAction}; fn main() { - let matches = command!().arg(arg!(-v --verbose ...)).get_matches(); + let matches = command!() + .arg(arg!(-v - -verbose).action(ArgAction::Count)) + .get_matches(); - println!("verbose: {:?}", matches.occurrences_of("verbose")); + println!( + "verbose: {:?}", + matches + .get_one::("verbose") + .expect("Count always defaulted") + ); } diff --git a/examples/tutorial_derive/02_app_settings.rs b/examples/tutorial_derive/02_app_settings.rs index 87965cd36bb..6a06929bbb7 100644 --- a/examples/tutorial_derive/02_app_settings.rs +++ b/examples/tutorial_derive/02_app_settings.rs @@ -2,7 +2,6 @@ use clap::{AppSettings, Parser}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] -#[clap(args_override_self = true)] #[clap(allow_negative_numbers = true)] #[clap(global_setting(AppSettings::DeriveDisplayOrder))] struct Cli { diff --git a/examples/typed-derive.rs b/examples/typed-derive.rs index b99370780df..85c750f9c11 100644 --- a/examples/typed-derive.rs +++ b/examples/typed-derive.rs @@ -22,7 +22,7 @@ struct Args { sleep: Option, /// Hand-written parser for tuples - #[clap(short = 'D', value_parser = parse_key_val::, multiple_occurrences(true))] + #[clap(short = 'D', value_parser = parse_key_val::)] defines: Vec<(String, i32)>, } diff --git a/src/builder/action.rs b/src/builder/action.rs index 3e4404c7e09..8d0dc027e4c 100644 --- a/src/builder/action.rs +++ b/src/builder/action.rs @@ -58,7 +58,6 @@ pub enum ArgAction { /// .arg( /// Arg::new("flag") /// .long("flag") - /// .multiple_occurrences(true) /// .action(clap::ArgAction::Append) /// ); /// @@ -71,51 +70,17 @@ pub enum ArgAction { /// ); /// ``` Append, - /// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches] - /// - /// # Examples - /// - /// ```rust - /// # use clap::Command; - /// # use clap::Arg; - /// let cmd = Command::new("mycmd") - /// .arg( - /// Arg::new("flag") - /// .long("flag") - /// .action(clap::ArgAction::StoreValue) - /// ); - /// - /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value"]).unwrap(); - /// assert!(matches.is_present("flag")); - /// assert_eq!(matches.occurrences_of("flag"), 1); - /// assert_eq!( - /// matches.get_many::("flag").unwrap_or_default().map(|v| v.as_str()).collect::>(), - /// vec!["value"] - /// ); - /// ``` + /// Deprecated, replaced with [`ArgAction::Set`] or [`ArgAction::Append`] + #[deprecated( + since = "3.2.0", + note = "Replaced with `ArgAction::Set` or `ArgAction::Append`" + )] StoreValue, - /// When encountered, increment [`ArgMatches::occurrences_of`][crate::ArgMatches::occurrences_of] - /// - /// No value is allowed - /// - /// # Examples - /// - /// ```rust - /// # use clap::Command; - /// # use clap::Arg; - /// let cmd = Command::new("mycmd") - /// .arg( - /// Arg::new("flag") - /// .long("flag") - /// .multiple_occurrences(true) - /// .action(clap::ArgAction::IncOccurrence) - /// ); - /// - /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "--flag"]).unwrap(); - /// assert!(matches.is_present("flag")); - /// assert_eq!(matches.occurrences_of("flag"), 2); - /// assert_eq!(matches.get_many::("flag").unwrap_or_default().count(), 0); - /// ``` + /// Deprecated, replaced with [`ArgAction::SetTrue`] or [`ArgAction::Count`] + #[deprecated( + since = "3.2.0", + note = "Replaced with `ArgAction::SetTrue` or `ArgAction::Count`" + )] IncOccurrence, /// When encountered, act as if `"true"` was encountered on the command-line /// @@ -284,7 +249,9 @@ impl ArgAction { match self { Self::Set => true, Self::Append => true, + #[allow(deprecated)] Self::StoreValue => true, + #[allow(deprecated)] Self::IncOccurrence => false, Self::SetTrue => false, Self::SetFalse => false, @@ -298,7 +265,9 @@ impl ArgAction { match self { Self::Set => None, Self::Append => None, + #[allow(deprecated)] Self::StoreValue => None, + #[allow(deprecated)] Self::IncOccurrence => None, Self::SetTrue => Some(std::ffi::OsStr::new("false")), Self::SetFalse => Some(std::ffi::OsStr::new("true")), @@ -312,11 +281,13 @@ impl ArgAction { match self { Self::Set => None, Self::Append => None, + #[allow(deprecated)] Self::StoreValue => None, + #[allow(deprecated)] Self::IncOccurrence => None, Self::SetTrue => Some(super::ValueParser::bool()), Self::SetFalse => Some(super::ValueParser::bool()), - Self::Count => Some(crate::value_parser!(u64)), + Self::Count => Some(crate::value_parser!(u64).into()), Self::Help => None, Self::Version => None, } @@ -329,7 +300,9 @@ impl ArgAction { match self { Self::Set => None, Self::Append => None, + #[allow(deprecated)] Self::StoreValue => None, + #[allow(deprecated)] Self::IncOccurrence => None, Self::SetTrue => Some(AnyValueId::of::()), Self::SetFalse => Some(AnyValueId::of::()), diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 048fe2af066..e1e47ef0946 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -776,53 +776,10 @@ impl<'help> Arg<'help> { } } - /// Specifies that the argument may appear more than once. - /// - /// For flags, this results in the number of occurrences of the flag being recorded. For - /// example `-ddd` or `-d -d -d` would count as three occurrences. For options or arguments - /// that take a value, this *does not* affect how many values they can accept. (i.e. only one - /// at a time is allowed) - /// - /// For example, `--opt val1 --opt val2` is allowed, but `--opt val1 val2` is not. - /// - /// # Examples - /// - /// An example with flags - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let m = Command::new("prog") - /// .arg(Arg::new("verbose") - /// .multiple_occurrences(true) - /// .short('v')) - /// .get_matches_from(vec![ - /// "prog", "-v", "-v", "-v" // note, -vvv would have same result - /// ]); - /// - /// assert!(m.is_present("verbose")); - /// assert_eq!(m.occurrences_of("verbose"), 3); - /// ``` - /// - /// An example with options - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let m = Command::new("prog") - /// .arg(Arg::new("file") - /// .multiple_occurrences(true) - /// .takes_value(true) - /// .short('F')) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" - /// ]); - /// - /// assert!(m.is_present("file")); - /// assert_eq!(m.occurrences_of("file"), 3); - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// ``` + /// Deprecated, replaced with [`Arg::action`] ([Issue #3772](https://github.com/clap-rs/clap/issues/3772)) #[inline] #[must_use] + #[deprecated(since = "3.2.0", note = "Replaced with `Arg::action` (Issue #3772)")] pub fn multiple_occurrences(self, yes: bool) -> Self { if yes { self.setting(ArgSettings::MultipleOccurrences) @@ -831,57 +788,13 @@ impl<'help> Arg<'help> { } } - /// The *maximum* number of occurrences for this argument. - /// - /// For example, if you had a - /// `-v` flag and you wanted up to 3 levels of verbosity you would set `.max_occurrences(3)`, and - /// this argument would be satisfied if the user provided it once or twice or thrice. - /// - /// **NOTE:** This implicitly sets [`Arg::multiple_occurrences(true)`] if the value is greater than 1. - /// # Examples - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// Arg::new("verbosity") - /// .short('v') - /// .max_occurrences(3); - /// ``` - /// - /// Supplying less than the maximum number of arguments is allowed - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let res = Command::new("prog") - /// .arg(Arg::new("verbosity") - /// .max_occurrences(3) - /// .short('v')) - /// .try_get_matches_from(vec![ - /// "prog", "-vvv" - /// ]); - /// - /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert_eq!(m.occurrences_of("verbosity"), 3); - /// ``` - /// - /// Supplying more than the maximum number of arguments is an error - /// - /// ```rust - /// # use clap::{Command, Arg, ErrorKind}; - /// let res = Command::new("prog") - /// .arg(Arg::new("verbosity") - /// .max_occurrences(2) - /// .short('v')) - /// .try_get_matches_from(vec![ - /// "prog", "-vvv" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind(), ErrorKind::TooManyOccurrences); - /// ``` - /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() + /// Deprecated, for flags this is replaced with `action(ArgAction::Count).value_parser(value_parser!(u64).range(..max))` #[inline] #[must_use] + #[deprecated( + since = "3.2.0", + note = "For flags, replaced with `action(ArgAction::Count).value_parser(value_parser!(u64).range(..max))`" + )] pub fn max_occurrences(mut self, qty: usize) -> Self { self.max_occurs = Some(qty); if qty > 1 { @@ -1014,12 +927,12 @@ impl<'help> Arg<'help> { /// .arg( /// Arg::new("flag") /// .long("flag") - /// .action(clap::ArgAction::StoreValue) + /// .action(clap::ArgAction::Set) /// ); /// /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value"]).unwrap(); /// assert!(matches.is_present("flag")); - /// assert_eq!(matches.occurrences_of("flag"), 1); + /// assert_eq!(matches.occurrences_of("flag"), 0); /// assert_eq!( /// matches.get_many::("flag").unwrap_or_default().map(|v| v.as_str()).collect::>(), /// vec!["value"] @@ -1041,7 +954,7 @@ impl<'help> Arg<'help> { /// - [`value_parser!`][crate::value_parser!] for auto-selecting a value parser for a given type /// - [`BoolishValueParser`][crate::builder::BoolishValueParser], and [`FalseyValueParser`][crate::builder::FalseyValueParser] for alternative `bool` implementations /// - [`NonEmptyStringValueParser`][crate::builder::NonEmptyStringValueParser] for basic validation for strings - /// - [`RangedI64ValueParser`][crate::builder::RangedI64ValueParser] for numeric ranges + /// - [`RangedI64ValueParser`][crate::builder::RangedI64ValueParser] and [`RangedU64ValueParser`][crate::builder::RangedU64ValueParser] for numeric ranges /// - [`ArgEnumValueParser`][crate::builder::ArgEnumValueParser] and [`PossibleValuesParser`][crate::builder::PossibleValuesParser] for static enumerated values /// - or any other [`TypedValueParser`][crate::builder::TypedValueParser] implementation /// @@ -1133,7 +1046,7 @@ impl<'help> Arg<'help> { /// until another argument is reached and it knows `--ui-paths` is done parsing. /// /// By adding additional parameters to `--ui-paths` we can solve this issue. Consider adding - /// [`Arg::number_of_values(1)`] or using *only* [`Arg::multiple_occurrences`]. The following are all + /// [`Arg::number_of_values(1)`] or using *only* [`ArgAction::Append`]. The following are all /// valid, and `signer` is parsed as a subcommand in the first case, but a value in the second /// case. /// @@ -1158,7 +1071,6 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(m.is_present("file")); - /// assert_eq!(m.occurrences_of("file"), 1); // notice only one occurrence /// let files: Vec<_> = m.values_of("file").unwrap().collect(); /// assert_eq!(files, ["file1", "file2", "file3"]); /// ``` @@ -1206,14 +1118,14 @@ impl<'help> Arg<'help> { /// appear to only fail sometimes...not good! /// /// A solution for the example above is to limit how many values with a [maximum], or [specific] - /// number, or to say [`Arg::multiple_occurrences`] is ok, but multiple values is not. + /// number, or to say [`ArgAction::Append`] is ok, but multiple values is not. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ArgAction}; /// let m = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) - /// .multiple_occurrences(true) + /// .action(ArgAction::Append) /// .short('F')) /// .arg(Arg::new("word")) /// .get_matches_from(vec![ @@ -1230,11 +1142,11 @@ impl<'help> Arg<'help> { /// As a final example, let's fix the above error and get a pretty message to the user :) /// /// ```rust - /// # use clap::{Command, Arg, ErrorKind}; + /// # use clap::{Command, Arg, ErrorKind, ArgAction}; /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) - /// .multiple_occurrences(true) + /// .action(ArgAction::Append) /// .short('F')) /// .arg(Arg::new("word")) /// .try_get_matches_from(vec![ @@ -1898,7 +1810,6 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(delims.is_present("option")); - /// assert_eq!(delims.occurrences_of("option"), 1); /// assert_eq!(delims.values_of("option").unwrap().collect::>(), ["val1", "val2", "val3"]); /// ``` /// The next example shows the difference when turning delimiters off. This is the default @@ -1915,7 +1826,6 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(nodelims.is_present("option")); - /// assert_eq!(nodelims.occurrences_of("option"), 1); /// assert_eq!(nodelims.value_of("option").unwrap(), "val1,val2,val3"); /// ``` /// [`Arg::value_delimiter`]: Arg::value_delimiter() @@ -2160,8 +2070,7 @@ impl<'help> Arg<'help> { /// /// **NOTE:** If the user *does not* use this argument at runtime [`ArgMatches::is_present`] will /// still return `true`. If you wish to determine whether the argument was used at runtime or - /// not, consider [`ArgMatches::occurrences_of`] which will return `0` if the argument was *not* - /// used at runtime. + /// not, consider [`ArgMatches::value_source`][crate::ArgMatches::value_source]. /// /// **NOTE:** This setting is perfectly compatible with [`Arg::default_value_if`] but slightly /// different. `Arg::default_value` *only* takes effect when the user has not provided this arg @@ -2178,7 +2087,7 @@ impl<'help> Arg<'help> { /// First we use the default value without providing any value at runtime. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ValueSource}; /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("myopt") @@ -2189,13 +2098,13 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("opt"), Some("myval")); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 0); + /// assert_eq!(m.value_source("opt"), Some(ValueSource::DefaultValue)); /// ``` /// /// Next we provide a value at runtime to override the default. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ValueSource}; /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("myopt") @@ -2206,7 +2115,7 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("opt"), Some("non_default")); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 1); + /// assert_eq!(m.value_source("opt"), Some(ValueSource::CommandLine)); /// ``` /// [`ArgMatches::occurrences_of`]: crate::ArgMatches::occurrences_of() /// [`ArgMatches::value_of`]: crate::ArgMatches::value_of() @@ -2272,7 +2181,7 @@ impl<'help> Arg<'help> { /// Here is an implementation of the common POSIX style `--color` argument. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ValueSource}; /// /// macro_rules! cmd { /// () => {{ @@ -2300,7 +2209,7 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("color"), Some("auto")); /// assert!(m.is_present("color")); - /// assert_eq!(m.occurrences_of("color"), 0); + /// assert_eq!(m.value_source("color"), Some(ValueSource::DefaultValue)); /// /// // next, we'll provide a runtime value to override the default (as usually done). /// @@ -2310,7 +2219,7 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("color"), Some("never")); /// assert!(m.is_present("color")); - /// assert_eq!(m.occurrences_of("color"), 1); + /// assert_eq!(m.value_source("color"), Some(ValueSource::CommandLine)); /// /// // finally, we will use the shortcut and only provide the argument without a value. /// @@ -2320,9 +2229,8 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("color"), Some("always")); /// assert!(m.is_present("color")); - /// assert_eq!(m.occurrences_of("color"), 1); + /// assert_eq!(m.value_source("color"), Some(ValueSource::CommandLine)); /// ``` - /// [`ArgMatches::occurrences_of`]: ArgMatches::occurrences_of() /// [`ArgMatches::value_of`]: ArgMatches::value_of() /// [`Arg::takes_value(true)`]: Arg::takes_value() /// [`ArgMatches::is_present`]: ArgMatches::is_present() @@ -2503,7 +2411,6 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.values_of("flag").unwrap().collect::>(), vec!["env1", "env2"]); /// ``` - /// [`ArgMatches::occurrences_of`]: ArgMatches::occurrences_of() /// [`ArgMatches::value_of`]: crate::ArgMatches::value_of() /// [`ArgMatches::is_present`]: ArgMatches::is_present() /// [`Arg::takes_value(true)`]: Arg::takes_value() @@ -4183,7 +4090,6 @@ impl<'help> Arg<'help> { /// .arg(arg!(--flag "some flag").overrides_with("flag")) /// .get_matches_from(vec!["posix", "--flag", "--flag"]); /// assert!(m.is_present("flag")); - /// assert_eq!(m.occurrences_of("flag"), 1); /// ``` /// /// Making an arg [`Arg::multiple_occurrences`] and override itself @@ -4196,7 +4102,6 @@ impl<'help> Arg<'help> { /// .arg(arg!(--flag ... "some flag").overrides_with("flag")) /// .get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); /// assert!(m.is_present("flag")); - /// assert_eq!(m.occurrences_of("flag"), 4); /// ``` /// /// Now notice with options (which *do not* set @@ -4209,7 +4114,6 @@ impl<'help> Arg<'help> { /// .arg(arg!(--opt "some option").overrides_with("opt")) /// .get_matches_from(vec!["", "--opt=some", "--opt=other"]); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 1); /// assert_eq!(m.value_of("opt"), Some("other")); /// ``` /// @@ -4227,7 +4131,6 @@ impl<'help> Arg<'help> { /// ) /// .get_matches_from(vec!["", "--opt", "1", "2", "--opt", "3", "4", "5"]); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 1); /// assert_eq!(m.values_of("opt").unwrap().collect::>(), &["3", "4", "5"]); /// ``` /// @@ -4242,7 +4145,6 @@ impl<'help> Arg<'help> { /// .overrides_with("opt")) /// .get_matches_from(vec!["", "--opt", "first", "over", "--opt", "other", "val"]); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 2); /// assert_eq!(m.values_of("opt").unwrap().collect::>(), &["first", "over", "other", "val"]); /// ``` #[must_use] @@ -4532,7 +4434,8 @@ impl<'help> Arg<'help> { self.is_set(ArgSettings::MultipleValues) } - /// Report whether [`Arg::multiple_occurrences`] is set + /// [`Arg::multiple_occurrences`] is going away ([Issue #3772](https://github.com/clap-rs/clap/issues/3772)) + #[deprecated(since = "3.2.0", note = "`multiple_occurrences` away (Issue #3772)")] pub fn is_multiple_occurrences_set(&self) -> bool { self.is_set(ArgSettings::MultipleOccurrences) } @@ -4902,6 +4805,21 @@ impl<'help> Arg<'help> { } else { self.settings.unset(ArgSettings::TakesValue); } + match action { + ArgAction::StoreValue + | ArgAction::IncOccurrence + | ArgAction::Help + | ArgAction::Version => {} + ArgAction::Set + | ArgAction::Append + | ArgAction::SetTrue + | ArgAction::SetFalse + | ArgAction::Count => { + if !self.is_positional() { + self.settings.set(ArgSettings::MultipleOccurrences); + } + } + } } if self.value_parser.is_none() { diff --git a/src/builder/command.rs b/src/builder/command.rs index 10823c705df..26acde48182 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -910,17 +910,8 @@ impl<'help> App<'help> { } } - /// Specifies that all arguments override themselves. - /// - /// This is the equivalent to saying the `foo` arg using [`Arg::overrides_with("foo")`] for all - /// defined arguments. - /// - /// **NOTE:** This will not be applied when [`Arg::multiple_occurrences(true)`]. - /// - /// **NOTE:** This choice is propagated to all child subcommands. - /// - /// [`Arg::overrides_with("foo")`]: crate::Arg::overrides_with() - #[inline] + /// Deprecated, replaced with [`ArgAction::Set`][super::ArgAction::Set] + #[deprecated(since = "3.2.0", note = "Replaced with `Arg::action(ArgAction::Set)`")] pub fn args_override_self(self, yes: bool) -> Self { if yes { self.global_setting(AppSettings::AllArgsOverrideSelf) diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs index 78db63c5d25..91ab7dfe54c 100644 --- a/src/builder/debug_asserts.rs +++ b/src/builder/debug_asserts.rs @@ -538,8 +538,11 @@ fn _verify_positionals(cmd: &Command) -> bool { let count = cmd .get_positionals() .filter(|p| { - p.is_multiple_occurrences_set() - || (p.is_multiple_values_set() && p.num_vals.is_none()) + #[allow(deprecated)] + { + p.is_multiple_occurrences_set() + || (p.is_multiple_values_set() && p.num_vals.is_none()) + } }) .count(); let ok = count <= 1 diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 1f18ad51b0e..885b46a1be1 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -44,6 +44,7 @@ pub use value_parser::OsStringValueParser; pub use value_parser::PathBufValueParser; pub use value_parser::PossibleValuesParser; pub use value_parser::RangedI64ValueParser; +pub use value_parser::RangedU64ValueParser; pub use value_parser::StringValueParser; pub use value_parser::TypedValueParser; pub use value_parser::ValueParser; diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs index 30b3610d0aa..27a3670db83 100644 --- a/src/builder/value_parser.rs +++ b/src/builder/value_parser.rs @@ -81,7 +81,7 @@ impl ValueParser { /// Pre-existing implementations include: /// - [`ArgEnumValueParser`] and [`PossibleValuesParser`] for static enumerated values /// - [`BoolishValueParser`] and [`FalseyValueParser`] for alternative `bool` implementations - /// - [`RangedI64ValueParser`] + /// - [`RangedI64ValueParser`] and [`RangedU64ValueParser`] /// - [`NonEmptyStringValueParser`] /// /// # Example @@ -302,6 +302,8 @@ impl From

for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -330,6 +332,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -358,6 +362,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -386,6 +392,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -414,6 +422,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -442,6 +452,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -1235,6 +1247,201 @@ impl> Default for RangedI64ValueParser { } } +/// Parse number that fall within a range of values +/// +/// # Example +/// +/// Usage: +/// ```rust +/// let mut cmd = clap::Command::new("raw") +/// .arg( +/// clap::Arg::new("port") +/// .long("port") +/// .value_parser(clap::value_parser!(u64).range(3000..)) +/// .takes_value(true) +/// .required(true) +/// ); +/// +/// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); +/// let port: u64 = *m.get_one("port") +/// .expect("required"); +/// assert_eq!(port, 3001); +/// ``` +/// +/// Semantics: +/// ```rust +/// # use std::ffi::OsStr; +/// # use clap::builder::TypedValueParser; +/// # let cmd = clap::Command::new("test"); +/// # let arg = None; +/// let value_parser = clap::builder::RangedU64ValueParser::::new().range(0..200); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-200")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("300")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-1")).is_err()); +/// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("0")).unwrap(), 0); +/// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("50")).unwrap(), 50); +/// ``` +#[derive(Copy, Clone, Debug)] +pub struct RangedU64ValueParser = u64> { + bounds: (std::ops::Bound, std::ops::Bound), + target: std::marker::PhantomData, +} + +impl> RangedU64ValueParser { + /// Select full range of `u64` + pub fn new() -> Self { + Self::from(..) + } + + /// Narrow the supported range + pub fn range>(mut self, range: B) -> Self { + // Consideration: when the user does `value_parser!(u8).range()` + // - Avoid programming mistakes by accidentally expanding the range + // - Make it convenient to limit the range like with `..10` + let start = match range.start_bound() { + l @ std::ops::Bound::Included(i) => { + debug_assert!( + self.bounds.contains(i), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + l @ std::ops::Bound::Excluded(i) => { + debug_assert!( + self.bounds.contains(&i.saturating_add(1)), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + std::ops::Bound::Unbounded => self.bounds.start_bound().cloned(), + }; + let end = match range.end_bound() { + l @ std::ops::Bound::Included(i) => { + debug_assert!( + self.bounds.contains(i), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + l @ std::ops::Bound::Excluded(i) => { + debug_assert!( + self.bounds.contains(&i.saturating_sub(1)), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + std::ops::Bound::Unbounded => self.bounds.end_bound().cloned(), + }; + self.bounds = (start, end); + self + } + + fn format_bounds(&self) -> String { + let mut result = match self.bounds.0 { + std::ops::Bound::Included(i) => i.to_string(), + std::ops::Bound::Excluded(i) => i.saturating_add(1).to_string(), + std::ops::Bound::Unbounded => u64::MIN.to_string(), + }; + result.push_str(".."); + match self.bounds.1 { + std::ops::Bound::Included(i) => { + result.push('='); + result.push_str(&i.to_string()); + } + std::ops::Bound::Excluded(i) => { + result.push_str(&i.to_string()); + } + std::ops::Bound::Unbounded => { + result.push_str(&u64::MAX.to_string()); + } + } + result + } +} + +impl> TypedValueParser for RangedU64ValueParser +where + >::Error: Send + Sync + 'static + std::error::Error + ToString, +{ + type Value = T; + + fn parse_ref( + &self, + cmd: &crate::Command, + arg: Option<&crate::Arg>, + raw_value: &std::ffi::OsStr, + ) -> Result { + let value = raw_value.to_str().ok_or_else(|| { + crate::Error::invalid_utf8( + cmd, + crate::output::Usage::new(cmd).create_usage_with_title(&[]), + ) + })?; + let value = value.parse::().map_err(|err| { + let arg = arg + .map(|a| a.to_string()) + .unwrap_or_else(|| "...".to_owned()); + crate::Error::value_validation( + arg, + raw_value.to_string_lossy().into_owned(), + err.into(), + ) + .with_cmd(cmd) + })?; + if !self.bounds.contains(&value) { + let arg = arg + .map(|a| a.to_string()) + .unwrap_or_else(|| "...".to_owned()); + return Err(crate::Error::value_validation( + arg, + raw_value.to_string_lossy().into_owned(), + format!("{} is not in {}", value, self.format_bounds()).into(), + ) + .with_cmd(cmd)); + } + + let value: Result = value.try_into(); + let value = value.map_err(|err| { + let arg = arg + .map(|a| a.to_string()) + .unwrap_or_else(|| "...".to_owned()); + crate::Error::value_validation( + arg, + raw_value.to_string_lossy().into_owned(), + err.into(), + ) + .with_cmd(cmd) + })?; + + Ok(value) + } +} + +impl, B: RangeBounds> From for RangedU64ValueParser { + fn from(range: B) -> Self { + Self { + bounds: (range.start_bound().cloned(), range.end_bound().cloned()), + target: Default::default(), + } + } +} + +impl> Default for RangedU64ValueParser { + fn default() -> Self { + Self::new() + } +} + /// Implementation for [`ValueParser::bool`] /// /// Useful for composing new [`TypedValueParser`]s @@ -1252,7 +1459,7 @@ impl BoolValueParser { ["true", "false"] .iter() .copied() - .map(|l| crate::PossibleValue::new(l)) + .map(crate::PossibleValue::new) } } @@ -1687,6 +1894,12 @@ impl ValueParserFactory for i64 { RangedI64ValueParser::new() } } +impl ValueParserFactory for u64 { + type Parser = RangedU64ValueParser; + fn value_parser() -> Self::Parser { + RangedU64ValueParser::new() + } +} #[doc(hidden)] #[derive(Debug)] @@ -1784,6 +1997,8 @@ pub mod via_prelude { /// assert_eq!(format!("{:?}", parser), "ValueParser::path_buf"); /// let parser = clap::value_parser!(u16).range(3000..); /// assert_eq!(format!("{:?}", parser), "RangedI64ValueParser { bounds: (Included(3000), Included(65535)), target: PhantomData }"); +/// let parser = clap::value_parser!(u64).range(3000..); +/// assert_eq!(format!("{:?}", parser), "RangedU64ValueParser { bounds: (Included(3000), Unbounded), target: PhantomData }"); /// /// // FromStr types /// let parser = clap::value_parser!(usize); diff --git a/src/macros.rs b/src/macros.rs index f50b854959e..0bbf6a14d46 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -425,7 +425,10 @@ macro_rules! arg_impl { @arg ({ debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + } let mut arg = $arg; let long = $crate::arg_impl! { @string $long }; @@ -447,7 +450,10 @@ macro_rules! arg_impl { @arg ({ debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + } let mut arg = $arg; let long = $crate::arg_impl! { @string $long }; @@ -470,7 +476,10 @@ macro_rules! arg_impl { ({ debug_assert_eq!($arg.get_long(), None, "Short flags should precede long flags"); debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + } $arg.short($crate::arg_impl! { @char $short }) }) @@ -488,7 +497,10 @@ macro_rules! arg_impl { ({ debug_assert_eq!($arg.get_long(), None, "Short flags should precede long flags"); debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); + } $arg.short($crate::arg_impl! { @char $short }) }) @@ -504,7 +516,10 @@ macro_rules! arg_impl { $crate::arg_impl! { @arg ({ - debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + } debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); let mut arg = $arg; @@ -530,7 +545,10 @@ macro_rules! arg_impl { $crate::arg_impl! { @arg ({ - debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + } debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); let mut arg = $arg; @@ -556,7 +574,10 @@ macro_rules! arg_impl { $crate::arg_impl! { @arg ({ - debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + } debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); let mut arg = $arg; @@ -586,7 +607,10 @@ macro_rules! arg_impl { $crate::arg_impl! { @arg ({ - debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + #[allow(deprecated)] + { + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + } debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); let mut arg = $arg; @@ -615,9 +639,9 @@ macro_rules! arg_impl { ) => { $crate::arg_impl! { @arg - ({ + ({#[allow(deprecated)]{ $arg.multiple_occurrences(true) - }) + }}) $($tail)* } }; diff --git a/src/parser/arg_matcher.rs b/src/parser/arg_matcher.rs index 690f504b8cc..8d15c5799f4 100644 --- a/src/parser/arg_matcher.rs +++ b/src/parser/arg_matcher.rs @@ -70,11 +70,13 @@ impl ArgMatcher { // We have to check if the parent's global arg wasn't used but still exists // such as from a default value. // - // For example, `myprog subcommand --global-arg=value` where --global-arg defines + // For example, `myprog subcommand --global-arg=value` where `--global-arg` defines // a default value of `other` myprog would have an existing MatchedArg for - // --global-arg where the value is `other`, however the occurs will be 0. + // `--global-arg` where the value is `other` let to_update = if let Some(parent_ma) = vals_map.get(global_arg) { - if parent_ma.get_occurrences() > 0 && ma.get_occurrences() == 0 { + if parent_ma.check_explicit(ArgPredicate::IsPresent) + && !ma.check_explicit(ArgPredicate::IsPresent) + { parent_ma } else { ma @@ -169,6 +171,7 @@ impl ArgMatcher { let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg)); debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id())); ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] ma.inc_occurrences(); ma.new_val_group(); } @@ -178,6 +181,7 @@ impl ArgMatcher { let ma = self.entry(id).or_insert(MatchedArg::new_group()); debug_assert_eq!(ma.type_id(), None); ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] ma.inc_occurrences(); ma.new_val_group(); } @@ -195,6 +199,7 @@ impl ArgMatcher { ) ); ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] ma.inc_occurrences(); ma.new_val_group(); } @@ -225,6 +230,7 @@ impl ArgMatcher { true } else if let Some(num) = o.num_vals { debug!("ArgMatcher::needs_more_vals: num_vals...{}", num); + #[allow(deprecated)] if o.is_multiple_occurrences_set() { (current_num % num) != 0 } else { diff --git a/src/parser/matches/arg_matches.rs b/src/parser/matches/arg_matches.rs index 6b27174f78b..79f7e9699c8 100644 --- a/src/parser/matches/arg_matches.rs +++ b/src/parser/matches/arg_matches.rs @@ -29,15 +29,13 @@ use crate::INTERNAL_ERROR_MSG; /// # Examples /// /// ```no_run -/// # use clap::{Command, Arg}; +/// # use clap::{Command, Arg, ValueSource}; /// let matches = Command::new("MyApp") /// .arg(Arg::new("out") /// .long("output") /// .required(true) -/// .takes_value(true)) -/// .arg(Arg::new("debug") -/// .short('d') -/// .multiple_occurrences(true)) +/// .takes_value(true) +/// .default_value("-")) /// .arg(Arg::new("cfg") /// .short('c') /// .takes_value(true)) @@ -57,14 +55,11 @@ use crate::INTERNAL_ERROR_MSG; /// /// // You can check the presence of an argument /// if matches.is_present("out") { -/// // Another way to check if an argument was present, or if it occurred multiple times is to -/// // use occurrences_of() which returns 0 if an argument isn't found at runtime, or the -/// // number of times that it occurred, if it was. To allow an argument to appear more than -/// // once, you must use the .multiple_occurrences(true) method, otherwise it will only return 1 or 0. -/// if matches.occurrences_of("debug") > 2 { -/// println!("Debug mode is REALLY on, don't be crazy"); +/// // However, if you want to know where the value came from +/// if matches.value_source("out").expect("checked is_present") == ValueSource::CommandLine { +/// println!("`out` set by user"); /// } else { -/// println!("Debug mode kind of on"); +/// println!("`out` is defaulted"); /// } /// } /// ``` @@ -92,7 +87,7 @@ impl ArgMatches { /// Returns `None` if the option wasn't present. /// /// *NOTE:* This will always return `Some(value)` if [`default_value`] has been set. - /// [`occurrences_of`] can be used to check if a value is present at runtime. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. /// /// # Panic /// @@ -118,7 +113,6 @@ impl ArgMatches { /// [option]: crate::Arg::takes_value() /// [positional]: crate::Arg::index() /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: crate::ArgMatches::occurrences_of() #[track_caller] pub fn get_one(&self, name: &str) -> Option<&T> { let id = Id::from(name); @@ -141,10 +135,10 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{Command, Arg, value_parser}; + /// # use clap::{Command, Arg, value_parser, ArgAction}; /// let m = Command::new("myprog") /// .arg(Arg::new("ports") - /// .multiple_occurrences(true) + /// .action(ArgAction::Append) /// .value_parser(value_parser!(usize)) /// .short('p') /// .takes_value(true) @@ -223,7 +217,7 @@ impl ArgMatches { /// Returns `None` if the option wasn't present. /// /// *NOTE:* This will always return `Some(value)` if [`default_value`] has been set. - /// [`occurrences_of`] can be used to check if a value is present at runtime. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. /// /// # Panic /// @@ -248,7 +242,6 @@ impl ArgMatches { /// [option]: crate::Arg::takes_value() /// [positional]: crate::Arg::index() /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: crate::ArgMatches::occurrences_of() #[track_caller] pub fn remove_one(&mut self, name: &str) -> Option { let id = Id::from(name); @@ -271,10 +264,11 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{Command, Arg, value_parser}; + /// # use clap::{Command, Arg, value_parser, ArgAction}; /// let mut m = Command::new("myprog") /// .arg(Arg::new("file") - /// .multiple_occurrences(true) + /// .action(ArgAction::Append) + /// .multiple_values(true) /// .required(true) /// .takes_value(true)) /// .get_matches_from(vec![ @@ -381,12 +375,12 @@ impl ArgMatches { /// /// # Examples /// ```rust - /// # use clap::{Command,Arg}; + /// # use clap::{Command,Arg, ArgAction}; /// let m = Command::new("myprog") /// .arg(Arg::new("exec") /// .short('x') /// .min_values(1) - /// .multiple_occurrences(true) + /// .action(ArgAction::Append) /// .value_terminator(";")) /// .get_matches_from(vec![ /// "myprog", "-x", "echo", "hi", ";", "-x", "echo", "bye"]); @@ -500,7 +494,7 @@ impl ArgMatches { /// Check if an argument was present at runtime. /// /// *NOTE:* This will always return `true` if [`default_value`] has been set. - /// [`occurrences_of`] can be used to check if a value is present at runtime. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. /// /// # Panics /// @@ -521,7 +515,6 @@ impl ArgMatches { /// ``` /// /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: ArgMatches::occurrences_of() pub fn is_present(&self, id: T) -> bool { let id = Id::from(id); @@ -552,7 +545,6 @@ impl ArgMatches { /// ``` /// /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: ArgMatches::occurrences_of() pub fn value_source(&self, id: T) -> Option { let id = Id::from(id); @@ -561,51 +553,14 @@ impl ArgMatches { value.and_then(MatchedArg::source) } - /// The number of times an argument was used at runtime. - /// - /// If an argument isn't present it will return `0`. - /// - /// **NOTE:** This returns the number of times the argument was used, *not* the number of - /// values. For example, `-o val1 val2 val3 -o val4` would return `2` (2 occurrences, but 4 - /// values). See [Arg::multiple_occurrences][crate::Arg::multiple_occurrences]. - /// - /// # Panics - /// - /// If `id` is is not a valid argument or group name. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let m = Command::new("myprog") - /// .arg(Arg::new("debug") - /// .short('d') - /// .multiple_occurrences(true)) - /// .get_matches_from(vec![ - /// "myprog", "-d", "-d", "-d" - /// ]); - /// - /// assert_eq!(m.occurrences_of("debug"), 3); - /// ``` - /// - /// This next example shows that counts actual uses of the argument, not just `-`'s - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let m = Command::new("myprog") - /// .arg(Arg::new("debug") - /// .short('d') - /// .multiple_occurrences(true)) - /// .arg(Arg::new("flag") - /// .short('f')) - /// .get_matches_from(vec![ - /// "myprog", "-ddfd" - /// ]); - /// - /// assert_eq!(m.occurrences_of("debug"), 3); - /// assert_eq!(m.occurrences_of("flag"), 1); - /// ``` + /// Deprecated, replaced with [`ArgMatches::get_many`]`.len()` or + /// [`ArgAction::Count`][crate::ArgAction]. + #[deprecated( + since = "3.2.0", + note = "Replaced with either `ArgMatches::get_many(...).len()` or `ArgAction::Count`" + )] pub fn occurrences_of(&self, id: T) -> u64 { + #![allow(deprecated)] self.get_arg(&Id::from(id)) .map_or(0, |a| a.get_occurrences()) } @@ -787,21 +742,21 @@ impl ArgMatches { /// Another quick example is when flags and options are used together /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ArgAction}; /// let m = Command::new("myapp") /// .arg(Arg::new("option") /// .short('o') /// .takes_value(true) - /// .multiple_occurrences(true)) + /// .action(ArgAction::Append)) /// .arg(Arg::new("flag") /// .short('f') - /// .multiple_occurrences(true)) + /// .action(ArgAction::Count)) /// .get_matches_from(vec!["myapp", "-o", "val1", "-f", "-o", "val2", "-f"]); /// // ARGV indices: ^0 ^1 ^2 ^3 ^4 ^5 ^6 /// // clap indices: ^2 ^3 ^5 ^6 /// /// assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2, 5]); - /// assert_eq!(m.indices_of("flag").unwrap().collect::>(), &[3, 6]); + /// assert_eq!(m.indices_of("flag").unwrap().collect::>(), &[6]); /// ``` /// /// One final example, which is an odd case; if we *don't* use value delimiter as we did with @@ -1296,11 +1251,11 @@ pub(crate) struct SubCommand { /// # Examples /// /// ```rust -/// # use clap::{Command, Arg}; +/// # use clap::{Command, Arg, ArgAction}; /// let mut m = Command::new("myapp") /// .arg(Arg::new("output") /// .short('o') -/// .multiple_occurrences(true) +/// .action(ArgAction::Append) /// .takes_value(true)) /// .get_matches_from(vec!["myapp", "-o", "val1", "-o", "val2"]); /// @@ -1353,11 +1308,11 @@ impl Default for Values2 { /// # Examples /// /// ```rust -/// # use clap::{Command, Arg}; +/// # use clap::{Command, Arg, ArgAction}; /// let m = Command::new("myapp") /// .arg(Arg::new("output") /// .short('o') -/// .multiple_occurrences(true) +/// .action(ArgAction::Append) /// .takes_value(true)) /// .get_matches_from(vec!["myapp", "-o", "val1", "-o", "val2"]); /// diff --git a/src/parser/matches/matched_arg.rs b/src/parser/matches/matched_arg.rs index 558e3098ca8..9de738f8830 100644 --- a/src/parser/matches/matched_arg.rs +++ b/src/parser/matches/matched_arg.rs @@ -67,14 +67,17 @@ impl MatchedArg { } } + #[deprecated(since = "3.2.0")] pub(crate) fn inc_occurrences(&mut self) { self.occurs += 1; } + #[deprecated(since = "3.2.0")] pub(crate) fn set_occurrences(&mut self, occurs: u64) { self.occurs = occurs } + #[deprecated(since = "3.2.0")] pub(crate) fn get_occurrences(&self) -> u64 { self.occurs } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index e6d9980081b..7b1d5130bf3 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1176,6 +1176,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } Ok(ParseResult::ValuesDone) } + #[allow(deprecated)] ArgAction::StoreValue => { if ident == Some(Identifier::Index) && arg.is_multiple_values_set() @@ -1196,6 +1197,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { if ident == Some(Identifier::Index) && arg.is_multiple_values_set() { // HACK: Maintain existing occurrence behavior let matched = matcher.get_mut(&arg.id).unwrap(); + #[allow(deprecated)] matched.set_occurrences(matched.num_vals() as u64); } if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { @@ -1205,6 +1207,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } Ok(ParseResult::ValuesDone) } + #[allow(deprecated)] ArgAction::IncOccurrence => { debug_assert_eq!(raw_vals, Vec::::new()); if source == ValueSource::CommandLine { @@ -1362,7 +1365,9 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } } else { match arg.get_action() { + #[allow(deprecated)] ArgAction::StoreValue => unreachable!("{:?} is not a flag", arg.get_id()), + #[allow(deprecated)] ArgAction::IncOccurrence => { debug!("Parser::add_env: Found a flag with value `{:?}`", val); let predicate = str_to_bool(val.to_str_lossy()); @@ -1558,6 +1563,10 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } fn start_custom_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>, source: ValueSource) { + if source == ValueSource::CommandLine { + // With each new occurrence, remove overrides from prior occurrences + self.remove_overrides(arg, matcher); + } matcher.start_custom_arg(arg, source); for group in self.cmd.groups_for_arg(&arg.id) { matcher.start_custom_group(&group, source); diff --git a/src/parser/validator.rs b/src/parser/validator.rs index a7456f917df..ebf2b234d4c 100644 --- a/src/parser/validator.rs +++ b/src/parser/validator.rs @@ -330,6 +330,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { } fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { + #![allow(deprecated)] debug!( "Validator::validate_arg_num_occurs: {:?}={}", a.name, @@ -374,6 +375,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { if let Some(num) = a.num_vals { let total_num = ma.num_vals(); debug!("Validator::validate_arg_num_vals: num_vals set...{}", num); + #[allow(deprecated)] let should_err = if a.is_multiple_occurrences_set() { total_num % num != 0 } else { @@ -385,6 +387,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { self.cmd, a.to_string(), num, + #[allow(deprecated)] if a.is_multiple_occurrences_set() { total_num % num } else { diff --git a/tests/builder/action.rs b/tests/builder/action.rs index 5c08590f06a..e6d426d4cb6 100644 --- a/tests/builder/action.rs +++ b/tests/builder/action.rs @@ -11,7 +11,10 @@ fn set() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(matches.get_one::("mammal"), None); assert_eq!(matches.is_present("mammal"), false); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), None); let matches = cmd @@ -20,7 +23,10 @@ fn set() { .unwrap(); assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); let matches = cmd @@ -29,7 +35,10 @@ fn set() { .unwrap(); assert_eq!(matches.get_one::("mammal").unwrap(), "cat"); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(4)); } @@ -40,7 +49,10 @@ fn append() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(matches.get_one::("mammal"), None); assert_eq!(matches.is_present("mammal"), false); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), None); let matches = cmd @@ -49,7 +61,10 @@ fn append() { .unwrap(); assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!( matches.indices_of("mammal").unwrap().collect::>(), vec![2] @@ -68,7 +83,10 @@ fn append() { vec!["dog", "cat"] ); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!( matches.indices_of("mammal").unwrap().collect::>(), vec![2, 4] @@ -83,7 +101,10 @@ fn set_true() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -92,7 +113,10 @@ fn set_true() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -101,7 +125,10 @@ fn set_true() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); } @@ -120,13 +147,19 @@ fn set_true_with_explicit_default_value() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); } @@ -225,7 +258,10 @@ fn set_false() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -234,7 +270,10 @@ fn set_false() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -243,7 +282,10 @@ fn set_false() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); } @@ -262,13 +304,19 @@ fn set_false_with_explicit_default_value() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); } @@ -333,7 +381,10 @@ fn count() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 0); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -342,7 +393,10 @@ fn count() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 1); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -351,7 +405,10 @@ fn count() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 2); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); } @@ -370,13 +427,19 @@ fn count_with_explicit_default_value() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 1); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 10); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); } diff --git a/tests/builder/app_settings.rs b/tests/builder/app_settings.rs index 405532dc0ea..34f8f6efc27 100644 --- a/tests/builder/app_settings.rs +++ b/tests/builder/app_settings.rs @@ -1,6 +1,6 @@ use std::ffi::OsString; -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, AppSettings, Arg, Command}; @@ -1064,6 +1064,7 @@ fn built_in_subcommand_escaped() { #[test] fn aaos_flags() { + #![allow(deprecated)] // flags let res = Command::new("posix") .args_override_self(true) @@ -1077,6 +1078,7 @@ fn aaos_flags() { #[test] fn aaos_flags_mult() { + #![allow(deprecated)] // flags with multiple let res = Command::new("posix") .args_override_self(true) @@ -1090,6 +1092,7 @@ fn aaos_flags_mult() { #[test] fn aaos_opts() { + #![allow(deprecated)] // opts let res = Command::new("posix") .args_override_self(true) @@ -1107,6 +1110,7 @@ fn aaos_opts() { #[test] fn aaos_opts_w_other_overrides() { + #![allow(deprecated)] // opts with other overrides let res = Command::new("posix") .args_override_self(true) @@ -1130,6 +1134,7 @@ fn aaos_opts_w_other_overrides() { #[test] fn aaos_opts_w_other_overrides_rev() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1152,6 +1157,7 @@ fn aaos_opts_w_other_overrides_rev() { #[test] fn aaos_opts_w_other_overrides_2() { + #![allow(deprecated)] // opts with other overrides let res = Command::new("posix") .args_override_self(true) @@ -1175,6 +1181,7 @@ fn aaos_opts_w_other_overrides_2() { #[test] fn aaos_opts_w_other_overrides_rev_2() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1197,6 +1204,7 @@ fn aaos_opts_w_other_overrides_rev_2() { #[test] fn aaos_opts_w_override_as_conflict_1() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1216,6 +1224,7 @@ fn aaos_opts_w_override_as_conflict_1() { #[test] fn aaos_opts_w_override_as_conflict_2() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1238,6 +1247,7 @@ fn aaos_opts_w_override_as_conflict_2() { #[test] fn aaos_opts_mult() { + #![allow(deprecated)] // opts with multiple let res = Command::new("posix") .args_override_self(true) @@ -1264,6 +1274,7 @@ fn aaos_opts_mult() { #[test] fn aaos_opts_mult_req_delims() { + #![allow(deprecated)] // opts with multiple and require delims let res = Command::new("posix") .args_override_self(true) @@ -1293,6 +1304,7 @@ fn aaos_opts_mult_req_delims() { #[test] fn aaos_pos_mult() { + #![allow(deprecated)] // opts with multiple let res = Command::new("posix") .args_override_self(true) @@ -1313,6 +1325,7 @@ fn aaos_pos_mult() { #[test] fn aaos_option_use_delim_false() { + #![allow(deprecated)] let m = Command::new("posix") .args_override_self(true) .arg(arg!(--opt "some option").use_value_delimiter(false)) diff --git a/tests/builder/arg_aliases.rs b/tests/builder/arg_aliases.rs index 71ccb18f89a..bfe2762326b 100644 --- a/tests/builder/arg_aliases.rs +++ b/tests/builder/arg_aliases.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; diff --git a/tests/builder/arg_aliases_short.rs b/tests/builder/arg_aliases_short.rs index 6d70910a0d0..d0a80a0d1d1 100644 --- a/tests/builder/arg_aliases_short.rs +++ b/tests/builder/arg_aliases_short.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; diff --git a/tests/builder/conflicts.rs b/tests/builder/conflicts.rs index 345afa50674..2fce0170388 100644 --- a/tests/builder/conflicts.rs +++ b/tests/builder/conflicts.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; diff --git a/tests/builder/default_missing_vals.rs b/tests/builder/default_missing_vals.rs index 1d2913e4d12..ecab4b82e36 100644 --- a/tests/builder/default_missing_vals.rs +++ b/tests/builder/default_missing_vals.rs @@ -19,7 +19,10 @@ fn opt_missing() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "auto" ); - assert_eq!(m.occurrences_of("color"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 0); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::DefaultValue @@ -46,7 +49,10 @@ fn opt_present_with_missing_value() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "always" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine @@ -73,7 +79,10 @@ fn opt_present_with_value() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "never" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine @@ -99,7 +108,10 @@ fn opt_present_with_empty_value() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine @@ -164,7 +176,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("false") ); - assert_eq!(m.occurrences_of("flag"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 0); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::DefaultValue @@ -179,7 +194,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("true") ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::CommandLine @@ -194,7 +212,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("true") ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::CommandLine @@ -206,7 +227,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("false") ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::CommandLine @@ -233,7 +257,10 @@ fn delimited_missing_value() { .collect::>(), vec!["one", "two"] ); - assert_eq!(m.occurrences_of("flag"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 0); + } let m = cmd.try_get_matches_from(["test", "--flag"]).unwrap(); assert_eq!( @@ -243,7 +270,10 @@ fn delimited_missing_value() { .collect::>(), vec!["three", "four"] ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } } #[cfg(debug_assertions)] @@ -295,7 +325,10 @@ fn valid_index() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "always" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine diff --git a/tests/builder/default_vals.rs b/tests/builder/default_vals.rs index b6de1e52e1c..dd05d94673b 100644 --- a/tests/builder/default_vals.rs +++ b/tests/builder/default_vals.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, Command}; #[test] diff --git a/tests/builder/delimiters.rs b/tests/builder/delimiters.rs index 81f5dac8d87..fbbc1b6622c 100644 --- a/tests/builder/delimiters.rs +++ b/tests/builder/delimiters.rs @@ -10,7 +10,10 @@ fn opt_default_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -27,7 +30,10 @@ fn opt_eq_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -44,7 +50,10 @@ fn opt_s_eq_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -61,7 +70,10 @@ fn opt_s_default_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -78,7 +90,10 @@ fn opt_s_no_space_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -100,7 +115,10 @@ fn opt_s_no_space_mult_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -123,7 +141,10 @@ fn opt_eq_mult_def_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() diff --git a/tests/builder/derive_order.rs b/tests/builder/derive_order.rs index a257b77cc76..5277806480f 100644 --- a/tests/builder/derive_order.rs +++ b/tests/builder/derive_order.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use std::str; diff --git a/tests/builder/display_order.rs b/tests/builder/display_order.rs index 68b4c3d3634..139682d80e3 100644 --- a/tests/builder/display_order.rs +++ b/tests/builder/display_order.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::Command; diff --git a/tests/builder/empty_values.rs b/tests/builder/empty_values.rs index 34102be29a1..9313435c604 100644 --- a/tests/builder/empty_values.rs +++ b/tests/builder/empty_values.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{error::ErrorKind, Arg, Command}; diff --git a/tests/builder/env.rs b/tests/builder/env.rs index 28129df32dc..9b4538cd5e4 100644 --- a/tests/builder/env.rs +++ b/tests/builder/env.rs @@ -16,7 +16,10 @@ fn env() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -37,7 +40,10 @@ fn env_bool_literal() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("present")); - assert_eq!(m.occurrences_of("present"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("present"), 0); + } assert_eq!(m.get_one::("present").map(|v| v.as_str()), None); assert!(!m.is_present("negated")); assert!(!m.is_present("absent")); @@ -58,7 +64,10 @@ fn env_os() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -82,7 +91,10 @@ fn no_env() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(!m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!(m.get_one::("arg").map(|v| v.as_str()), None); } @@ -99,7 +111,10 @@ fn no_env_no_takes_value() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(!m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!(m.get_one::("arg").map(|v| v.as_str()), None); } @@ -119,7 +134,10 @@ fn with_default() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -141,7 +159,10 @@ fn opt_user_override() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 1); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "opt" @@ -171,7 +192,10 @@ fn positionals() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -193,7 +217,10 @@ fn positionals_user_override() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 1); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "opt" @@ -225,7 +252,10 @@ fn multiple_one() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_many::("arg") .unwrap() @@ -252,7 +282,10 @@ fn multiple_three() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_many::("arg") .unwrap() @@ -278,7 +311,10 @@ fn multiple_no_delimiter() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_many::("arg") .unwrap() @@ -304,7 +340,10 @@ fn possible_value() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -349,7 +388,10 @@ fn value_parser() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" diff --git a/tests/builder/error.rs b/tests/builder/error.rs index 317d7251b71..61f8dcf2385 100644 --- a/tests/builder/error.rs +++ b/tests/builder/error.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, value_parser, Arg, Command, Error}; diff --git a/tests/builder/flag_subcommands.rs b/tests/builder/flag_subcommands.rs index 6c43414846b..bb1201d44a6 100644 --- a/tests/builder/flag_subcommands.rs +++ b/tests/builder/flag_subcommands.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, Command}; diff --git a/tests/builder/flags.rs b/tests/builder/flags.rs index eb14a6e5ec2..0cd99f4c62b 100644 --- a/tests/builder/flags.rs +++ b/tests/builder/flags.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; const USE_FLAG_AS_ARGUMENT: &str = @@ -56,7 +56,10 @@ fn lots_o_flags_sep() { assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); let m = r.unwrap(); assert!(m.is_present("o")); - assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("o"), 297); + } // i.e. more than u8 } #[test] @@ -74,7 +77,10 @@ fn lots_o_flags_combined() { assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); let m = r.unwrap(); assert!(m.is_present("o")); - assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("o"), 297); + } // i.e. more than u8 } #[test] diff --git a/tests/builder/grouped_values.rs b/tests/builder/grouped_values.rs index acb750923c6..03d1b5355c5 100644 --- a/tests/builder/grouped_values.rs +++ b/tests/builder/grouped_values.rs @@ -1,6 +1,6 @@ #![cfg(feature = "unstable-grouped")] -use clap::{Arg, Command}; +use clap::{Arg, ArgAction, Command}; #[test] fn grouped_value_works() { @@ -10,7 +10,7 @@ fn grouped_value_works() { .long("option") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .try_get_matches_from(&[ "cli", @@ -42,7 +42,7 @@ fn issue_1026() { .long("target") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .try_get_matches_from(&[ "backup", "-s", "server", "-u", "user", "--target", "target1", "file1", "file2", @@ -70,7 +70,7 @@ fn grouped_value_long_flag_delimiter() { .takes_value(true) .use_value_delimiter(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .try_get_matches_from(vec![ "myapp", @@ -100,7 +100,7 @@ fn grouped_value_short_flag_delimiter() { .takes_value(true) .use_value_delimiter(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .try_get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"]) .unwrap(); @@ -183,41 +183,41 @@ fn grouped_interleaved_positional_values() { .short('f') .long("flag") .takes_value(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ); let m = cmd .try_get_matches_from(["foo", "1", "2", "-f", "a", "3", "-f", "b", "4"]) .unwrap(); + let pos: Vec<_> = m.grouped_values_of("pos").unwrap().collect(); assert_eq!(pos, vec![vec!["1", "2", "3", "4"]]); - assert_eq!(m.occurrences_of("pos"), 4); + let flag: Vec<_> = m.grouped_values_of("flag").unwrap().collect(); assert_eq!(flag, vec![vec!["a"], vec!["b"]]); - assert_eq!(m.occurrences_of("flag"), 2); } #[test] fn grouped_interleaved_positional_occurrences() { let cmd = clap::Command::new("foo") - .arg(clap::Arg::new("pos").multiple_occurrences(true)) + .arg(clap::Arg::new("pos").multiple_values(true)) .arg( clap::Arg::new("flag") .short('f') .long("flag") .takes_value(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ); let m = cmd .try_get_matches_from(["foo", "1", "2", "-f", "a", "3", "-f", "b", "4"]) .unwrap(); + let pos: Vec<_> = m.grouped_values_of("pos").unwrap().collect(); - assert_eq!(pos, vec![vec!["1"], vec!["2"], vec!["3"], vec!["4"]]); - assert_eq!(m.occurrences_of("pos"), 4); + assert_eq!(pos, vec![vec!["1", "2", "3", "4"]]); + let flag: Vec<_> = m.grouped_values_of("flag").unwrap().collect(); assert_eq!(flag, vec![vec!["a"], vec!["b"]]); - assert_eq!(m.occurrences_of("flag"), 2); } #[test] @@ -228,7 +228,7 @@ fn issue_1374() { .long("input") .overrides_with("input") .min_values(0) - .multiple_occurrences(true), + .action(ArgAction::Append), ); let matches = cmd .clone() @@ -251,7 +251,8 @@ fn issue_1374() { } #[test] -fn issue_2171() { +fn issue_2171_deprecated() { + #![allow(deprecated)] let schema = Command::new("ripgrep#1701 reproducer") .args_override_self(true) .arg(Arg::new("pretty").short('p').long("pretty")) @@ -271,3 +272,34 @@ fn issue_2171() { let _ = schema.clone().try_get_matches_from(argv).unwrap(); } } + +#[test] +fn issue_2171() { + let schema = Command::new("ripgrep#1701 reproducer") + .arg( + Arg::new("pretty") + .short('p') + .long("pretty") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("search_zip") + .short('z') + .long("search-zip") + .action(ArgAction::SetTrue), + ); + + let test_args = &[ + vec!["reproducer", "-pz", "-p"], + vec!["reproducer", "-pzp"], + vec!["reproducer", "-zpp"], + vec!["reproducer", "-pp", "-z"], + vec!["reproducer", "-p", "-p", "-z"], + vec!["reproducer", "-p", "-pz"], + vec!["reproducer", "-ppz"], + ]; + + for argv in test_args { + let _ = schema.clone().try_get_matches_from(argv).unwrap(); + } +} diff --git a/tests/builder/groups.rs b/tests/builder/groups.rs index 2247b989728..f13b299eb4e 100644 --- a/tests/builder/groups.rs +++ b/tests/builder/groups.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; diff --git a/tests/builder/help.rs b/tests/builder/help.rs index 737ef25c8ae..6aaec5afee0 100644 --- a/tests/builder/help.rs +++ b/tests/builder/help.rs @@ -1,6 +1,6 @@ -use crate::utils; +use super::utils; -use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command, PossibleValue}; +use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, PossibleValue}; static REQUIRE_DELIM_HELP: &str = "test 1.3 Kevin K. @@ -672,7 +672,7 @@ fn help_multi_subcommand_error() { ) .required(false) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ), ), ); @@ -720,7 +720,7 @@ fn args_with_last_usage() { .help("Prints out more stuff.") .short('v') .long("verbose") - .multiple_occurrences(true), + .action(ArgAction::SetTrue), ) .arg( Arg::new("timeout") @@ -822,7 +822,7 @@ fn multi_level_sc_help() { ) .required(false) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ), ), ); @@ -2514,7 +2514,8 @@ fn positional_multiple_occurrences_is_dotted() { Arg::new("foo") .required(true) .takes_value(true) - .multiple_occurrences(true), + .multiple_values(true) + .action(ArgAction::Append), ); utils::assert_output( cmd, @@ -2538,7 +2539,8 @@ OPTIONS: .required(true) .takes_value(true) .value_name("BAR") - .multiple_occurrences(true), + .multiple_values(true) + .action(ArgAction::Append), ); utils::assert_output( cmd, diff --git a/tests/builder/help_env.rs b/tests/builder/help_env.rs index 7aa5d8154b7..5b8c2e8aa2e 100644 --- a/tests/builder/help_env.rs +++ b/tests/builder/help_env.rs @@ -4,7 +4,7 @@ use std::env; use clap::{Arg, Command}; -use crate::utils; +use super::utils; static HIDE_ENV: &str = "ctest 0.1 diff --git a/tests/builder/hidden_args.rs b/tests/builder/hidden_args.rs index d2421d7ca47..8af6a788ac7 100644 --- a/tests/builder/hidden_args.rs +++ b/tests/builder/hidden_args.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; diff --git a/tests/builder/indices.rs b/tests/builder/indices.rs index 403cf86f579..c5f236fd07c 100644 --- a/tests/builder/indices.rs +++ b/tests/builder/indices.rs @@ -1,4 +1,4 @@ -use clap::{Arg, Command}; +use clap::{Arg, ArgAction, Command}; #[test] fn indices_mult_opts() { @@ -8,7 +8,7 @@ fn indices_mult_opts() { .short('e') .takes_value(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg( Arg::new("include") @@ -37,7 +37,7 @@ fn index_mult_opts() { .short('e') .takes_value(true) .multiple_values(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg( Arg::new("include") @@ -67,88 +67,64 @@ fn index_flag() { #[test] fn index_flags() { let m = Command::new("ind") - .arg(Arg::new("exclude").short('e').multiple_occurrences(true)) - .arg(Arg::new("include").short('i').multiple_occurrences(true)) + .arg(Arg::new("exclude").short('e').action(ArgAction::SetTrue)) + .arg(Arg::new("include").short('i').action(ArgAction::SetTrue)) .try_get_matches_from(vec!["ind", "-e", "-i", "-e", "-e", "-i"]) .unwrap(); - assert_eq!(m.index_of("exclude"), Some(1)); - assert_eq!(m.index_of("include"), Some(2)); + assert_eq!(m.index_of("exclude"), Some(4)); + assert_eq!(m.index_of("include"), Some(5)); } #[test] fn indices_mult_flags() { let m = Command::new("ind") - .arg(Arg::new("exclude").short('e').multiple_occurrences(true)) - .arg(Arg::new("include").short('i').multiple_occurrences(true)) + .arg(Arg::new("exclude").short('e').action(ArgAction::SetTrue)) + .arg(Arg::new("include").short('i').action(ArgAction::SetTrue)) .try_get_matches_from(vec!["ind", "-e", "-i", "-e", "-e", "-i"]) .unwrap(); - assert_eq!( - m.indices_of("exclude").unwrap().collect::>(), - &[1, 3, 4] - ); - assert_eq!( - m.indices_of("include").unwrap().collect::>(), - &[2, 5] - ); + assert_eq!(m.indices_of("exclude").unwrap().collect::>(), &[4]); + assert_eq!(m.indices_of("include").unwrap().collect::>(), &[5]); } #[test] fn indices_mult_flags_combined() { let m = Command::new("ind") - .arg(Arg::new("exclude").short('e').multiple_occurrences(true)) - .arg(Arg::new("include").short('i').multiple_occurrences(true)) + .arg(Arg::new("exclude").short('e').action(ArgAction::SetTrue)) + .arg(Arg::new("include").short('i').action(ArgAction::SetTrue)) .try_get_matches_from(vec!["ind", "-eieei"]) .unwrap(); - assert_eq!( - m.indices_of("exclude").unwrap().collect::>(), - &[1, 3, 4] - ); - assert_eq!( - m.indices_of("include").unwrap().collect::>(), - &[2, 5] - ); + assert_eq!(m.indices_of("exclude").unwrap().collect::>(), &[4]); + assert_eq!(m.indices_of("include").unwrap().collect::>(), &[5]); } #[test] fn indices_mult_flags_opt_combined() { let m = Command::new("ind") - .arg(Arg::new("exclude").short('e').multiple_occurrences(true)) - .arg(Arg::new("include").short('i').multiple_occurrences(true)) + .arg(Arg::new("exclude").short('e').action(ArgAction::SetTrue)) + .arg(Arg::new("include").short('i').action(ArgAction::SetTrue)) .arg(Arg::new("option").short('o').takes_value(true)) .try_get_matches_from(vec!["ind", "-eieeio", "val"]) .unwrap(); - assert_eq!( - m.indices_of("exclude").unwrap().collect::>(), - &[1, 3, 4] - ); - assert_eq!( - m.indices_of("include").unwrap().collect::>(), - &[2, 5] - ); + assert_eq!(m.indices_of("exclude").unwrap().collect::>(), &[4]); + assert_eq!(m.indices_of("include").unwrap().collect::>(), &[5]); assert_eq!(m.indices_of("option").unwrap().collect::>(), &[7]); } #[test] fn indices_mult_flags_opt_combined_eq() { let m = Command::new("ind") - .arg(Arg::new("exclude").short('e').multiple_occurrences(true)) - .arg(Arg::new("include").short('i').multiple_occurrences(true)) + .arg(Arg::new("exclude").short('e').action(ArgAction::SetTrue)) + .arg(Arg::new("include").short('i').action(ArgAction::SetTrue)) .arg(Arg::new("option").short('o').takes_value(true)) .try_get_matches_from(vec!["ind", "-eieeio=val"]) .unwrap(); - assert_eq!( - m.indices_of("exclude").unwrap().collect::>(), - &[1, 3, 4] - ); - assert_eq!( - m.indices_of("include").unwrap().collect::>(), - &[2, 5] - ); + assert_eq!(m.indices_of("exclude").unwrap().collect::>(), &[4]); + assert_eq!(m.indices_of("include").unwrap().collect::>(), &[5]); assert_eq!(m.indices_of("option").unwrap().collect::>(), &[7]); } @@ -187,16 +163,11 @@ fn indices_mult_opt_value_no_delim_eq() { #[test] fn indices_mult_opt_mult_flag() { let m = Command::new("myapp") - .arg( - Arg::new("option") - .short('o') - .takes_value(true) - .multiple_occurrences(true), - ) - .arg(Arg::new("flag").short('f').multiple_occurrences(true)) + .arg(Arg::new("option").short('o').action(ArgAction::Append)) + .arg(Arg::new("flag").short('f').action(ArgAction::SetTrue)) .try_get_matches_from(vec!["myapp", "-o", "val1", "-f", "-o", "val2", "-f"]) .unwrap(); assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2, 5]); - assert_eq!(m.indices_of("flag").unwrap().collect::>(), &[3, 6]); + assert_eq!(m.indices_of("flag").unwrap().collect::>(), &[6]); } diff --git a/tests/builder/legacy/action.rs b/tests/builder/legacy/action.rs new file mode 100644 index 00000000000..e6d426d4cb6 --- /dev/null +++ b/tests/builder/legacy/action.rs @@ -0,0 +1,505 @@ +#![allow(clippy::bool_assert_comparison)] + +use clap::Arg; +use clap::ArgAction; +use clap::Command; + +#[test] +fn set() { + let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Set)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(matches.get_one::("mammal"), None); + assert_eq!(matches.is_present("mammal"), false); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), None); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog"]) + .unwrap(); + assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog", "--mammal", "cat"]) + .unwrap(); + assert_eq!(matches.get_one::("mammal").unwrap(), "cat"); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(4)); +} + +#[test] +fn append() { + let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Append)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(matches.get_one::("mammal"), None); + assert_eq!(matches.is_present("mammal"), false); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), None); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog"]) + .unwrap(); + assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!( + matches.indices_of("mammal").unwrap().collect::>(), + vec![2] + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog", "--mammal", "cat"]) + .unwrap(); + assert_eq!( + matches + .get_many::("mammal") + .unwrap() + .map(|s| s.as_str()) + .collect::>(), + vec!["dog", "cat"] + ); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!( + matches.indices_of("mammal").unwrap().collect::>(), + vec![2, 4] + ); +} + +#[test] +fn set_true() { + let cmd = + Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); +} + +#[test] +fn set_true_with_explicit_default_value() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .default_value("false"), + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); +} + +#[test] +fn set_true_with_default_value_if_present() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .default_value_if("dog", None, Some("true")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); +} + +#[test] +fn set_true_with_default_value_if_value() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .default_value_if("dog", Some("true"), Some("true")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); +} + +#[test] +fn set_true_with_required_if_eq() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .required_if_eq("dog", "true"), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + cmd.clone() + .try_get_matches_from(["test", "--dog"]) + .unwrap_err(); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--dog", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); +} + +#[test] +fn set_false() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse), + ); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); +} + +#[test] +fn set_false_with_explicit_default_value() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse) + .default_value("true"), + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); +} + +#[test] +fn set_false_with_default_value_if_present() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse) + .default_value_if("dog", None, Some("false")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); +} + +#[test] +fn set_false_with_default_value_if_value() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse) + .default_value_if("dog", Some("false"), Some("false")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); +} + +#[test] +fn count() { + let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Count)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 2); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); +} + +#[test] +fn count_with_explicit_default_value() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::Count) + .default_value("10"), + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 10); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); +} + +#[test] +fn count_with_default_value_if_present() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::Count) + .default_value_if("dog", None, Some("10")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::Count)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 1); + assert_eq!(*matches.get_one::("mammal").unwrap(), 10); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); +} + +#[test] +fn count_with_default_value_if_value() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::Count) + .default_value_if("dog", Some("2"), Some("10")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::Count)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 1); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--dog", "--dog"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 2); + assert_eq!(*matches.get_one::("mammal").unwrap(), 10); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); +} diff --git a/tests/builder/legacy/app_from_crate.rs b/tests/builder/legacy/app_from_crate.rs new file mode 100644 index 00000000000..7559e697ce1 --- /dev/null +++ b/tests/builder/legacy/app_from_crate.rs @@ -0,0 +1,28 @@ +#![cfg(feature = "cargo")] +#![allow(deprecated)] + +use clap::{app_from_crate, error::ErrorKind}; + +static EVERYTHING: &str = "clap {{version}} +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + clap + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +#[test] +fn app_from_crate() { + let res = app_from_crate!().try_get_matches_from(vec!["clap", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!( + err.to_string(), + EVERYTHING.replace("{{version}}", env!("CARGO_PKG_VERSION")) + ); +} diff --git a/tests/builder/legacy/app_settings.rs b/tests/builder/legacy/app_settings.rs new file mode 100644 index 00000000000..3759d4cb15e --- /dev/null +++ b/tests/builder/legacy/app_settings.rs @@ -0,0 +1,1261 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, AppSettings, Arg, Command}; + +static ALLOW_EXT_SC: &str = "clap-test v1.4.8 + +USAGE: + clap-test [SUBCOMMAND] + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +static DONT_COLLAPSE_ARGS: &str = "clap-test v1.4.8 + +USAGE: + clap-test [arg1] [arg2] [arg3] + +ARGS: + some + some + some + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +static REQUIRE_EQUALS: &str = "clap-test v1.4.8 + +USAGE: + clap-test --opt= + +OPTIONS: + -h, --help Print help information + -o, --opt= some + -V, --version Print version information +"; + +static SKIP_POS_VALS: &str = "test 1.3 +Kevin K. +tests stuff + +USAGE: + test [OPTIONS] [arg1] + +ARGS: + some pos arg + +OPTIONS: + -h, --help Print help information + -o, --opt some option + -V, --version Print version information +"; + +static ARG_REQUIRED_ELSE_HELP: &str = "test 1.0 + +USAGE: + test [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info Provides more info + -V, --version Print version information +"; + +static SUBCOMMAND_REQUIRED_ELSE_HELP: &str = "test 1.0 + +USAGE: + test + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + info +"; + +#[test] +fn setting() { + #![allow(deprecated)] + let m = Command::new("setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); +} + +#[test] +fn global_setting() { + #![allow(deprecated)] + let m = Command::new("global_setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); +} + +#[test] +fn unset_setting() { + #![allow(deprecated)] + let m = Command::new("unset_setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); + + let m = m.args_override_self(false); + assert!(!m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m); +} + +#[test] +fn unset_global_setting() { + #![allow(deprecated)] + let m = Command::new("unset_global_setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); + + let m = m.args_override_self(false); + assert!(!m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m); +} + +#[test] +fn setting_bitor() { + #![allow(deprecated)] + let m = Command::new("setting_bitor").setting( + AppSettings::InferSubcommands | AppSettings::Hidden | AppSettings::DisableHelpSubcommand, + ); + + assert!(m.is_set(AppSettings::InferSubcommands)); + assert!(m.is_set(AppSettings::Hidden)); + assert!(m.is_set(AppSettings::DisableHelpSubcommand)); +} + +#[test] +fn unset_setting_bitor() { + #![allow(deprecated)] + let m = Command::new("unset_setting_bitor") + .setting(AppSettings::InferSubcommands) + .setting(AppSettings::Hidden) + .setting(AppSettings::DisableHelpSubcommand); + + assert!(m.is_set(AppSettings::InferSubcommands)); + assert!(m.is_set(AppSettings::Hidden)); + assert!(m.is_set(AppSettings::DisableHelpSubcommand)); + + let m = m.unset_setting( + AppSettings::InferSubcommands | AppSettings::Hidden | AppSettings::DisableHelpSubcommand, + ); + assert!(!m.is_set(AppSettings::InferSubcommands), "{:#?}", m); + assert!(!m.is_set(AppSettings::Hidden), "{:#?}", m); + assert!(!m.is_set(AppSettings::DisableHelpSubcommand), "{:#?}", m); +} + +#[test] +fn sub_command_negate_required() { + Command::new("sub_command_negate") + .subcommand_negates_reqs(true) + .arg(Arg::new("test").required(true).index(1)) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec!["myprog", "sub1"]) + .unwrap(); +} + +#[test] +fn sub_command_negate_required_2() { + let result = Command::new("sub_command_negate") + .subcommand_negates_reqs(true) + .arg(Arg::new("test").required(true).index(1)) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn sub_command_required() { + let result = Command::new("sc_required") + .subcommand_required(true) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingSubcommand); +} + +#[test] +fn arg_required_else_help() { + let result = Command::new("arg_required") + .arg_required_else_help(true) + .arg(Arg::new("test").index(1)) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_over_req_arg() { + let result = Command::new("arg_required") + .arg_required_else_help(true) + .arg(Arg::new("test").index(1).required(true)) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_over_req_subcommand() { + let result = Command::new("sub_required") + .arg_required_else_help(true) + .subcommand_required(true) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_with_default() { + let result = Command::new("arg_required") + .arg_required_else_help(true) + .arg(arg!(--input ).required(false).default_value("-")) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_error_message() { + let cmd = Command::new("test") + .arg_required_else_help(true) + .version("1.0") + .arg( + Arg::new("info") + .help("Provides more info") + .short('i') + .long("info"), + ); + utils::assert_output( + cmd, + "test", + ARG_REQUIRED_ELSE_HELP, + true, // Unlike normal displaying of help, we should provide a fatal exit code + ); +} + +#[test] +fn subcommand_required_else_help() { + #![allow(deprecated)] + let result = Command::new("test") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(Command::new("info")) + .try_get_matches_from(&[""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn subcommand_required_else_help_error_message() { + #![allow(deprecated)] + let cmd = Command::new("test") + .setting(AppSettings::SubcommandRequiredElseHelp) + .version("1.0") + .subcommand(Command::new("info").arg(Arg::new("filename"))); + utils::assert_output( + cmd, + "test", + SUBCOMMAND_REQUIRED_ELSE_HELP, + true, // Unlike normal displaying of help, we should provide a fatal exit code + ); +} + +#[cfg(not(feature = "suggestions"))] +#[test] +fn infer_subcommands_fail_no_args() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand); +} + +#[cfg(feature = "suggestions")] +#[test] +fn infer_subcommands_fail_no_args() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); +} + +#[test] +fn infer_subcommands_fail_with_args() { + let m = Command::new("prog") + .infer_subcommands(true) + .arg(Arg::new("some")) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "t"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + assert_eq!(m.unwrap().value_of("some"), Some("t")); +} + +#[test] +fn infer_subcommands_fail_with_args2() { + let m = Command::new("prog") + .infer_subcommands(true) + .arg(Arg::new("some")) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "te"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + assert_eq!(m.unwrap().value_of("some"), Some("te")); +} + +#[test] +fn infer_subcommands_pass() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .try_get_matches_from(vec!["prog", "te"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[test] +fn infer_subcommands_pass_close() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "tes"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[test] +fn infer_subcommands_pass_exact_match() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("testa")) + .subcommand(Command::new("testb")) + .try_get_matches_from(vec!["prog", "test"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[cfg(feature = "suggestions")] +#[test] +fn infer_subcommands_fail_suggestions() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "temps"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); +} + +#[cfg(not(feature = "suggestions"))] +#[test] +fn infer_subcommands_fail_suggestions() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "temps"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand); +} + +#[test] +fn no_bin_name() { + let result = Command::new("arg_required") + .no_binary_name(true) + .arg(Arg::new("test").required(true).index(1)) + .try_get_matches_from(vec!["testing"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); + let matches = result.unwrap(); + assert_eq!(matches.value_of("test").unwrap(), "testing"); +} + +#[test] +fn skip_possible_values() { + let cmd = Command::new("test") + .author("Kevin K.") + .about("tests stuff") + .version("1.3") + .hide_possible_values(true) + .args(&[ + arg!(-o --opt "some option") + .required(false) + .possible_values(["one", "two"]), + arg!([arg1] "some pos arg").possible_values(["three", "four"]), + ]); + + utils::assert_output(cmd, "test --help", SKIP_POS_VALS, false); +} + +#[test] +fn stop_delim_values_only_pos_follows() { + let r = Command::new("onlypos") + .dont_delimit_trailing_values(true) + .args(&[ + arg!(f: -f "some opt").required(false), + arg!([arg] ... "some arg"), + ]) + .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert!(!m.is_present("f")); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + &["-f", "-g,x"] + ); +} + +#[test] +fn dont_delim_values_trailingvararg() { + let m = Command::new("positional") + .trailing_var_arg(true) + .dont_delimit_trailing_values(true) + .arg(arg!([opt] ... "some pos")) + .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["test", "--foo", "-Wl,-bar"] + ); +} + +#[test] +fn delim_values_only_pos_follows() { + let r = Command::new("onlypos") + .args(&[arg!(f: -f [flag] "some opt"), arg!([arg] ... "some arg")]) + .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert!(!m.is_present("f")); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + &["-f", "-g,x"] + ); +} + +#[test] +fn delim_values_trailingvararg() { + let m = Command::new("positional") + .trailing_var_arg(true) + .arg(arg!([opt] ... "some pos")) + .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["test", "--foo", "-Wl,-bar"] + ); +} + +#[test] +fn delim_values_only_pos_follows_with_delim() { + let r = Command::new("onlypos") + .args(&[ + arg!(f: -f [flag] "some opt"), + arg!([arg] ... "some arg").use_value_delimiter(true), + ]) + .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert!(!m.is_present("f")); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + &["-f", "-g", "x"] + ); +} + +#[test] +fn delim_values_trailingvararg_with_delim() { + let m = Command::new("positional") + .trailing_var_arg(true) + .arg(arg!([opt] ... "some pos").use_value_delimiter(true)) + .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["test", "--foo", "-Wl", "-bar"] + ); +} + +#[test] +fn leading_hyphen_short() { + let res = Command::new("leadhy") + .allow_hyphen_values(true) + .arg(Arg::new("some")) + .arg(Arg::new("other").short('o')) + .try_get_matches_from(vec!["", "-bar", "-o"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert!(m.is_present("some")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("some").unwrap(), "-bar"); +} + +#[test] +fn leading_hyphen_long() { + let res = Command::new("leadhy") + .allow_hyphen_values(true) + .arg(Arg::new("some")) + .arg(Arg::new("other").short('o')) + .try_get_matches_from(vec!["", "--bar", "-o"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert!(m.is_present("some")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("some").unwrap(), "--bar"); +} + +#[test] +fn leading_hyphen_opt() { + let res = Command::new("leadhy") + .allow_hyphen_values(true) + .arg(Arg::new("some").takes_value(true).long("opt")) + .arg(Arg::new("other").short('o')) + .try_get_matches_from(vec!["", "--opt", "--bar", "-o"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert!(m.is_present("some")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("some").unwrap(), "--bar"); +} + +#[test] +fn allow_negative_numbers() { + let res = Command::new("negnum") + .allow_negative_numbers(true) + .arg(Arg::new("panum")) + .arg(Arg::new("onum").short('o').takes_value(true)) + .try_get_matches_from(vec!["negnum", "-20", "-o", "-1.2"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert_eq!(m.value_of("panum").unwrap(), "-20"); + assert_eq!(m.value_of("onum").unwrap(), "-1.2"); +} + +#[test] +fn allow_negative_numbers_fail() { + let res = Command::new("negnum") + .allow_negative_numbers(true) + .arg(Arg::new("panum")) + .arg(Arg::new("onum").short('o').takes_value(true)) + .try_get_matches_from(vec!["negnum", "--foo", "-o", "-1.2"]); + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument) +} + +#[test] +fn leading_double_hyphen_trailingvararg() { + let m = Command::new("positional") + .trailing_var_arg(true) + .allow_hyphen_values(true) + .arg(arg!([opt] ... "some pos")) + .try_get_matches_from(vec!["", "--foo", "-Wl", "bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["--foo", "-Wl", "bar"] + ); +} + +#[test] +fn disable_help_subcommand() { + let result = Command::new("disablehelp") + .disable_help_subcommand(true) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec!["", "help"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn dont_collapse_args() { + let cmd = Command::new("clap-test") + .version("v1.4.8") + .dont_collapse_args_in_usage(true) + .args(&[ + Arg::new("arg1").help("some"), + Arg::new("arg2").help("some"), + Arg::new("arg3").help("some"), + ]); + utils::assert_output(cmd, "clap-test --help", DONT_COLLAPSE_ARGS, false); +} + +#[test] +fn require_eq() { + let cmd = Command::new("clap-test").version("v1.4.8").arg( + Arg::new("opt") + .long("opt") + .short('o') + .required(true) + .require_equals(true) + .value_name("FILE") + .help("some"), + ); + utils::assert_output(cmd, "clap-test --help", REQUIRE_EQUALS, false); +} + +#[test] +fn args_negate_subcommands_one_level() { + let res = Command::new("disablehelp") + .args_conflicts_with_subcommands(true) + .subcommand_negates_reqs(true) + .arg(arg!( "some arg")) + .arg(arg!( "some arg")) + .subcommand( + Command::new("sub1").subcommand(Command::new("sub2").subcommand(Command::new("sub3"))), + ) + .try_get_matches_from(vec!["", "pickles", "sub1"]); + assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert_eq!(m.value_of("arg2"), Some("sub1")); +} + +#[test] +fn args_negate_subcommands_two_levels() { + let res = Command::new("disablehelp") + .args_conflicts_with_subcommands(true) + .subcommand_negates_reqs(true) + .arg(arg!( "some arg")) + .arg(arg!( "some arg")) + .subcommand( + Command::new("sub1") + .args_conflicts_with_subcommands(true) + .subcommand_negates_reqs(true) + .arg(arg!( "some")) + .arg(arg!( "some")) + .subcommand(Command::new("sub2").subcommand(Command::new("sub3"))), + ) + .try_get_matches_from(vec!["", "sub1", "arg", "sub2"]); + assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert_eq!( + m.subcommand_matches("sub1").unwrap().value_of("arg2"), + Some("sub2") + ); +} + +#[test] +fn propagate_vals_down() { + let m = Command::new("myprog") + .arg(arg!([cmd] "command to run").global(true)) + .subcommand(Command::new("foo")) + .try_get_matches_from(vec!["myprog", "set", "foo"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + let m = m.unwrap(); + assert_eq!(m.value_of("cmd"), Some("set")); + let sub_m = m.subcommand_matches("foo").unwrap(); + assert_eq!(sub_m.value_of("cmd"), Some("set")); +} + +#[test] +fn allow_missing_positional() { + let m = Command::new("test") + .allow_missing_positional(true) + .arg(arg!([src] "some file").default_value("src")) + .arg(arg!( "some file")) + .try_get_matches_from(vec!["test", "file"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + let m = m.unwrap(); + assert_eq!(m.value_of("src"), Some("src")); + assert_eq!(m.value_of("dest"), Some("file")); +} + +#[test] +fn allow_missing_positional_no_default() { + let m = Command::new("test") + .allow_missing_positional(true) + .arg(arg!([src] "some file")) + .arg(arg!( "some file")) + .try_get_matches_from(vec!["test", "file"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + let m = m.unwrap(); + assert_eq!(m.value_of("src"), None); + assert_eq!(m.value_of("dest"), Some("file")); +} + +#[test] +fn missing_positional_no_hyphen() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH] "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "foo", "arg1", "arg2", "arg3"]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + + let m = r.unwrap(); + + let expected_bench = Some("foo"); + let expected_args = vec!["arg1", "arg2", "arg3"]; + + assert_eq!(m.value_of("BENCH"), expected_bench); + assert_eq!( + m.values_of("ARGS").unwrap().collect::>(), + &*expected_args + ); +} + +#[test] +fn missing_positional_hyphen() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH] "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "--", "arg1", "arg2", "arg3"]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + + let m = r.unwrap(); + + let expected_bench = None; + let expected_args = vec!["arg1", "arg2", "arg3"]; + + assert_eq!(m.value_of("BENCH"), expected_bench); + assert_eq!( + m.values_of("ARGS").unwrap().collect::>(), + &*expected_args + ); +} + +#[test] +fn missing_positional_hyphen_far_back() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH1] "some bench")) + .arg(arg!([BENCH2] "some bench")) + .arg(arg!([BENCH3] "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + + let m = r.unwrap(); + + let expected_bench1 = Some("foo"); + let expected_bench2 = None; + let expected_bench3 = None; + let expected_args = vec!["arg1", "arg2", "arg3"]; + + assert_eq!(m.value_of("BENCH1"), expected_bench1); + assert_eq!(m.value_of("BENCH2"), expected_bench2); + assert_eq!(m.value_of("BENCH3"), expected_bench3); + assert_eq!( + m.values_of("ARGS").unwrap().collect::>(), + &*expected_args + ); +} + +#[test] +fn missing_positional_hyphen_req_error() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH1] "some bench")) + .arg(arg!( "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]); + assert!(r.is_err()); + assert_eq!(r.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn issue_1066_allow_leading_hyphen_and_unknown_args() { + let res = Command::new("prog") + .allow_hyphen_values(true) + .arg(arg!(--"some-argument")) + .try_get_matches_from(vec!["prog", "hello"]); + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn issue_1066_allow_leading_hyphen_and_unknown_args_no_vals() { + let res = Command::new("prog") + .allow_hyphen_values(true) + .arg(arg!(--"some-argument")) + .try_get_matches_from(vec!["prog", "--hello"]); + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn issue_1066_allow_leading_hyphen_and_unknown_args_option() { + let res = Command::new("prog") + .allow_hyphen_values(true) + .arg(arg!(--"some-argument" )) + .try_get_matches_from(vec!["prog", "-hello"]); + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn issue_1437_allow_hyphen_values_for_positional_arg() { + let m = Command::new("tmp") + .arg( + Arg::new("pat") + .allow_hyphen_values(true) + .required(true) + .takes_value(true), + ) + .try_get_matches_from(["tmp", "-file"]) + .unwrap(); + assert_eq!(m.value_of("pat"), Some("-file")); +} + +#[test] +fn issue_1093_allow_ext_sc() { + let cmd = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true); + utils::assert_output(cmd, "clap-test --help", ALLOW_EXT_SC, false); +} + +#[test] +#[cfg(not(feature = "unstable-v4"))] +fn allow_ext_sc_empty_args() { + let res = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .try_get_matches_from(vec!["clap-test", "external-cmd"]); + + assert!(res.is_ok(), "{}", res.unwrap_err()); + + match res.unwrap().subcommand() { + Some((name, args)) => { + assert_eq!(name, "external-cmd"); + assert_eq!(args.values_of_lossy(""), None); + } + _ => unreachable!(), + } +} + +#[test] +#[cfg(feature = "unstable-v4")] +fn allow_ext_sc_empty_args() { + let res = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .try_get_matches_from(vec!["clap-test", "external-cmd"]); + + assert!(res.is_ok(), "{}", res.unwrap_err()); + + match res.unwrap().subcommand() { + Some((name, args)) => { + assert_eq!(name, "external-cmd"); + assert_eq!(args.values_of_lossy(""), Some(vec![])); + } + _ => unreachable!(), + } +} + +#[test] +fn allow_ext_sc_when_sc_required() { + let res = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .subcommand_required(true) + .try_get_matches_from(vec!["clap-test", "external-cmd", "foo"]); + + assert!(res.is_ok(), "{}", res.unwrap_err()); + + match res.unwrap().subcommand() { + Some((name, args)) => { + assert_eq!(name, "external-cmd"); + assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()])); + } + _ => unreachable!(), + } +} + +#[test] +fn external_subcommand_looks_like_built_in() { + let res = Command::new("cargo") + .version("1.26.0") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .subcommand(Command::new("install")) + .try_get_matches_from(vec!["cargo", "install-update", "foo"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + match m.subcommand() { + Some((name, args)) => { + assert_eq!(name, "install-update"); + assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()])); + } + _ => panic!("external_subcommand didn't work"), + } +} + +#[test] +fn built_in_subcommand_escaped() { + let res = Command::new("cargo") + .version("1.26.0") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .subcommand(Command::new("install")) + .try_get_matches_from(vec!["cargo", "--", "install", "foo"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + match m.subcommand() { + Some((name, args)) => { + assert_eq!(name, "install"); + assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()])); + } + _ => panic!("external_subcommand didn't work"), + } +} + +#[test] +fn aaos_flags() { + // flags + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--flag "some flag")) + .try_get_matches_from(vec!["", "--flag", "--flag"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("flag")); + assert_eq!(m.occurrences_of("flag"), 1); +} + +#[test] +fn aaos_flags_mult() { + // flags with multiple + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--flag ... "some flag")) + .try_get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("flag")); + assert_eq!(m.occurrences_of("flag"), 4); +} + +#[test] +fn aaos_opts() { + // opts + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option")) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!(m.value_of("opt"), Some("other")); +} + +#[test] +fn aaos_opts_w_other_overrides() { + // opts with other overrides + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option").required(false)) + .arg( + arg!(--other "some other option") + .required(false) + .overrides_with("opt"), + ) + .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert!(!m.is_present("other")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!(m.value_of("opt"), Some("other")); +} + +#[test] +fn aaos_opts_w_other_overrides_rev() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option").required(true)) + .arg( + arg!(--other "some other option") + .required(true) + .overrides_with("opt"), + ) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(!m.is_present("opt")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("other"), Some("val")); +} + +#[test] +fn aaos_opts_w_other_overrides_2() { + // opts with other overrides + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(false) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(false)) + .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert!(!m.is_present("other")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!(m.value_of("opt"), Some("other")); +} + +#[test] +fn aaos_opts_w_other_overrides_rev_2() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(true) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(true)) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(!m.is_present("opt")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("other"), Some("val")); +} + +#[test] +fn aaos_opts_w_override_as_conflict_1() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(true) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(true)) + .try_get_matches_from(vec!["", "--opt=some"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert!(!m.is_present("other")); + assert_eq!(m.value_of("opt"), Some("some")); +} + +#[test] +fn aaos_opts_w_override_as_conflict_2() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(true) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(true)) + .try_get_matches_from(vec!["", "--other=some"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(!m.is_present("opt")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("other"), Some("some")); +} + +#[test] +fn aaos_opts_mult() { + // opts with multiple + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt ... "some option") + .number_of_values(1) + .takes_value(true) + .use_value_delimiter(true) + .require_value_delimiter(true), + ) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 3); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["some", "other", "one", "two"] + ); +} + +#[test] +fn aaos_opts_mult_req_delims() { + // opts with multiple and require delims + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt ... "some option").multiple_values(true)) + .try_get_matches_from(vec![ + "", + "--opt", + "first", + "overrides", + "--opt", + "some", + "other", + "val", + ]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 2); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["first", "overrides", "some", "other", "val"] + ); +} + +#[test] +fn aaos_pos_mult() { + // opts with multiple + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!([val] ... "some pos")) + .try_get_matches_from(vec!["", "some", "other", "value"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("val")); + assert_eq!(m.occurrences_of("val"), 3); + assert_eq!( + m.values_of("val").unwrap().collect::>(), + &["some", "other", "value"] + ); +} + +#[test] +fn aaos_option_use_delim_false() { + let m = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option").use_value_delimiter(false)) + .try_get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["one,two"] + ); +} + +#[test] +fn no_auto_help() { + let cmd = Command::new("myprog") + .setting(AppSettings::NoAutoHelp) + .subcommand(Command::new("foo")); + + let result = cmd.clone().try_get_matches_from("myprog --help".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("help")); + + let result = cmd.clone().try_get_matches_from("myprog -h".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("help")); + + let result = cmd.clone().try_get_matches_from("myprog help".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert_eq!(result.unwrap().subcommand_name(), Some("help")); +} + +#[test] +fn no_auto_version() { + let cmd = Command::new("myprog") + .version("3.0") + .setting(AppSettings::NoAutoVersion); + + let result = cmd + .clone() + .try_get_matches_from("myprog --version".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); + + let result = cmd.clone().try_get_matches_from("myprog -V".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); +} + +#[test] +fn no_auto_version_mut_arg() { + let cmd = Command::new("myprog") + .version("3.0") + .mut_arg("version", |v| v.help("custom help")) + .setting(AppSettings::NoAutoVersion); + + let result = cmd + .clone() + .try_get_matches_from("myprog --version".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); + + let result = cmd.clone().try_get_matches_from("myprog -V".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); +} + +#[test] +#[cfg(feature = "color")] +fn color_is_global() { + let mut cmd = Command::new("myprog") + .color(clap::ColorChoice::Never) + .subcommand(Command::new("foo")); + cmd.build(); + assert_eq!(cmd.get_color(), clap::ColorChoice::Never); + + let sub = cmd.get_subcommands().collect::>()[0]; + assert_eq!(sub.get_color(), clap::ColorChoice::Never); +} diff --git a/tests/builder/legacy/arg_aliases.rs b/tests/builder/legacy/arg_aliases.rs new file mode 100644 index 00000000000..09b5000c681 --- /dev/null +++ b/tests/builder/legacy/arg_aliases.rs @@ -0,0 +1,196 @@ +use super::utils; + +use clap::{arg, Arg, Command}; + +static SC_VISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag [aliases: v_flg, flag2, flg3] + -h, --help Print help information + -o, --opt [aliases: visible] + -V, --version Print version information +"; + +static SC_INVISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag + -h, --help Print help information + -o, --opt + -V, --version Print version information +"; + +#[test] +fn single_alias_of_option() { + let a = Command::new("single_alias") + .arg( + Arg::new("alias") + .long("alias") + .takes_value(true) + .help("single alias") + .alias("new-opt"), + ) + .try_get_matches_from(vec!["", "--new-opt", "cool"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("alias")); + assert_eq!(a.value_of("alias").unwrap(), "cool"); +} + +#[test] +fn multiple_aliases_of_option() { + let a = Command::new("multiple_aliases").arg( + Arg::new("aliases") + .long("aliases") + .takes_value(true) + .help("multiple aliases") + .aliases(&["alias1", "alias2", "alias3"]), + ); + let long = a + .clone() + .try_get_matches_from(vec!["", "--aliases", "value"]); + assert!(long.is_ok(), "{}", long.unwrap_err()); + let long = long.unwrap(); + + let als1 = a + .clone() + .try_get_matches_from(vec!["", "--alias1", "value"]); + assert!(als1.is_ok(), "{}", als1.unwrap_err()); + let als1 = als1.unwrap(); + + let als2 = a + .clone() + .try_get_matches_from(vec!["", "--alias2", "value"]); + assert!(als2.is_ok(), "{}", als2.unwrap_err()); + let als2 = als2.unwrap(); + + let als3 = a + .clone() + .try_get_matches_from(vec!["", "--alias3", "value"]); + assert!(als3.is_ok(), "{}", als3.unwrap_err()); + let als3 = als3.unwrap(); + + assert!(long.is_present("aliases")); + assert!(als1.is_present("aliases")); + assert!(als2.is_present("aliases")); + assert!(als3.is_present("aliases")); + assert_eq!(long.value_of("aliases").unwrap(), "value"); + assert_eq!(als1.value_of("aliases").unwrap(), "value"); + assert_eq!(als2.value_of("aliases").unwrap(), "value"); + assert_eq!(als3.value_of("aliases").unwrap(), "value"); +} + +#[test] +fn single_alias_of_flag() { + let a = Command::new("test") + .arg(Arg::new("flag").long("flag").alias("alias")) + .try_get_matches_from(vec!["", "--alias"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("flag")); +} + +#[test] +fn multiple_aliases_of_flag() { + let a = Command::new("test").arg(Arg::new("flag").long("flag").aliases(&[ + "invisible", + "set", + "of", + "cool", + "aliases", + ])); + + let flag = a.clone().try_get_matches_from(vec!["", "--flag"]); + assert!(flag.is_ok(), "{}", flag.unwrap_err()); + let flag = flag.unwrap(); + + let inv = a.clone().try_get_matches_from(vec!["", "--invisible"]); + assert!(inv.is_ok(), "{}", inv.unwrap_err()); + let inv = inv.unwrap(); + + let cool = a.clone().try_get_matches_from(vec!["", "--cool"]); + assert!(cool.is_ok(), "{}", cool.unwrap_err()); + let cool = cool.unwrap(); + + let als = a.clone().try_get_matches_from(vec!["", "--aliases"]); + assert!(als.is_ok(), "{}", als.unwrap_err()); + let als = als.unwrap(); + + assert!(flag.is_present("flag")); + assert!(inv.is_present("flag")); + assert!(cool.is_present("flag")); + assert!(als.is_present("flag")); +} + +#[test] +fn alias_on_a_subcommand_option() { + let m = Command::new("test") + .subcommand( + Command::new("some").arg( + Arg::new("test") + .short('t') + .long("test") + .takes_value(true) + .alias("opt") + .help("testing testing"), + ), + ) + .arg(Arg::new("other").long("other").aliases(&["o1", "o2", "o3"])) + .try_get_matches_from(vec!["test", "some", "--opt", "awesome"]) + .unwrap(); + + assert!(m.subcommand_matches("some").is_some()); + let sub_m = m.subcommand_matches("some").unwrap(); + assert!(sub_m.is_present("test")); + assert_eq!(sub_m.value_of("test").unwrap(), "awesome"); +} + +#[test] +fn invisible_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .aliases(&["invisible", "als1", "more"]), + ) + .arg(arg!(-f - -flag).aliases(&["unseeable", "flg1", "anyway"])), + ); + utils::assert_output(cmd, "ct test --help", SC_INVISIBLE_ALIAS_HELP, false); +} + +#[test] +fn visible_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .alias("invisible") + .visible_alias("visible"), + ) + .arg( + Arg::new("flg") + .long("flag") + .short('f') + .visible_aliases(&["v_flg", "flag2", "flg3"]), + ), + ); + utils::assert_output(cmd, "ct test --help", SC_VISIBLE_ALIAS_HELP, false); +} diff --git a/tests/builder/legacy/arg_aliases_short.rs b/tests/builder/legacy/arg_aliases_short.rs new file mode 100644 index 00000000000..f5f712847cc --- /dev/null +++ b/tests/builder/legacy/arg_aliases_short.rs @@ -0,0 +1,193 @@ +use super::utils; + +use clap::{arg, Arg, Command}; + +static SC_VISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag [aliases: flag1] [short aliases: a, b, 🦆] + -h, --help Print help information + -o, --opt [short aliases: v] + -V, --version Print version information +"; + +static SC_INVISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag + -h, --help Print help information + -o, --opt + -V, --version Print version information +"; + +#[test] +fn single_short_alias_of_option() { + let a = Command::new("single_alias") + .arg( + Arg::new("alias") + .long("alias") + .takes_value(true) + .help("single short alias") + .short_alias('a'), + ) + .try_get_matches_from(vec!["", "-a", "cool"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("alias")); + assert_eq!(a.value_of("alias").unwrap(), "cool"); +} + +#[test] +fn multiple_short_aliases_of_option() { + let a = Command::new("multiple_aliases").arg( + Arg::new("aliases") + .long("aliases") + .takes_value(true) + .help("multiple aliases") + .short_aliases(&['1', '2', '3']), + ); + let long = a + .clone() + .try_get_matches_from(vec!["", "--aliases", "value"]); + assert!(long.is_ok(), "{}", long.unwrap_err()); + let long = long.unwrap(); + + let als1 = a.clone().try_get_matches_from(vec!["", "-1", "value"]); + assert!(als1.is_ok(), "{}", als1.unwrap_err()); + let als1 = als1.unwrap(); + + let als2 = a.clone().try_get_matches_from(vec!["", "-2", "value"]); + assert!(als2.is_ok(), "{}", als2.unwrap_err()); + let als2 = als2.unwrap(); + + let als3 = a.clone().try_get_matches_from(vec!["", "-3", "value"]); + assert!(als3.is_ok(), "{}", als3.unwrap_err()); + let als3 = als3.unwrap(); + + assert!(long.is_present("aliases")); + assert!(als1.is_present("aliases")); + assert!(als2.is_present("aliases")); + assert!(als3.is_present("aliases")); + assert_eq!(long.value_of("aliases").unwrap(), "value"); + assert_eq!(als1.value_of("aliases").unwrap(), "value"); + assert_eq!(als2.value_of("aliases").unwrap(), "value"); + assert_eq!(als3.value_of("aliases").unwrap(), "value"); +} + +#[test] +fn single_short_alias_of_flag() { + let a = Command::new("test") + .arg(Arg::new("flag").long("flag").short_alias('f')) + .try_get_matches_from(vec!["", "-f"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("flag")); +} + +#[test] +fn multiple_short_aliases_of_flag() { + let a = Command::new("test").arg( + Arg::new("flag") + .long("flag") + .short_aliases(&['a', 'b', 'c', 'd', 'e']), + ); + + let flag = a.clone().try_get_matches_from(vec!["", "--flag"]); + assert!(flag.is_ok(), "{}", flag.unwrap_err()); + let flag = flag.unwrap(); + + let als1 = a.clone().try_get_matches_from(vec!["", "-a"]); + assert!(als1.is_ok(), "{}", als1.unwrap_err()); + let als1 = als1.unwrap(); + + let als2 = a.clone().try_get_matches_from(vec!["", "-b"]); + assert!(als2.is_ok(), "{}", als2.unwrap_err()); + let als2 = als2.unwrap(); + + let als3 = a.clone().try_get_matches_from(vec!["", "-c"]); + assert!(als3.is_ok(), "{}", als3.unwrap_err()); + let als3 = als3.unwrap(); + + assert!(flag.is_present("flag")); + assert!(als1.is_present("flag")); + assert!(als2.is_present("flag")); + assert!(als3.is_present("flag")); +} + +#[test] +fn short_alias_on_a_subcommand_option() { + let m = Command::new("test") + .subcommand( + Command::new("some").arg( + Arg::new("test") + .short('t') + .long("test") + .takes_value(true) + .short_alias('o') + .help("testing testing"), + ), + ) + .arg( + Arg::new("other") + .long("other") + .short_aliases(&['1', '2', '3']), + ) + .try_get_matches_from(vec!["test", "some", "-o", "awesome"]) + .unwrap(); + + assert!(m.subcommand_matches("some").is_some()); + let sub_m = m.subcommand_matches("some").unwrap(); + assert!(sub_m.is_present("test")); + assert_eq!(sub_m.value_of("test").unwrap(), "awesome"); +} + +#[test] +fn invisible_short_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .short_aliases(&['a', 'b', 'c']), + ) + .arg(arg!(-f - -flag).short_aliases(&['x', 'y', 'z'])), + ); + utils::assert_output(cmd, "ct test --help", SC_INVISIBLE_ALIAS_HELP, false); +} + +#[test] +fn visible_short_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .short_alias('i') + .visible_short_alias('v'), + ) + .arg( + Arg::new("flg") + .long("flag") + .short('f') + .visible_alias("flag1") + .visible_short_aliases(&['a', 'b', '🦆']), + ), + ); + utils::assert_output(cmd, "ct test --help", SC_VISIBLE_ALIAS_HELP, false); +} diff --git a/tests/builder/legacy/arg_matcher_assertions.rs b/tests/builder/legacy/arg_matcher_assertions.rs new file mode 100644 index 00000000000..e2c2378e8f0 --- /dev/null +++ b/tests/builder/legacy/arg_matcher_assertions.rs @@ -0,0 +1,41 @@ +#[cfg(debug_assertions)] +use clap::{Arg, Command}; + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "`f` is not a name of an argument or a group."] +fn arg_matches_if_present_wrong_arg() { + let m = Command::new("test") + .arg(Arg::new("flag").short('f')) + .try_get_matches_from(&["test", "-f"]) + .unwrap(); + + assert!(m.is_present("flag")); + m.is_present("f"); +} + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "`o` is not a name of an argument or a group."] +fn arg_matches_value_of_wrong_arg() { + let m = Command::new("test") + .arg(Arg::new("opt").short('o').takes_value(true)) + .try_get_matches_from(&["test", "-o", "val"]) + .unwrap(); + + assert_eq!(m.value_of("opt"), Some("val")); + m.value_of("o"); +} + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "`seed` is not a name of a subcommand."] +fn arg_matches_subcommand_matches_wrong_sub() { + let m = Command::new("test") + .subcommand(Command::new("speed")) + .try_get_matches_from(&["test", "speed"]) + .unwrap(); + + assert!(m.subcommand_matches("speed").is_some()); + m.subcommand_matches("seed"); +} diff --git a/tests/builder/legacy/arg_settings.rs b/tests/builder/legacy/arg_settings.rs new file mode 100644 index 00000000000..d4e900ab92c --- /dev/null +++ b/tests/builder/legacy/arg_settings.rs @@ -0,0 +1,44 @@ +#![allow(deprecated)] +use clap::{Arg, ArgSettings}; + +#[test] +fn setting() { + let m = Arg::new("setting").setting(ArgSettings::Required); + assert!(m.is_required_set()); +} + +#[test] +fn unset_setting() { + let m = Arg::new("unset_setting").setting(ArgSettings::Required); + assert!(m.is_required_set()); + + let m = m.unset_setting(ArgSettings::Required); + assert!(!m.is_required_set(), "{:#?}", m); +} + +#[test] +fn setting_bitor() { + let m = Arg::new("setting_bitor") + .setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last); + + assert!(m.is_required_set()); + assert!(m.is_hide_set()); + assert!(m.is_last_set()); +} + +#[test] +fn unset_setting_bitor() { + let m = Arg::new("unset_setting_bitor") + .setting(ArgSettings::Required) + .setting(ArgSettings::Hidden) + .setting(ArgSettings::Last); + + assert!(m.is_required_set()); + assert!(m.is_hide_set()); + assert!(m.is_last_set()); + + let m = m.unset_setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last); + assert!(!m.is_required_set(), "{:#?}", m); + assert!(!m.is_hide_set(), "{:#?}", m); + assert!(!m.is_last_set(), "{:#?}", m); +} diff --git a/tests/builder/legacy/borrowed.rs b/tests/builder/legacy/borrowed.rs new file mode 100644 index 00000000000..4b4cd39a2d2 --- /dev/null +++ b/tests/builder/legacy/borrowed.rs @@ -0,0 +1,17 @@ +use clap::{Arg, Command}; + +#[test] +fn borrowed_args() { + let arg = Arg::new("some").short('s').long("some").help("other help"); + let arg2 = Arg::new("some2") + .short('S') + .long("some-thing") + .help("other help"); + let result = Command::new("sub_command_negate") + .arg(Arg::new("test").index(1)) + .arg(&arg) + .arg(&arg2) + .subcommand(Command::new("sub1").arg(&arg)) + .try_get_matches_from(vec!["prog"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); +} diff --git a/tests/builder/legacy/cargo.rs b/tests/builder/legacy/cargo.rs new file mode 100644 index 00000000000..d2eb6822fa6 --- /dev/null +++ b/tests/builder/legacy/cargo.rs @@ -0,0 +1,80 @@ +#![cfg(feature = "cargo")] + +use clap::{ + crate_authors, crate_description, crate_name, crate_version, error::ErrorKind, Command, +}; + +static DESCRIPTION_ONLY: &str = "prog 1 +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + prog + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +static AUTHORS_ONLY: &str = "prog 1 + + +USAGE: + prog + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +#[test] +fn crate_version() { + let res = Command::new("prog") + .version(crate_version!()) + .try_get_matches_from(vec!["prog", "--version"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayVersion); + assert_eq!( + err.to_string(), + format!("prog {}\n", env!("CARGO_PKG_VERSION")) + ); +} + +#[test] +fn crate_description() { + let res = Command::new("prog") + .version("1") + .about(crate_description!()) + .try_get_matches_from(vec!["prog", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!(err.to_string(), DESCRIPTION_ONLY); +} + +#[test] +fn crate_authors() { + let res = Command::new("prog") + .version("1") + .author(crate_authors!()) + .try_get_matches_from(vec!["prog", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!(err.to_string(), AUTHORS_ONLY); +} + +#[test] +fn crate_name() { + let res = Command::new(crate_name!()) + .version("3.0") + .try_get_matches_from(vec!["clap", "--version"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayVersion); + assert_eq!(err.to_string(), "clap 3.0\n"); +} diff --git a/tests/builder/legacy/command.rs b/tests/builder/legacy/command.rs new file mode 100644 index 00000000000..9f31d7a9d6a --- /dev/null +++ b/tests/builder/legacy/command.rs @@ -0,0 +1,27 @@ +#![cfg(feature = "cargo")] + +use clap::{command, error::ErrorKind}; + +static EVERYTHING: &str = "clap {{version}} +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + clap + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +#[test] +fn command() { + let res = command!().try_get_matches_from(vec!["clap", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!( + err.to_string(), + EVERYTHING.replace("{{version}}", env!("CARGO_PKG_VERSION")) + ); +} diff --git a/tests/builder/legacy/conflicts.rs b/tests/builder/legacy/conflicts.rs new file mode 100644 index 00000000000..8a728e89429 --- /dev/null +++ b/tests/builder/legacy/conflicts.rs @@ -0,0 +1,509 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; + +static CONFLICT_ERR: &str = "error: The argument '--flag' cannot be used with '-F' + +USAGE: + clap-test --flag --long-option-2 + +For more information try --help +"; + +static CONFLICT_ERR_REV: &str = "error: The argument '-F' cannot be used with '--flag' + +USAGE: + clap-test -F --long-option-2 + +For more information try --help +"; + +static CONFLICT_ERR_THREE: &str = "error: The argument '--one' cannot be used with: + --two + --three + +USAGE: + three_conflicting_arguments --one + +For more information try --help +"; + +#[test] +fn flag_conflict() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("other")) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-f", "-o"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn flag_conflict_2() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("other")) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-o", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn flag_conflict_with_all() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with_all(&["other"])) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-o", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn flag_conflict_with_everything() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").exclusive(true)) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-o", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn arg_conflicts_with_group() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("gr")) + .group(ArgGroup::new("gr").arg("some").arg("other")) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--flag"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn arg_conflicts_with_group_with_multiple_sources() { + let mut cmd = clap::Command::new("group_conflict") + .arg(clap::arg!(-f --flag "some flag").conflicts_with("gr")) + .group(clap::ArgGroup::new("gr").multiple(true)) + .arg( + clap::arg!(--some "some arg") + .required(false) + .group("gr"), + ) + .arg( + clap::arg!(--other "other arg") + .required(false) + .default_value("1000") + .group("gr"), + ); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some", "usb1"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some", "usb1", "--other", "40"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some", "usb1"]); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn group_conflicts_with_arg() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag")) + .group( + ArgGroup::new("gr") + .arg("some") + .arg("other") + .conflicts_with("flag"), + ) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--flag"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn arg_conflicts_with_required_group() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("gr")) + .group(ArgGroup::new("gr").required(true).arg("some").arg("other")) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn required_group_conflicts_with_arg() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag")) + .group( + ArgGroup::new("gr") + .required(true) + .arg("some") + .arg("other") + .conflicts_with("flag"), + ) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn conflict_output() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 fa --flag --long-option-2 val2 -F", + CONFLICT_ERR, + true, + ); +} + +#[test] +fn conflict_output_rev() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 fa -F --long-option-2 val2 --flag", + CONFLICT_ERR_REV, + true, + ); +} + +#[test] +fn conflict_output_with_required() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 --flag --long-option-2 val2 -F", + CONFLICT_ERR, + true, + ); +} + +#[test] +fn conflict_output_rev_with_required() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 -F --long-option-2 val2 --flag", + CONFLICT_ERR_REV, + true, + ); +} + +#[test] +fn conflict_output_three_conflicting() { + let cmd = Command::new("three_conflicting_arguments") + .arg( + Arg::new("one") + .long("one") + .conflicts_with_all(&["two", "three"]), + ) + .arg( + Arg::new("two") + .long("two") + .conflicts_with_all(&["one", "three"]), + ) + .arg( + Arg::new("three") + .long("three") + .conflicts_with_all(&["one", "two"]), + ); + utils::assert_output( + cmd, + "three_conflicting_arguments --one --two --three", + CONFLICT_ERR_THREE, + true, + ); +} + +#[test] +fn two_conflicting_arguments() { + let a = Command::new("two_conflicting_arguments") + .arg( + Arg::new("develop") + .long("develop") + .conflicts_with("production"), + ) + .arg( + Arg::new("production") + .long("production") + .conflicts_with("develop"), + ) + .try_get_matches_from(vec!["", "--develop", "--production"]); + + assert!(a.is_err()); + let a = a.unwrap_err(); + assert!( + a.to_string() + .contains("The argument \'--develop\' cannot be used with \'--production\'"), + "{}", + a + ); +} + +#[test] +fn three_conflicting_arguments() { + let a = Command::new("three_conflicting_arguments") + .arg( + Arg::new("one") + .long("one") + .conflicts_with_all(&["two", "three"]), + ) + .arg( + Arg::new("two") + .long("two") + .conflicts_with_all(&["one", "three"]), + ) + .arg( + Arg::new("three") + .long("three") + .conflicts_with_all(&["one", "two"]), + ) + .try_get_matches_from(vec!["", "--one", "--two", "--three"]); + + assert!(a.is_err()); + let a = a.unwrap_err(); + assert!( + a.to_string() + .contains("The argument \'--one\' cannot be used with:"), + "{}", + a + ); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument 'config' cannot conflict with itself"] +fn self_conflicting_arg() { + let _ = Command::new("prog") + .arg(Arg::new("config").long("config").conflicts_with("config")) + .try_get_matches_from(vec!["", "--config"]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument or group 'extra' specified in 'conflicts_with*' for 'config' does not exist"] +fn conflicts_with_invalid_arg() { + let _ = Command::new("prog") + .arg(Arg::new("config").long("config").conflicts_with("extra")) + .try_get_matches_from(vec!["", "--config"]); +} + +#[test] +fn conflict_with_unused_default() { + let result = Command::new("conflict") + .arg( + arg!(-o --opt "some opt") + .required(false) + .default_value("default"), + ) + .arg(arg!(-f --flag "some flag").conflicts_with("opt")) + .try_get_matches_from(vec!["myprog", "-f"]); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn conflicts_with_alongside_default() { + let result = Command::new("conflict") + .arg( + arg!(-o --opt "some opt") + .default_value("default") + .required(false) + .conflicts_with("flag"), + ) + .arg(arg!(-f --flag "some flag")) + .try_get_matches_from(vec!["myprog", "-f"]); + + assert!( + result.is_ok(), + "conflicts_with should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn group_in_conflicts_with() { + let result = Command::new("conflict") + .arg( + Arg::new("opt") + .long("opt") + .default_value("default") + .group("one"), + ) + .arg(Arg::new("flag").long("flag").conflicts_with("one")) + .try_get_matches_from(vec!["myprog", "--flag"]); + + assert!( + result.is_ok(), + "conflicts_with on an arg group should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn group_conflicts_with_default_value() { + let result = Command::new("conflict") + .arg( + Arg::new("opt") + .long("opt") + .default_value("default") + .group("one"), + ) + .arg(Arg::new("flag").long("flag").group("one")) + .try_get_matches_from(vec!["myprog", "--flag"]); + + assert!( + result.is_ok(), + "arg group count should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn group_conflicts_with_default_arg() { + let result = Command::new("conflict") + .arg(Arg::new("opt").long("opt").default_value("default")) + .arg(Arg::new("flag").long("flag").group("one")) + .group(ArgGroup::new("one").conflicts_with("opt")) + .try_get_matches_from(vec!["myprog", "--flag"]); + + assert!( + result.is_ok(), + "arg group conflicts_with should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn exclusive_with_required() { + let cmd = Command::new("bug") + .arg(Arg::new("test").long("test").exclusive(true)) + .arg(Arg::new("input").takes_value(true).required(true)); + + cmd.clone() + .try_get_matches_from(["bug", "--test", "required"]) + .unwrap_err(); + + cmd.clone() + .try_get_matches_from(["bug", "required"]) + .unwrap(); + + cmd.clone().try_get_matches_from(["bug", "--test"]).unwrap(); +} diff --git a/tests/builder/legacy/default_missing_vals.rs b/tests/builder/legacy/default_missing_vals.rs new file mode 100644 index 00000000000..52ed95226fa --- /dev/null +++ b/tests/builder/legacy/default_missing_vals.rs @@ -0,0 +1,187 @@ +use clap::{arg, Arg, ArgMatches, Command}; + +#[test] +fn opt_missing() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .min_values(0) + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), "auto"); + assert_eq!(m.occurrences_of("color"), 0); +} + +#[test] +fn opt_present_with_missing_value() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .min_values(0) + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec!["", "--color"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), "always"); + assert_eq!(m.occurrences_of("color"), 1); +} + +#[test] +fn opt_present_with_value() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .min_values(0) + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec!["", "--color=never"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), "never"); + assert_eq!(m.occurrences_of("color"), 1); +} + +#[test] +fn opt_present_with_empty_value() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec!["", "--color="]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), ""); + assert_eq!(m.occurrences_of("color"), 1); +} + +//## `default_value`/`default_missing_value` non-interaction checks + +#[test] +fn opt_default() { + // assert no change to usual argument handling when adding default_missing_value() + let r = Command::new("cmd") + .arg( + arg!(o: -o [opt] "some opt") + .default_value("default") + .default_missing_value("default_missing"), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), "default"); +} + +#[test] +fn opt_default_user_override() { + // assert no change to usual argument handling when adding default_missing_value() + let r = Command::new("cmd") + .arg( + arg!(o: -o [opt] "some opt") + .default_value("default") + .default_missing_value("default_missing"), + ) + .try_get_matches_from(vec!["", "-o=value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), "value"); +} + +#[test] +#[allow(clippy::bool_assert_comparison)] +fn default_missing_value_flag_value() { + let cmd = Command::new("test").arg( + Arg::new("flag") + .long("flag") + .takes_value(true) + .default_missing_value("true"), + ); + + fn flag_value(m: ArgMatches) -> bool { + match m.value_of("flag") { + None => false, + Some(x) => x.parse().expect("non boolean value"), + } + } + + assert_eq!( + flag_value(cmd.clone().try_get_matches_from(&["test"]).unwrap()), + false + ); + assert_eq!( + flag_value( + cmd.clone() + .try_get_matches_from(&["test", "--flag"]) + .unwrap() + ), + true + ); + assert_eq!( + flag_value( + cmd.clone() + .try_get_matches_from(&["test", "--flag=true"]) + .unwrap() + ), + true + ); + assert_eq!( + flag_value( + cmd.clone() + .try_get_matches_from(&["test", "--flag=false"]) + .unwrap() + ), + false + ); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_missing_value=value doesn't match possible values"] +fn default_missing_values_are_possible_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .possible_values(["one", "two"]) + .default_missing_value("value"), + ) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_missing_value=value failed validation: invalid digit found in string"] +fn default_missing_values_are_valid() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .default_missing_value("value"), + ) + .try_get_matches(); +} diff --git a/tests/builder/legacy/default_vals.rs b/tests/builder/legacy/default_vals.rs new file mode 100644 index 00000000000..0bba658eac4 --- /dev/null +++ b/tests/builder/legacy/default_vals.rs @@ -0,0 +1,731 @@ +use super::utils; +use clap::{arg, error::ErrorKind, Arg, Command}; + +#[test] +fn opts() { + let r = Command::new("df") + .arg( + arg!(o: -o "some opt") + .required(false) + .default_value("default"), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), "default"); +} + +#[test] +fn opt_without_value_fail() { + let r = Command::new("df") + .arg( + arg!(o: -o "some opt") + .required(false) + .default_value("default") + .forbid_empty_values(true), + ) + .try_get_matches_from(vec!["", "-o"]); + assert!(r.is_err()); + let err = r.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::EmptyValue); + assert!(err + .to_string() + .contains("The argument '-o ' requires a value but none was supplied")); +} + +#[test] +fn opt_user_override() { + let r = Command::new("df") + .arg( + arg!(--opt "some arg") + .required(false) + .default_value("default"), + ) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.value_of("opt").unwrap(), "value"); +} + +#[test] +fn positionals() { + let r = Command::new("df") + .arg(arg!([arg] "some opt").default_value("default")) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn positional_user_override() { + let r = Command::new("df") + .arg(arg!([arg] "some arg").default_value("default")) + .try_get_matches_from(vec!["", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "value"); +} + +// OsStr Default Values + +#[test] +fn osstr_opts() { + use std::ffi::OsStr; + let expected = OsStr::new("default"); + + let r = Command::new("df") + .arg( + arg!(o: -o "some opt") + .required(false) + .default_value_os(expected), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), expected); +} + +#[test] +fn osstr_opt_user_override() { + use std::ffi::OsStr; + let default = OsStr::new("default"); + + let r = Command::new("df") + .arg( + arg!(--opt "some arg") + .required(false) + .default_value_os(default), + ) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.value_of("opt").unwrap(), "value"); +} + +#[test] +fn osstr_positionals() { + use std::ffi::OsStr; + let expected = OsStr::new("default"); + + let r = Command::new("df") + .arg(arg!([arg] "some opt").default_value_os(expected)) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), expected); +} + +#[test] +fn osstr_positional_user_override() { + use std::ffi::OsStr; + let default = OsStr::new("default"); + + let r = Command::new("df") + .arg(arg!([arg] "some arg").default_value_os(default)) + .try_get_matches_from(vec!["", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "value"); +} + +// --- Default if arg is present + +#[test] +fn default_if_arg_present_no_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(true)) + .arg(arg!([arg] "some arg").default_value_if("opt", None, Some("default"))) + .try_get_matches_from(vec!["", "--opt", "some"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_no_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", None, Some("default"))) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "first"); +} + +#[test] +fn default_if_arg_present_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec!["", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +// Conditional Default Values + +#[test] +fn default_if_arg_present_with_value_no_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), Some("default"))) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_with_value_no_default_fail() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), Some("default"))) + .try_get_matches_from(vec!["", "--opt", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert!(m.value_of("arg").is_none()); +} + +#[test] +fn default_if_arg_present_with_value_no_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("some"), Some("default"))) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_with_value_no_arg_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "first"); +} + +#[test] +fn default_if_arg_present_with_value_no_arg_with_default_fail() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "first"); +} + +#[test] +fn default_if_arg_present_with_value_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_with_value_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_value_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "value", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +// Unsetting the default + +#[test] +fn no_default_if_arg_present_with_value_no_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), None)) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); +} + +#[test] +fn no_default_if_arg_present_with_value_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("default") + .default_value_if("opt", Some("value"), None), + ) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert!(m.value_of("arg").is_none()); +} + +#[test] +fn no_default_if_arg_present_with_value_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("default") + .default_value_if("opt", Some("value"), None), + ) + .try_get_matches_from(vec!["", "--opt", "value", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn no_default_if_arg_present_no_arg_with_value_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("default") + .default_value_if("opt", Some("value"), None), + ) + .try_get_matches_from(vec!["", "--opt", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +// Multiple conditions + +#[test] +fn default_ifs_arg_present() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[ + ("opt", Some("some"), Some("default")), + ("flag", None, Some("flg")), + ]), + ) + .try_get_matches_from(vec!["", "--flag"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "flg"); +} + +#[test] +fn no_default_ifs_arg_present() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[("opt", Some("some"), Some("default")), ("flag", None, None)]), + ) + .try_get_matches_from(vec!["", "--flag"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert!(m.value_of("arg").is_none()); +} + +#[test] +fn default_ifs_arg_present_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[ + ("opt", Some("some"), Some("default")), + ("flag", None, Some("flg")), + ]), + ) + .try_get_matches_from(vec!["", "--flag", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "value"); +} + +#[test] +fn default_ifs_arg_present_order() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[ + ("opt", Some("some"), Some("default")), + ("flag", None, Some("flg")), + ]), + ) + .try_get_matches_from(vec!["", "--opt=some", "--flag"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +// Interaction with requires + +#[test] +fn conditional_reqs_pass() { + let m = Command::new("Test cmd") + .arg( + Arg::new("target") + .takes_value(true) + .default_value("file") + .long("target"), + ) + .arg( + Arg::new("input") + .takes_value(true) + .required(true) + .long("input"), + ) + .arg( + Arg::new("output") + .takes_value(true) + .required_if_eq("target", "file") + .long("output"), + ) + .try_get_matches_from(vec!["test", "--input", "some", "--output", "other"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + assert_eq!(m.value_of("output"), Some("other")); + assert_eq!(m.value_of("input"), Some("some")); +} + +#[test] +fn multiple_defaults() { + let r = Command::new("diff") + .arg( + Arg::new("files") + .long("files") + .number_of_values(2) + .allow_invalid_utf8(true) + .default_values(&["old", "new"]), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("files")); + assert_eq!(m.values_of_lossy("files").unwrap(), vec!["old", "new"]); +} + +#[test] +fn multiple_defaults_override() { + let r = Command::new("diff") + .arg( + Arg::new("files") + .long("files") + .number_of_values(2) + .allow_invalid_utf8(true) + .default_values(&["old", "new"]), + ) + .try_get_matches_from(vec!["", "--files", "other", "mine"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("files")); + assert_eq!(m.values_of_lossy("files").unwrap(), vec!["other", "mine"]); +} + +#[test] +fn default_vals_donnot_show_in_smart_usage() { + let cmd = Command::new("bug") + .arg( + Arg::new("foo") + .long("config") + .takes_value(true) + .default_value("bar"), + ) + .arg(Arg::new("input").required(true)); + + utils::assert_output( + cmd, + "bug", + "error: The following required arguments were not provided: + + +USAGE: + bug [OPTIONS] + +For more information try --help +", + true, + ); +} + +#[test] +fn issue_1050_num_vals_and_defaults() { + let res = Command::new("hello") + .arg( + Arg::new("exit-code") + .long("exit-code") + .takes_value(true) + .number_of_values(1) + .default_value("0"), + ) + .try_get_matches_from(vec!["hello", "--exit-code=1"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert_eq!(m.value_of("exit-code"), Some("1")); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group 'group' is required but all of it's arguments have a default value."] +fn required_groups_with_default_values() { + use clap::{Arg, ArgGroup, Command}; + + let _ = Command::new("test") + .arg(Arg::new("arg").default_value("value")) + .group(ArgGroup::new("group").args(&["arg"]).required(true)) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument 'arg' is required and can't have a default value"] +fn required_args_with_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg(Arg::new("arg").required(true).default_value("value")) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_value=value doesn't match possible values"] +fn default_values_are_possible_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .possible_values(["one", "two"]) + .default_value("value"), + ) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_value=one failed validation: invalid digit found in string"] +fn invalid_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .default_value("one"), + ) + .try_get_matches(); +} + +#[test] +fn valid_delimited_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .use_value_delimiter(true) + .require_value_delimiter(true) + .default_value("1,2,3"), + ) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_value=one failed validation: invalid digit found in string"] +fn invalid_delimited_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .use_value_delimiter(true) + .require_value_delimiter(true) + .default_value("one,two"), + ) + .try_get_matches(); +} + +#[test] +fn with_value_delimiter() { + let cmd = Command::new("multiple_values").arg( + Arg::new("option") + .long("option") + .help("multiple options") + .value_delimiter(';') + .default_value("first;second"), + ); + + let matches = cmd.try_get_matches_from(vec![""]).unwrap(); + + assert_eq!( + matches.values_of("option").unwrap().collect::>(), + ["first", "second"] + ); +} + +#[test] +fn missing_with_value_delimiter() { + let cmd = Command::new("program").arg( + Arg::new("option") + .long("option") + .value_delimiter(';') + .default_missing_values(&["value1;value2;value3", "value4;value5"]), + ); + + let matches = cmd + .try_get_matches_from(vec!["program", "--option"]) + .unwrap(); + + assert_eq!( + matches.values_of("option").unwrap().collect::>(), + ["value1", "value2", "value3", "value4", "value5"] + ); +} diff --git a/tests/builder/legacy/delimiters.rs b/tests/builder/legacy/delimiters.rs new file mode 100644 index 00000000000..2ead7a3d333 --- /dev/null +++ b/tests/builder/legacy/delimiters.rs @@ -0,0 +1,113 @@ +use clap::{Arg, Command}; + +#[test] +fn opt_default_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").long("option").takes_value(true)) + .try_get_matches_from(vec!["", "--option", "val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_eq_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").long("option").takes_value(true)) + .try_get_matches_from(vec!["", "--option=val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_eq_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").short('o').takes_value(true)) + .try_get_matches_from(vec!["", "-o=val1,val2,val3"]); + + assert!(m.is_ok(), "{:?}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_default_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").short('o').takes_value(true)) + .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); + + assert!(m.is_ok(), "{:?}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_no_space_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").short('o').takes_value(true)) + .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_no_space_mult_no_delim() { + let m = Command::new("no_delim") + .arg( + Arg::new("option") + .short('o') + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_eq_mult_def_delim() { + let m = Command::new("no_delim") + .arg( + Arg::new("option") + .long("opt") + .takes_value(true) + .multiple_values(true) + .use_value_delimiter(true), + ) + .try_get_matches_from(vec!["", "--opt=val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!( + m.values_of("option").unwrap().collect::>(), + &["val1", "val2", "val3"] + ); +} diff --git a/tests/builder/legacy/derive_order.rs b/tests/builder/legacy/derive_order.rs new file mode 100644 index 00000000000..5277806480f --- /dev/null +++ b/tests/builder/legacy/derive_order.rs @@ -0,0 +1,293 @@ +use super::utils; + +use std::str; + +use clap::{AppSettings, Arg, Command}; + +static NO_DERIVE_ORDER: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_a second flag + --flag_b first flag + -h, --help Print help information + --option_a second option + --option_b first option + -V, --version Print version information +"; + +static UNIFIED_HELP_AND_DERIVE: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_b first flag + --option_b first option + --flag_a second flag + --option_a second option + -h, --help Print help information + -V, --version Print version information +"; + +static UNIFIED_DERIVE_SC_PROP: &str = "test-sub 1.2 + +USAGE: + test sub [OPTIONS] + +OPTIONS: + --flag_b first flag + --option_b first option + --flag_a second flag + --option_a second option + -h, --help Print help information + -V, --version Print version information +"; + +static UNIFIED_DERIVE_SC_PROP_EXPLICIT_ORDER: &str = "test-sub 1.2 + +USAGE: + test sub [OPTIONS] + +OPTIONS: + --flag_a second flag + --flag_b first flag + --option_b first option + --option_a second option + -h, --help Print help information + -V, --version Print version information +"; + +static PREFER_USER_HELP_DERIVE_ORDER: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + -h, --help Print help message + --flag_b first flag + --flag_a second flag + -V, --version Print version information +"; + +static PREFER_USER_HELP_SUBCMD_DERIVE_ORDER: &str = "test-sub 1.2 + +USAGE: + test sub [OPTIONS] + +OPTIONS: + -h, --help Print help message + --flag_b first flag + --flag_a second flag + -V, --version Print version information +"; + +#[test] +fn no_derive_order() { + let cmd = Command::new("test").version("1.2").args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a").long("flag_a").help("second flag"), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]); + + utils::assert_output(cmd, "test --help", NO_DERIVE_ORDER, false); +} + +#[test] +fn derive_order() { + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a").long("flag_a").help("second flag"), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]); + + utils::assert_output(cmd, "test --help", UNIFIED_HELP_AND_DERIVE, false); +} + +#[test] +fn derive_order_next_order() { + static HELP: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_b first flag + --option_b first option + -h, --help Print help information + -V, --version Print version information + --flag_a second flag + --option_a second option +"; + + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .next_display_order(10000) + .arg(Arg::new("flag_a").long("flag_a").help("second flag")) + .arg( + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ) + .next_display_order(10) + .arg(Arg::new("flag_b").long("flag_b").help("first flag")) + .arg( + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + ); + + utils::assert_output(cmd, "test --help", HELP, false); +} + +#[test] +fn derive_order_no_next_order() { + static HELP: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_a first flag + --flag_b second flag + -h, --help Print help information + --option_a first option + --option_b second option + -V, --version Print version information +"; + + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .next_display_order(None) + .arg(Arg::new("flag_a").long("flag_a").help("first flag")) + .arg( + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("first option"), + ) + .arg(Arg::new("flag_b").long("flag_b").help("second flag")) + .arg( + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("second option"), + ); + + utils::assert_output(cmd, "test --help", HELP, false); +} + +#[test] +fn derive_order_subcommand_propagate() { + let cmd = Command::new("test") + .global_setting(AppSettings::DeriveDisplayOrder) + .subcommand( + Command::new("sub").version("1.2").args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a").long("flag_a").help("second flag"), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]), + ); + + utils::assert_output(cmd, "test sub --help", UNIFIED_DERIVE_SC_PROP, false); +} + +#[test] +fn derive_order_subcommand_propagate_with_explicit_display_order() { + let cmd = Command::new("test") + .global_setting(AppSettings::DeriveDisplayOrder) + .subcommand( + Command::new("sub").version("1.2").args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a") + .long("flag_a") + .help("second flag") + .display_order(0), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]), + ); + + utils::assert_output( + cmd, + "test sub --help", + UNIFIED_DERIVE_SC_PROP_EXPLICIT_ORDER, + false, + ); +} + +#[test] +fn prefer_user_help_with_derive_order() { + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .args(&[ + Arg::new("help") + .long("help") + .short('h') + .help("Print help message"), + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("flag_a").long("flag_a").help("second flag"), + ]); + + utils::assert_output(cmd, "test --help", PREFER_USER_HELP_DERIVE_ORDER, false); +} + +#[test] +fn prefer_user_help_in_subcommand_with_derive_order() { + let cmd = Command::new("test") + .global_setting(AppSettings::DeriveDisplayOrder) + .subcommand( + Command::new("sub").version("1.2").args(&[ + Arg::new("help") + .long("help") + .short('h') + .help("Print help message"), + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("flag_a").long("flag_a").help("second flag"), + ]), + ); + + utils::assert_output( + cmd, + "test sub --help", + PREFER_USER_HELP_SUBCMD_DERIVE_ORDER, + false, + ); +} diff --git a/tests/builder/legacy/display_order.rs b/tests/builder/legacy/display_order.rs new file mode 100644 index 00000000000..139682d80e3 --- /dev/null +++ b/tests/builder/legacy/display_order.rs @@ -0,0 +1,26 @@ +use super::utils; + +use clap::Command; + +#[test] +fn very_large_display_order() { + let cmd = Command::new("test").subcommand(Command::new("sub").display_order(usize::MAX)); + + utils::assert_output( + cmd, + "test --help", + "test + +USAGE: + test [SUBCOMMAND] + +OPTIONS: + -h, --help Print help information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + sub +", + false, + ); +} diff --git a/tests/builder/legacy/double_require.rs b/tests/builder/legacy/double_require.rs new file mode 100644 index 00000000000..13470cff6c1 --- /dev/null +++ b/tests/builder/legacy/double_require.rs @@ -0,0 +1,88 @@ +use clap::{error::ErrorKind, Arg, Command}; + +static HELP: &str = "prog + +USAGE: + prog [OPTIONS] + +OPTIONS: + -a + -b + -c + -h, --help Print help information +"; + +static ONLY_B_ERROR: &str = "error: The following required arguments were not provided: + -c + +USAGE: + prog -b -c + +For more information try --help +"; + +static ONLY_C_ERROR: &str = "error: The following required arguments were not provided: + -b + +USAGE: + prog -c -b + +For more information try --help +"; + +fn cmd() -> Command<'static> { + Command::new("prog") + .arg( + Arg::new("a") + .short('a') + .required_unless_present_any(&["b", "c"]) + .conflicts_with_all(&["b", "c"]), + ) + .arg( + Arg::new("b") + .short('b') + .required_unless_present("a") + .requires("c"), + ) + .arg( + Arg::new("c") + .short('c') + .required_unless_present("a") + .requires("b"), + ) +} + +#[test] +fn valid_cases() { + let res = cmd().try_get_matches_from(vec!["", "-a"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = cmd().clone().try_get_matches_from(vec!["", "-b", "-c"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = cmd().try_get_matches_from(vec!["", "-c", "-b"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); +} + +#[test] +fn help_text() { + let res = cmd().try_get_matches_from(vec!["prog", "--help"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + println!("{}", err); + assert_eq!(err.to_string(), HELP); +} + +#[test] +fn no_duplicate_error() { + let res = cmd().try_get_matches_from(vec!["", "-b"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + assert_eq!(err.to_string(), ONLY_B_ERROR); + + let res = cmd().try_get_matches_from(vec!["", "-c"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + assert_eq!(err.to_string(), ONLY_C_ERROR); +} diff --git a/tests/builder/legacy/empty_values.rs b/tests/builder/legacy/empty_values.rs new file mode 100644 index 00000000000..46def9899b4 --- /dev/null +++ b/tests/builder/legacy/empty_values.rs @@ -0,0 +1,128 @@ +use super::utils; + +use clap::{error::ErrorKind, Arg, Command}; + +#[test] +fn empty_values() { + let m = Command::new("config") + .arg(Arg::new("config").long("config").takes_value(true)) + .try_get_matches_from(&["config", "--config", ""]) + .unwrap(); + assert_eq!(m.value_of("config"), Some("")); +} + +#[test] +fn empty_values_with_equals() { + let m = Command::new("config") + .arg(Arg::new("config").long("config").takes_value(true)) + .try_get_matches_from(&["config", "--config="]) + .unwrap(); + assert_eq!(m.value_of("config"), Some("")); + + let m = Command::new("config") + .arg(Arg::new("config").short('c').takes_value(true)) + .try_get_matches_from(&["config", "-c="]) + .unwrap(); + assert_eq!(m.value_of("config"), Some("")) +} + +#[test] +fn no_empty_values() { + let m = Command::new("config") + .arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "--config", ""]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); + + let m = Command::new("config") + .arg( + Arg::new("config") + .short('c') + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "-c", ""]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue) +} + +#[test] +fn no_empty_values_with_equals() { + let m = Command::new("config") + .arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "--config="]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); + + let m = Command::new("config") + .arg( + Arg::new("config") + .short('c') + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "-c="]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); +} + +#[test] +fn no_empty_values_without_equals() { + let m = Command::new("config") + .arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "--config"]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); + + let m = Command::new("config") + .arg( + Arg::new("config") + .short('c') + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "-c"]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue) +} + +#[test] +fn no_empty_values_without_equals_but_requires_equals() { + let cmd = Command::new("config").arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true) + .require_equals(true), + ); + let m = cmd.clone().try_get_matches_from(&["config", "--config"]); + // Should error on no equals rather than empty value. + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::NoEquals); + + static NO_EUQALS_ERROR: &str = + "error: Equal sign is needed when assigning values to '--config='. + +USAGE: + config [OPTIONS] + +For more information try --help +"; + + utils::assert_output(cmd, "config --config", NO_EUQALS_ERROR, true); +} diff --git a/tests/builder/legacy/env.rs b/tests/builder/legacy/env.rs new file mode 100644 index 00000000000..e5416eef470 --- /dev/null +++ b/tests/builder/legacy/env.rs @@ -0,0 +1,352 @@ +#![cfg(feature = "env")] + +use std::env; +use std::ffi::OsStr; + +use clap::{arg, Arg, Command}; + +#[test] +fn env() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = Command::new("df") + .arg(arg!([arg] "some opt").env("CLP_TEST_ENV").takes_value(true)) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn env_bool_literal() { + env::set_var("CLP_TEST_FLAG_TRUE", "On"); + env::set_var("CLP_TEST_FLAG_FALSE", "nO"); + + let r = Command::new("df") + .arg(Arg::new("present").short('p').env("CLP_TEST_FLAG_TRUE")) + .arg(Arg::new("negated").short('n').env("CLP_TEST_FLAG_FALSE")) + .arg(Arg::new("absent").short('a').env("CLP_TEST_FLAG_ABSENT")) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("present")); + assert_eq!(m.occurrences_of("present"), 0); + assert_eq!(m.value_of("present"), None); + assert!(!m.is_present("negated")); + assert!(!m.is_present("absent")); +} + +#[test] +fn env_os() { + env::set_var("CLP_TEST_ENV_OS", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env_os(OsStr::new("CLP_TEST_ENV_OS")) + .takes_value(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn no_env() { + // All the other tests use the presence of the Environment variable... + // we need another variable just in case one of the others is running at the same time... + env::remove_var("CLP_TEST_ENV_NONE"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_NONE") + .takes_value(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg"), None); +} + +#[test] +fn no_env_no_takes_value() { + // All the other tests use the presence of the Environment variable... + // we need another variable just in case one of the others is running at the same time... + env::remove_var("CLP_TEST_ENV_NONE"); + + let r = Command::new("df") + .arg(arg!([arg] "some opt").env("CLP_TEST_ENV_NONE")) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg"), None); +} + +#[test] +fn with_default() { + env::set_var("CLP_TEST_ENV_WD", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_WD") + .takes_value(true) + .default_value("default"), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn opt_user_override() { + env::set_var("CLP_TEST_ENV_OR", "env"); + + let r = Command::new("df") + .arg( + arg!(--arg [FILE] "some arg") + .env("CLP_TEST_ENV_OR") + .takes_value(true), + ) + .try_get_matches_from(vec!["", "--arg", "opt"]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 1); + assert_eq!(m.value_of("arg").unwrap(), "opt"); + + // see https://github.com/clap-rs/clap/issues/1835 + let values: Vec<_> = m.values_of("arg").unwrap().collect(); + assert_eq!(values, vec!["opt"]); +} + +#[test] +fn positionals() { + env::set_var("CLP_TEST_ENV_P", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_P") + .takes_value(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn positionals_user_override() { + env::set_var("CLP_TEST_ENV_POR", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_POR") + .takes_value(true), + ) + .try_get_matches_from(vec!["", "opt"]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 1); + assert_eq!(m.value_of("arg").unwrap(), "opt"); + + // see https://github.com/clap-rs/clap/issues/1835 + let values: Vec<_> = m.values_of("arg").unwrap().collect(); + assert_eq!(values, vec!["opt"]); +} + +#[test] +fn multiple_one() { + env::set_var("CLP_TEST_ENV_MO", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_MO") + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.values_of("arg").unwrap().collect::>(), vec!["env"]); +} + +#[test] +fn multiple_three() { + env::set_var("CLP_TEST_ENV_MULTI1", "env1,env2,env3"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_MULTI1") + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + vec!["env1", "env2", "env3"] + ); +} + +#[test] +fn multiple_no_delimiter() { + env::set_var("CLP_TEST_ENV_MULTI2", "env1 env2 env3"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_MULTI2") + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + vec!["env1 env2 env3"] + ); +} + +#[test] +fn possible_value() { + env::set_var("CLP_TEST_ENV_PV", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_PV") + .takes_value(true) + .possible_value("env"), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn not_possible_value() { + env::set_var("CLP_TEST_ENV_NPV", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_NPV") + .takes_value(true) + .possible_value("never"), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_err()); +} + +#[test] +fn validator() { + env::set_var("CLP_TEST_ENV_VDOR", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_VDOR") + .takes_value(true) + .validator(|s| { + if s == "env" { + Ok(()) + } else { + Err("not equal".to_string()) + } + }), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn validator_output() { + env::set_var("CLP_TEST_ENV_VO", "42"); + + let m = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_VO") + .takes_value(true) + .validator(|s| s.parse::()), + ) + .try_get_matches_from(vec![""]) + .unwrap(); + + assert_eq!(m.value_of("arg").unwrap().parse(), Ok(42)); +} + +#[test] +fn validator_invalid() { + env::set_var("CLP_TEST_ENV_IV", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_IV") + .takes_value(true) + .validator(|s| { + if s != "env" { + Ok(()) + } else { + Err("is equal".to_string()) + } + }), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_err()); +} diff --git a/tests/builder/legacy/error.rs b/tests/builder/legacy/error.rs new file mode 100644 index 00000000000..249024bd048 --- /dev/null +++ b/tests/builder/legacy/error.rs @@ -0,0 +1,90 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, Command, Error}; + +fn assert_error(err: Error, expected_kind: ErrorKind, expected_output: &str, stderr: bool) { + let actual_output = err.to_string(); + assert_eq!( + stderr, + err.use_stderr(), + "Should Use STDERR failed. Should be {} but is {}", + stderr, + err.use_stderr() + ); + assert_eq!(expected_kind, err.kind()); + utils::assert_eq(expected_output, actual_output) +} + +#[test] +fn app_error() { + static MESSAGE: &str = "error: Failed for mysterious reasons + +USAGE: + test [OPTIONS] --all + +For more information try --help +"; + let cmd = Command::new("test") + .arg( + Arg::new("all") + .short('a') + .long("all") + .required(true) + .help("Also do versioning for private crates (will not be published)"), + ) + .arg( + Arg::new("exact") + .long("exact") + .help("Specify inter dependency version numbers exactly with `=`"), + ) + .arg( + Arg::new("no_git_commit") + .long("no-git-commit") + .help("Do not commit version changes"), + ) + .arg( + Arg::new("no_git_push") + .long("no-git-push") + .help("Do not push generated commit and tags to git remote"), + ); + let mut cmd = cmd; + let expected_kind = ErrorKind::InvalidValue; + let err = cmd.error(expected_kind, "Failed for mysterious reasons"); + assert_error(err, expected_kind, MESSAGE, true); +} + +#[test] +fn value_validation_has_newline() { + let m = Command::new("test") + .arg(arg!().help("Network port to use")) + .try_get_matches_from(["test", "foo"]) + .unwrap(); + + let res = m.value_of_t::("PORT"); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert!( + err.to_string().ends_with('\n'), + "Errors should have a trailing newline, got {:?}", + err.to_string() + ); +} + +#[test] +fn argument_not_found_auto_has_newline() { + let m = Command::new("test") + .arg(arg!([PORT]).help("Network port to use")) + .try_get_matches_from(["test"]) + .unwrap(); + + let res = m.value_of_t::("PORT"); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert!( + err.to_string().ends_with('\n'), + "Errors should have a trailing newline, got {:?}", + err.to_string() + ); +} diff --git a/tests/builder/legacy/flag_subcommands.rs b/tests/builder/legacy/flag_subcommands.rs new file mode 100644 index 00000000000..bb1201d44a6 --- /dev/null +++ b/tests/builder/legacy/flag_subcommands.rs @@ -0,0 +1,613 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, Command}; + +#[test] +fn flag_subcommand_normal() { + let matches = Command::new("test") + .subcommand( + Command::new("some").short_flag('S').long_flag("some").arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "some", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_normal_with_alias() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .long_flag("S") + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .alias("result"), + ) + .try_get_matches_from(vec!["myprog", "result", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short() { + let matches = Command::new("test") + .subcommand( + Command::new("some").short_flag('S').arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "-S", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short_with_args() { + let matches = Command::new("test") + .subcommand( + Command::new("some").short_flag('S').arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "-St"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short_with_alias() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_alias('M') + .short_flag_alias('B'), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short_with_alias_same_as_short_flag() { + let matches = Command::new("test") + .subcommand(Command::new("some").short_flag('S').short_flag_alias('S')) + .try_get_matches_from(vec!["myprog", "-S"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); +} + +#[test] +fn flag_subcommand_long_with_alias_same_as_long_flag() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .long_flag("sync") + .long_flag_alias("sync"), + ) + .try_get_matches_from(vec!["myprog", "--sync"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); +} + +#[test] +fn flag_subcommand_short_with_aliases_vis_and_hidden() { + let cmd = Command::new("test").subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .visible_short_flag_aliases(&['M', 'B']) + .short_flag_alias('C'), + ); + let app1 = cmd.clone(); + let matches1 = app1.try_get_matches_from(vec!["test", "-M"]).unwrap(); + assert_eq!(matches1.subcommand_name().unwrap(), "some"); + + let app2 = cmd.clone(); + let matches2 = app2.try_get_matches_from(vec!["test", "-C"]).unwrap(); + assert_eq!(matches2.subcommand_name().unwrap(), "some"); + + let app3 = cmd.clone(); + let matches3 = app3.try_get_matches_from(vec!["test", "-B"]).unwrap(); + assert_eq!(matches3.subcommand_name().unwrap(), "some"); +} + +#[test] +fn flag_subcommand_short_with_aliases() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_aliases(&['M', 'B']), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +#[should_panic] +fn flag_subcommand_short_with_alias_hyphen() { + let _ = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_alias('-'), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); +} + +#[test] +#[should_panic] +fn flag_subcommand_short_with_aliases_hyphen() { + let _ = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_aliases(&['-', '-', '-']), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_short_after_long_arg() { + let m = Command::new("pacman") + .subcommand( + Command::new("sync") + .short_flag('S') + .arg(Arg::new("clean").short('c')), + ) + .arg(Arg::new("arg").long("arg").takes_value(true)) + .try_get_matches_from(vec!["pacman", "--arg", "foo", "-Sc"]) + .unwrap(); + let subm = m.subcommand_matches("sync"); + assert!(subm.is_some()); + let subm = subm.unwrap(); + assert!(subm.is_present("clean")); +} + +#[test] +fn flag_subcommand_long() { + let matches = Command::new("test") + .subcommand( + Command::new("some").long_flag("some").arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "--some", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_long_with_alias() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .long_flag("some") + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .long_flag_alias("result"), + ) + .try_get_matches_from(vec!["myprog", "--result", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_long_with_aliases() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .long_flag("some") + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .long_flag_aliases(&["result", "someall"]), + ) + .try_get_matches_from(vec!["myprog", "--result", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_multiple() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .long_flag("some") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-p --print "print something")) + .subcommand( + Command::new("result") + .short_flag('R') + .long_flag("result") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-p --print "print something")), + ), + ) + .try_get_matches_from(vec!["myprog", "-SfpRfp"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("flag")); + assert!(sub_matches.is_present("print")); + assert_eq!(sub_matches.subcommand_name().unwrap(), "result"); + let result_matches = sub_matches.subcommand_matches("result").unwrap(); + assert!(result_matches.is_present("flag")); + assert!(result_matches.is_present("print")); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'-f\' short flag for the \'test\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_short_conflict_with_arg() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .arg(Arg::new("test").short('f')) + .try_get_matches_from(vec!["myprog", "-f"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'-f\' short flag is specified for both \'some\' and \'result\' subcommands"] +fn flag_subcommand_short_conflict_with_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .subcommand(Command::new("result").short_flag('t').short_flag_alias('f')) + .try_get_matches_from(vec!["myprog", "-f"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'--flag\' long flag is specified for both \'some\' and \'result\' subcommands"] +fn flag_subcommand_long_conflict_with_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").long_flag("flag")) + .subcommand( + Command::new("result") + .long_flag("test") + .long_flag_alias("flag"), + ) + .try_get_matches_from(vec!["myprog", "--flag"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'-f\' short flag for the \'test\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_short_conflict_with_arg_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .arg(Arg::new("test").short('t').short_alias('f')) + .try_get_matches_from(vec!["myprog", "-f"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'--some\' long flag for the \'test\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_long_conflict_with_arg_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .arg(Arg::new("test").long("test").alias("some")) + .try_get_matches_from(vec!["myprog", "--some"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'--flag\' long flag for the \'flag\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_long_conflict_with_arg() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('a').long_flag("flag")) + .arg(Arg::new("flag").long("flag")) + .try_get_matches_from(vec!["myprog", "--flag"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_conflict_with_help() { + let _ = Command::new("test") + .subcommand(Command::new("help").short_flag('h').long_flag("help")) + .try_get_matches_from(vec!["myprog", "--help"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_conflict_with_version() { + let _ = Command::new("test") + .subcommand(Command::new("ver").short_flag('V').long_flag("version")) + .try_get_matches_from(vec!["myprog", "--version"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_long_infer_pass() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .try_get_matches_from(vec!["prog", "--te"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[cfg(not(feature = "suggestions"))] +#[test] +fn flag_subcommand_long_infer_fail() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("temp").long_flag("temp")) + .try_get_matches_from(vec!["prog", "--te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[cfg(feature = "suggestions")] +#[test] +fn flag_subcommand_long_infer_fail() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("temp").long_flag("temp")) + .try_get_matches_from(vec!["prog", "--te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn flag_subcommand_long_infer_pass_close() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("temp").long_flag("temp")) + .try_get_matches_from(vec!["prog", "--tes"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[test] +fn flag_subcommand_long_infer_exact_match() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("testa").long_flag("testa")) + .subcommand(Command::new("testb").long_flag("testb")) + .try_get_matches_from(vec!["prog", "--test"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +static FLAG_SUBCOMMAND_HELP: &str = "pacman-query +Query the package database. + +USAGE: + pacman {query|--query|-Q} [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info ... view package information + -s, --search ... search locally installed packages for matching strings +"; + +#[test] +fn flag_subcommand_long_short_normal_usage_string() { + let cmd = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .short_flag('Q') + .long_flag("query") + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .takes_value(true) + .multiple_values(true), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .takes_value(true) + .multiple_values(true), + ), + ); + utils::assert_output(cmd, "pacman -Qh", FLAG_SUBCOMMAND_HELP, false); +} + +static FLAG_SUBCOMMAND_NO_SHORT_HELP: &str = "pacman-query +Query the package database. + +USAGE: + pacman {query|--query} [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info ... view package information + -s, --search ... search locally installed packages for matching strings +"; + +#[test] +fn flag_subcommand_long_normal_usage_string() { + let cmd = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .long_flag("query") + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .takes_value(true) + .multiple_values(true), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .takes_value(true) + .multiple_values(true), + ), + ); + utils::assert_output( + cmd, + "pacman query --help", + FLAG_SUBCOMMAND_NO_SHORT_HELP, + false, + ); +} + +static FLAG_SUBCOMMAND_NO_LONG_HELP: &str = "pacman-query +Query the package database. + +USAGE: + pacman {query|-Q} [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info ... view package information + -s, --search ... search locally installed packages for matching strings +"; + +#[test] +fn flag_subcommand_short_normal_usage_string() { + let cmd = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .short_flag('Q') + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .takes_value(true) + .multiple_values(true), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .takes_value(true) + .multiple_values(true), + ), + ); + utils::assert_output( + cmd, + "pacman query --help", + FLAG_SUBCOMMAND_NO_LONG_HELP, + false, + ); +} diff --git a/tests/builder/legacy/flags.rs b/tests/builder/legacy/flags.rs new file mode 100644 index 00000000000..f397296c51c --- /dev/null +++ b/tests/builder/legacy/flags.rs @@ -0,0 +1,198 @@ +use super::utils; +use clap::{arg, Arg, Command}; + +const USE_FLAG_AS_ARGUMENT: &str = + "error: Found argument '--another-flag' which wasn't expected, or isn't valid in this context + +\tIf you tried to supply `--another-flag` as a value rather than a flag, use `-- --another-flag` + +USAGE: + mycat [OPTIONS] [filename] + +For more information try --help +"; + +#[test] +fn flag_using_short() { + let m = Command::new("flag") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + ]) + .try_get_matches_from(vec!["", "-f", "-c"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn lots_o_flags_sep() { + let r = Command::new("opts") + .arg(arg!(o: -o ... "some flag")) + .try_get_matches_from(vec![ + "", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", + ]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 +} + +#[test] +fn lots_o_flags_combined() { + let r = Command::new("opts") + .arg(arg!(o: -o ... "some flag")) + .try_get_matches_from(vec![ + "", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-ooooooooooooooooooooooooooooooooooooooooo", + ]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 +} + +#[test] +fn flag_using_long() { + let m = Command::new("flag") + .args(&[arg!(--flag "some flag"), arg!(--color "some other flag")]) + .try_get_matches_from(vec!["", "--flag", "--color"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn flag_using_long_with_literals() { + use clap::error::ErrorKind; + + let m = Command::new("flag") + .arg(Arg::new("rainbow").long("rainbow")) + .try_get_matches_from(vec!["", "--rainbow=false"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::TooManyValues); +} + +#[test] +fn flag_using_mixed() { + let m = Command::new("flag") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + ]) + .try_get_matches_from(vec!["", "-f", "--color"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); + + let m = Command::new("flag") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + ]) + .try_get_matches_from(vec!["", "--flag", "-c"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn multiple_flags_in_single() { + let m = Command::new("multe_flags") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + arg!(-d --debug "another other flag"), + ]) + .try_get_matches_from(vec!["", "-fcd"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); + assert!(m.is_present("debug")); +} + +#[test] +fn issue_1284_argument_in_flag_style() { + let cmd = Command::new("mycat") + .arg(Arg::new("filename")) + .arg(Arg::new("a-flag").long("a-flag")); + + let m = cmd + .clone() + .try_get_matches_from(vec!["", "--", "--another-flag"]) + .unwrap(); + assert_eq!(m.value_of("filename"), Some("--another-flag")); + + let m = cmd + .clone() + .try_get_matches_from(vec!["", "--a-flag"]) + .unwrap(); + assert!(m.is_present("a-flag")); + + let m = cmd + .clone() + .try_get_matches_from(vec!["", "--", "--a-flag"]) + .unwrap(); + assert_eq!(m.value_of("filename"), Some("--a-flag")); + + utils::assert_output(cmd, "mycat --another-flag", USE_FLAG_AS_ARGUMENT, true); +} + +#[test] +fn issue_2308_multiple_dashes() { + static MULTIPLE_DASHES: &str = + "error: Found argument '-----' which wasn't expected, or isn't valid in this context + + If you tried to supply `-----` as a value rather than a flag, use `-- -----` + +USAGE: + test + +For more information try --help +"; + let cmd = Command::new("test").arg(Arg::new("arg").takes_value(true).required(true)); + + utils::assert_output(cmd, "test -----", MULTIPLE_DASHES, true); +} + +#[test] +#[cfg(not(feature = "unstable-v4"))] +fn leading_dash_stripped() { + let cmd = Command::new("mycat").arg(Arg::new("filename").long("--filename")); + let matches = cmd.try_get_matches_from(["mycat", "--filename"]).unwrap(); + assert!(matches.is_present("filename")); +} + +#[test] +#[cfg(feature = "unstable-v4")] +#[cfg(debug_assertions)] +#[should_panic = "Argument filename: long \"--filename\" must not start with a `-`, that will be handled by the parser"] +fn leading_dash_stripped() { + let cmd = Command::new("mycat").arg(Arg::new("filename").long("--filename")); + cmd.debug_assert(); +} diff --git a/tests/builder/legacy/global_args.rs b/tests/builder/legacy/global_args.rs new file mode 100644 index 00000000000..d8024089664 --- /dev/null +++ b/tests/builder/legacy/global_args.rs @@ -0,0 +1,114 @@ +use clap::{arg, Arg, Command}; + +#[test] +fn issue_1076() { + let mut cmd = Command::new("myprog") + .arg( + Arg::new("GLOBAL_ARG") + .long("global-arg") + .help("Specifies something needed by the subcommands") + .global(true) + .takes_value(true) + .default_value("default_value"), + ) + .arg( + Arg::new("GLOBAL_FLAG") + .long("global-flag") + .help("Specifies something needed by the subcommands") + .global(true) + .takes_value(true), + ) + .subcommand(Command::new("outer").subcommand(Command::new("inner"))); + let _ = cmd.try_get_matches_from_mut(vec!["myprog"]); + let _ = cmd.try_get_matches_from_mut(vec!["myprog"]); + let _ = cmd.try_get_matches_from_mut(vec!["myprog"]); +} + +#[test] +fn propagate_global_arg_in_subcommand_to_subsubcommand_1385() { + let m1 = Command::new("foo") + .subcommand( + Command::new("sub1") + .arg(Arg::new("arg1").long("arg1").takes_value(true).global(true)) + .subcommand(Command::new("sub1a")), + ) + .try_get_matches_from(&["foo", "sub1", "--arg1", "v1", "sub1a"]) + .unwrap(); + assert_eq!( + "v1", + m1.subcommand_matches("sub1") + .unwrap() + .subcommand_matches("sub1a") + .unwrap() + .value_of("arg1") + .unwrap() + ); +} + +#[test] +fn propagate_global_arg_to_subcommand_in_subsubcommand_2053() { + let m = Command::new("opts") + .arg(arg!(--"global-flag").global(true)) + .arg(arg!(--"global-str" ).required(false).global(true)) + .subcommand( + Command::new("test") + .arg(arg!(--"sub-flag").global(true)) + .arg(arg!(--"sub-str" ).required(false).global(true)) + .subcommand(Command::new("test")), + ) + .try_get_matches_from(&[ + "cmd", + "test", + "test", + "--global-flag", + "--global-str", + "hello", + "--sub-flag", + "--sub-str", + "world", + ]) + .unwrap(); + assert_eq!( + Some("world"), + m.subcommand_matches("test").unwrap().value_of("sub-str") + ); +} + +#[test] +fn global_arg_available_in_subcommand() { + let m = Command::new("opt") + .args(&[ + Arg::new("global").global(true).long("global"), + Arg::new("not").global(false).long("not"), + ]) + .subcommand(Command::new("ping")) + .try_get_matches_from(&["opt", "ping", "--global"]) + .unwrap(); + + assert!(m.is_present("global")); + assert!(m.subcommand_matches("ping").unwrap().is_present("global")); +} + +#[test] +fn deeply_nested_discovery() { + let cmd = Command::new("a") + .arg(arg!(--"long-a").global(true)) + .subcommand( + Command::new("b") + .arg(arg!(--"long-b").global(true)) + .subcommand( + Command::new("c") + .arg(arg!(--"long-c").global(true)) + .subcommand(Command::new("d")), + ), + ); + + let m = cmd + .try_get_matches_from(["a", "b", "c", "d", "--long-a", "--long-b", "--long-c"]) + .unwrap(); + assert!(m.is_present("long-a")); + let m = m.subcommand_matches("b").unwrap(); + assert!(m.is_present("long-b")); + let m = m.subcommand_matches("c").unwrap(); + assert!(m.is_present("long-c")); +} diff --git a/tests/builder/legacy/grouped_values.rs b/tests/builder/legacy/grouped_values.rs new file mode 100644 index 00000000000..622e9c93e87 --- /dev/null +++ b/tests/builder/legacy/grouped_values.rs @@ -0,0 +1,221 @@ +#![cfg(feature = "unstable-grouped")] + +use clap::{Arg, Command}; + +#[test] +fn grouped_value_works() { + let m = Command::new("cli") + .arg( + Arg::new("option") + .long("option") + .takes_value(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(&[ + "cli", + "--option", + "fr_FR:mon option 1", + "en_US:my option 1", + "--option", + "fr_FR:mon option 2", + "en_US:my option 2", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![ + vec!["fr_FR:mon option 1", "en_US:my option 1",], + vec!["fr_FR:mon option 2", "en_US:my option 2",], + ] + ); +} + +#[test] +fn issue_1026() { + let m = Command::new("cli") + .arg(Arg::new("server").short('s').takes_value(true)) + .arg(Arg::new("user").short('u').takes_value(true)) + .arg( + Arg::new("target") + .long("target") + .takes_value(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(&[ + "backup", "-s", "server", "-u", "user", "--target", "target1", "file1", "file2", + "file3", "--target", "target2", "file4", "file5", "file6", "file7", "--target", + "target3", "file8", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("target").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![ + vec!["target1", "file1", "file2", "file3"], + vec!["target2", "file4", "file5", "file6", "file7",], + vec!["target3", "file8"] + ] + ); +} + +#[test] +fn grouped_value_long_flag_delimiter() { + let m = Command::new("myapp") + .arg( + Arg::new("option") + .long("option") + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(vec![ + "myapp", + "--option=hmm", + "--option=val1,val2,val3", + "--option", + "alice,bob", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![ + vec!["hmm"], + vec!["val1", "val2", "val3"], + vec!["alice", "bob"] + ] + ); +} + +#[test] +fn grouped_value_short_flag_delimiter() { + let m = Command::new("myapp") + .arg( + Arg::new("option") + .short('o') + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["foo"], vec!["val1", "val2", "val3"], vec!["bar"]] + ); +} + +#[test] +fn grouped_value_positional_arg() { + let m = Command::new("multiple_values") + .arg( + Arg::new("pos") + .help("multiple positionals") + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![ + "myprog", "val1", "val2", "val3", "val4", "val5", "val6", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("pos").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["val1", "val2", "val3", "val4", "val5", "val6"]] + ); +} + +#[test] +fn grouped_value_multiple_positional_arg() { + let m = Command::new("multiple_values") + .arg(Arg::new("pos1").help("multiple positionals")) + .arg( + Arg::new("pos2") + .help("multiple positionals") + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![ + "myprog", "val1", "val2", "val3", "val4", "val5", "val6", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("pos2").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["val2", "val3", "val4", "val5", "val6"]] + ); +} + +#[test] +fn grouped_value_multiple_positional_arg_last_multiple() { + let m = Command::new("multiple_values") + .arg(Arg::new("pos1").help("multiple positionals")) + .arg( + Arg::new("pos2") + .help("multiple positionals") + .takes_value(true) + .multiple_values(true) + .last(true), + ) + .try_get_matches_from(vec![ + "myprog", "val1", "--", "val2", "val3", "val4", "val5", "val6", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("pos2").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["val2", "val3", "val4", "val5", "val6"]] + ); +} + +#[test] +fn issue_1374() { + let cmd = Command::new("MyApp").arg( + Arg::new("input") + .takes_value(true) + .long("input") + .overrides_with("input") + .min_values(0) + .multiple_occurrences(true), + ); + let matches = cmd + .clone() + .try_get_matches_from(&["MyApp", "--input", "a", "b", "c", "--input", "d"]) + .unwrap(); + let vs = matches.values_of("input").unwrap(); + assert_eq!(vs.collect::>(), vec!["a", "b", "c", "d"]); + let matches = cmd + .clone() + .try_get_matches_from(&["MyApp", "--input", "a", "b", "--input", "c", "d"]) + .unwrap(); + let vs = matches.values_of("input").unwrap(); + assert_eq!(vs.collect::>(), vec!["a", "b", "c", "d"]); +} + +#[test] +fn issue_2171() { + let schema = Command::new("ripgrep#1701 reproducer") + .args_override_self(true) + .arg(Arg::new("pretty").short('p').long("pretty")) + .arg(Arg::new("search_zip").short('z').long("search-zip")); + + let test_args = &[ + vec!["reproducer", "-pz", "-p"], + vec!["reproducer", "-pzp"], + vec!["reproducer", "-zpp"], + vec!["reproducer", "-pp", "-z"], + vec!["reproducer", "-p", "-p", "-z"], + vec!["reproducer", "-p", "-pz"], + vec!["reproducer", "-ppz"], + ]; + + for argv in test_args { + let _ = schema.clone().try_get_matches_from(argv).unwrap(); + } +} diff --git a/tests/builder/legacy/groups.rs b/tests/builder/legacy/groups.rs new file mode 100644 index 00000000000..67a6fea0615 --- /dev/null +++ b/tests/builder/legacy/groups.rs @@ -0,0 +1,325 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; + +static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided: + + +USAGE: + clap-test + +For more information try --help +"; + +static REQ_GROUP_CONFLICT_USAGE: &str = + "error: The argument '--delete' cannot be used with '' + +USAGE: + clap-test + +For more information try --help +"; + +static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = + "error: The argument '--delete' cannot be used with '--all' + +USAGE: + clap-test <--all|--delete> + +For more information try --help +"; + +#[test] +fn required_group_missing_arg() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!( -c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flag", "color"]).required(true)) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group 'req' contains non-existent argument"] +fn non_existing_arg() { + let _ = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flg", "color"]).required(true)) + .try_get_matches_from(vec![""]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group name must be unique\n\n\t'req' is already in use"] +fn unique_group_name() { + let _ = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flag"]).required(true)) + .group(ArgGroup::new("req").args(&["color"]).required(true)) + .try_get_matches_from(vec![""]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group name '' must not conflict with argument name"] +fn groups_new_of_arg_name() { + let _ = Command::new("group") + .arg(Arg::new("a").long("a").group("a")) + .try_get_matches_from(vec!["", "--a"]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group name 'a' must not conflict with argument name"] +fn arg_group_new_of_arg_name() { + let _ = Command::new("group") + .arg(Arg::new("a").long("a").group("a")) + .group(ArgGroup::new("a")) + .try_get_matches_from(vec!["", "--a"]); +} + +#[test] +fn group_single_value() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color [color] "some option")) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec!["", "-c", "blue"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + let m = res.unwrap(); + assert!(m.is_present("grp")); + assert_eq!(m.value_of("grp").unwrap(), "blue"); +} + +#[test] +fn group_single_flag() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color [color] "some option")) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec!["", "-f"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + let m = res.unwrap(); + assert!(m.is_present("grp")); + assert!(m.value_of("grp").is_none()); +} + +#[test] +fn group_empty() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color [color] "some option")) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec![""]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + let m = res.unwrap(); + assert!(!m.is_present("grp")); + assert!(m.value_of("grp").is_none()); +} + +#[test] +fn group_reqired_flags_empty() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some option")) + .group(ArgGroup::new("grp").required(true).args(&["flag", "color"])) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn group_multi_value_single_arg() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some option").multiple_values(true)) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]); + assert!(res.is_ok(), "{:?}", res.unwrap_err().kind()); + + let m = res.unwrap(); + assert!(m.is_present("grp")); + assert_eq!( + &*m.values_of("grp").unwrap().collect::>(), + &["blue", "red", "green"] + ); +} + +#[test] +fn empty_group() { + let r = Command::new("empty_group") + .arg(arg!(-f --flag "some flag")) + .group(ArgGroup::new("vers").required(true)) + .try_get_matches_from(vec!["empty_prog"]); + assert!(r.is_err()); + let err = r.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn req_group_usage_string() { + let cmd = Command::new("req_group") + .arg(arg!([base] "Base commit")) + .arg(arg!( + -d --delete "Remove the base commit information" + )) + .group( + ArgGroup::new("base_or_delete") + .args(&["base", "delete"]) + .required(true), + ); + + utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true); +} + +#[test] +fn req_group_with_conflict_usage_string() { + let cmd = Command::new("req_group") + .arg(arg!([base] "Base commit").conflicts_with("delete")) + .arg(arg!( + -d --delete "Remove the base commit information" + )) + .group( + ArgGroup::new("base_or_delete") + .args(&["base", "delete"]) + .required(true), + ); + + utils::assert_output( + cmd, + "clap-test --delete base", + REQ_GROUP_CONFLICT_USAGE, + true, + ); +} + +#[test] +fn req_group_with_conflict_usage_string_only_options() { + let cmd = Command::new("req_group") + .arg(arg!(-a --all "All").conflicts_with("delete")) + .arg(arg!( + -d --delete "Remove the base commit information" + )) + .group( + ArgGroup::new("all_or_delete") + .args(&["all", "delete"]) + .required(true), + ); + utils::assert_output( + cmd, + "clap-test --delete --all", + REQ_GROUP_CONFLICT_ONLY_OPTIONS, + true, + ); +} + +#[test] +fn required_group_multiple_args() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group( + ArgGroup::new("req") + .args(&["flag", "color"]) + .required(true) + .multiple(true), + ) + .try_get_matches_from(vec!["group", "-f", "-c"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn group_multiple_args_error() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flag", "color"])) + .try_get_matches_from(vec!["group", "-f", "-c"]); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn group_overrides_required() { + let command = Command::new("group") + .arg(arg!(--foo )) + .arg(arg!(--bar )) + .group(ArgGroup::new("group").args(&["foo", "bar"]).required(true)); + let result = command.try_get_matches_from(vec!["group", "--foo", "value"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + assert!(m.is_present("foo")); + assert!(!m.is_present("bar")); +} + +#[test] +fn group_usage_use_val_name() { + static GROUP_USAGE_USE_VAL_NAME: &str = "prog + +USAGE: + prog + +ARGS: + + +OPTIONS: + -h, --help Print help information +"; + let cmd = Command::new("prog") + .arg(Arg::new("a").value_name("A")) + .group(ArgGroup::new("group").arg("a").required(true)); + utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false); +} + +#[test] +fn group_acts_like_arg() { + let result = Command::new("prog") + .arg(Arg::new("debug").long("debug").group("mode")) + .arg(Arg::new("verbose").long("verbose").group("mode")) + .try_get_matches_from(vec!["prog", "--debug"]); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + assert!(m.is_present("mode")); +} + +/* This is used to be fixed in a hack, we need to find a better way to fix it. +#[test] +fn issue_1794() { + let cmd = clap::Command::new("hello") + .bin_name("deno") + .arg(Arg::new("option1").long("option1").takes_value(false)) + .arg(Arg::new("pos1").takes_value(true)) + .arg(Arg::new("pos2").takes_value(true)) + .group( + ArgGroup::new("arg1") + .args(&["pos1", "option1"]) + .required(true), + ); + + let m = cmd.clone().try_get_matches_from(&["cmd", "pos1", "pos2"]).unwrap(); + assert_eq!(m.value_of("pos1"), Some("pos1")); + assert_eq!(m.value_of("pos2"), Some("pos2")); + assert!(!m.is_present("option1")); + + let m = cmd + .clone() + .try_get_matches_from(&["cmd", "--option1", "positional"]).unwrap(); + assert_eq!(m.value_of("pos1"), None); + assert_eq!(m.value_of("pos2"), Some("positional")); + assert!(m.is_present("option1")); +} +*/ diff --git a/tests/builder/legacy/help.rs b/tests/builder/legacy/help.rs new file mode 100644 index 00000000000..6701ed712fe --- /dev/null +++ b/tests/builder/legacy/help.rs @@ -0,0 +1,2867 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command, PossibleValue}; + +static REQUIRE_DELIM_HELP: &str = "test 1.3 +Kevin K. +tests stuff + +USAGE: + test --fake : + +OPTIONS: + -f, --fake : some help + -h, --help Print help information + -V, --version Print version information +"; + +static HELP: &str = "clap-test v1.4.8 +Kevin K. +tests clap library + +USAGE: + clap-test [OPTIONS] [ARGS] [SUBCOMMAND] + +ARGS: + tests positionals + tests positionals with exclusions + ... tests specific values [possible values: vi, emacs] + +OPTIONS: + -f, --flag tests flags + -F tests flags with exclusions + -h, --help Print help information + --long-option-2 tests long options with exclusions + --maxvals3 ... Tests 3 max vals + --minvals2 ... Tests 2 min vals + --multvals Tests multiple values, not mult occs + --multvalsmo Tests multiple values, and mult occs + -o, --option ... tests options + -O, --option3 specific vals [possible values: fast, slow] + --optvaleq[=] Tests optional value, require = sign + --optvalnoeq [] Tests optional value + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + subcmd tests subcommands +"; + +static SC_NEGATES_REQS: &str = "prog 1.0 + +USAGE: + prog --opt [PATH] + prog [PATH] + +ARGS: + help + +OPTIONS: + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + test +"; + +static ARGS_NEGATE_SC: &str = "prog 1.0 + +USAGE: + prog [OPTIONS] [PATH] + prog + +ARGS: + help + +OPTIONS: + -f, --flag testing flags + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + test +"; + +static AFTER_HELP: &str = "some text that comes before the help + +clap-test v1.4.8 +tests clap library + +USAGE: + clap-test + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +some text that comes after the help +"; + +static AFTER_LONG_HELP: &str = "some longer text that comes before the help + +clap-test v1.4.8 +tests clap library + +USAGE: + clap-test + +OPTIONS: + -h, --help + Print help information + + -V, --version + Print version information + +some longer text that comes after the help +"; + +static HIDDEN_ARGS: &str = "prog 1.0 + +USAGE: + prog [OPTIONS] + +OPTIONS: + -f, --flag testing flags + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information +"; + +static SC_HELP: &str = "clap-test-subcmd 0.1 +Kevin K. +tests subcommands + +USAGE: + clap-test subcmd [OPTIONS] [--] [scpositional] + +ARGS: + tests positionals + +OPTIONS: + -f, --flag tests flags + -h, --help Print help information + -o, --option ... tests options + -s, --subcmdarg tests other args + -V, --version Print version information +"; + +static ISSUE_1046_HIDDEN_SCS: &str = "prog 1.0 + +USAGE: + prog [OPTIONS] [PATH] + +ARGS: + some + +OPTIONS: + -f, --flag testing flags + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information +"; + +// Using number_of_values(1) with multiple_values(true) misaligns help message +static ISSUE_760: &str = "ctest 0.1 + +USAGE: + ctest [OPTIONS] + +OPTIONS: + -h, --help Print help information + -o, --option