From 3f94d17c71d8dea26133dbe107289c7f0c187499 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Wed, 16 Jun 2021 06:28:25 +0100 Subject: [PATCH 1/2] Removed Arg::multiple --- CHANGELOG.md | 1 + README.md | 2 +- clap_derive/src/derives/into_app.rs | 8 +- clap_derive/tests/basic.rs | 2 +- clap_derive/tests/custom-string-parsers.rs | 4 +- .../tests/explicit_name_no_renaming.rs | 2 +- clap_derive/tests/options.rs | 10 +- clap_derive/tests/raw_idents.rs | 2 +- examples/05_flag_args.rs | 7 +- examples/07_option_args.rs | 8 +- examples/20_subcommands.rs | 2 +- examples/22_stop_parsing_with_--.rs | 7 +- examples/23_flag_subcommands_pacman.rs | 4 +- src/build/app/settings.rs | 10 +- src/build/arg/mod.rs | 514 +++++++++--------- src/build/arg/value_hint.rs | 4 +- src/macros.rs | 8 +- src/parse/errors.rs | 4 +- src/parse/matches/arg_matches.rs | 24 +- src/parse/parser.rs | 10 +- tests/delimiters.rs | 2 +- tests/env.rs | 6 +- tests/flag_subcommands.rs | 8 +- tests/global_args.rs | 5 +- tests/grouped_values.rs | 21 +- tests/help.rs | 18 +- tests/indices.rs | 14 +- tests/multiple_values.rs | 215 ++++---- tests/positionals.rs | 4 +- tests/possible_values.rs | 12 +- tests/subcommands.rs | 7 +- 31 files changed, 465 insertions(+), 480 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30b48a64..6a0c9c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ TODO: `cargo`, `std` features * **Removed Methods** * **Arg** * `Arg::settings` in favor of `Arg::setting(Setting1 | Setting2)` + * `Arg::multiple` in favour of `Arg::multiple_values` and `Arg::multiple_occurrences` * **Renamed Settings** * `AppSettings::DisableHelpFlags` => `AppSettings::DisableHelpFlag` * `AppSettings::DisableVersion` => `AppSettings::DisableVersionFlag` diff --git a/README.md b/README.md index fa24f01e..ecaf8e0b 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ fn main() { .index(1)) .arg(Arg::new("v") .short('v') - .multiple(true) + .multiple_occurrences(true) .takes_value(true) .about("Sets the level of verbosity")) .subcommand(App::new("test") diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs index 68d32e18..081ce00f 100644 --- a/clap_derive/src/derives/into_app.rs +++ b/clap_derive/src/derives/into_app.rs @@ -291,7 +291,7 @@ pub fn gen_app_augmentation( Ty::OptionOption => quote_spanned! { ty.span()=> .takes_value(true) - .multiple(false) + .multiple_values(false) .min_values(0) .max_values(1) #validator @@ -299,7 +299,7 @@ pub fn gen_app_augmentation( Ty::OptionVec => quote_spanned! { ty.span()=> .takes_value(true) - .multiple(true) + .multiple_values(true) .min_values(0) #validator }, @@ -315,7 +315,7 @@ pub fn gen_app_augmentation( quote_spanned! { ty.span()=> .takes_value(true) - .multiple(true) + .multiple_values(true) #possible_values #validator } @@ -327,7 +327,7 @@ pub fn gen_app_augmentation( Ty::Other if flag => quote_spanned! { ty.span()=> .takes_value(false) - .multiple(false) + .multiple_values(false) }, Ty::Other => { diff --git a/clap_derive/tests/basic.rs b/clap_derive/tests/basic.rs index 57745377..9592c305 100644 --- a/clap_derive/tests/basic.rs +++ b/clap_derive/tests/basic.rs @@ -25,7 +25,7 @@ fn basic() { assert_eq!(Opt { arg: vec![] }, Opt::parse_from(&["test"])); assert_eq!( Opt { arg: vec![24, 42] }, - Opt::parse_from(&["test", "-a24", "--arg", "42"]) + Opt::parse_from(&["test", "--arg", "24", "42"]) ); } diff --git a/clap_derive/tests/custom-string-parsers.rs b/clap_derive/tests/custom-string-parsers.rs index fccd0083..b2a2feff 100644 --- a/clap_derive/tests/custom-string-parsers.rs +++ b/clap_derive/tests/custom-string-parsers.rs @@ -26,7 +26,7 @@ struct PathOpt { #[clap(short, default_value = "../", parse(from_os_str))] default_path: PathBuf, - #[clap(short, parse(from_os_str))] + #[clap(short, parse(from_os_str), multiple_occurrences(true))] vector_path: Vec, #[clap(short, parse(from_os_str))] @@ -254,7 +254,7 @@ fn test_custom_bool() { verbose: bool, #[clap(short, parse(try_from_str = parse_bool))] tribool: Option, - #[clap(short, parse(try_from_str = parse_bool))] + #[clap(short, parse(try_from_str = parse_bool), multiple_occurrences(true))] bitset: Vec, } diff --git a/clap_derive/tests/explicit_name_no_renaming.rs b/clap_derive/tests/explicit_name_no_renaming.rs index 57ca16d0..be402036 100644 --- a/clap_derive/tests/explicit_name_no_renaming.rs +++ b/clap_derive/tests/explicit_name_no_renaming.rs @@ -7,7 +7,7 @@ use utils::*; fn explicit_short_long_no_rename() { #[derive(Clap, PartialEq, Debug)] struct Opt { - #[clap(short = '.', long = ".foo")] + #[clap(short = '.', long = ".foo", multiple_occurrences(true))] foo: Vec, } diff --git a/clap_derive/tests/options.rs b/clap_derive/tests/options.rs index 34d2eae2..e5ecfdce 100644 --- a/clap_derive/tests/options.rs +++ b/clap_derive/tests/options.rs @@ -70,7 +70,7 @@ fn option_with_raw_default() { fn options() { #[derive(Clap, PartialEq, Debug)] struct Opt { - #[clap(short, long)] + #[clap(short, long, multiple_occurrences(true))] arg: Vec, } assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "-a24"])); @@ -120,7 +120,7 @@ fn option_from_str() { fn optional_argument_for_optional_option() { #[derive(Clap, PartialEq, Debug)] struct Opt { - #[clap(short)] + #[clap(short, multiple_occurrences(true))] #[allow(clippy::option_option)] arg: Option>, } @@ -193,7 +193,7 @@ fn two_option_options() { fn optional_vec() { #[derive(Clap, PartialEq, Debug)] struct Opt { - #[clap(short)] + #[clap(short, multiple_occurrences(true))] arg: Option>, } assert_eq!( @@ -250,10 +250,10 @@ fn optional_vec() { fn two_optional_vecs() { #[derive(Clap, PartialEq, Debug)] struct Opt { - #[clap(short)] + #[clap(short, multiple_occurrences(true))] arg: Option>, - #[clap(short)] + #[clap(short, multiple_occurrences(true))] b: Option>, } diff --git a/clap_derive/tests/raw_idents.rs b/clap_derive/tests/raw_idents.rs index 6e8d0008..83fb3ff7 100644 --- a/clap_derive/tests/raw_idents.rs +++ b/clap_derive/tests/raw_idents.rs @@ -4,7 +4,7 @@ use clap::Clap; fn raw_idents() { #[derive(Clap, Debug, PartialEq)] struct Opt { - #[clap(short, long)] + #[clap(short, long, multiple_occurrences(true))] r#type: Vec, } diff --git a/examples/05_flag_args.rs b/examples/05_flag_args.rs index ded32752..6dfc193b 100644 --- a/examples/05_flag_args.rs +++ b/examples/05_flag_args.rs @@ -18,8 +18,7 @@ fn main() { .about("turns up the awesome") // Displayed when showing help info .short('a') // Trigger this arg with "-a" .long("awesome") // Trigger this arg with "--awesome" - .takes_value(true) - .multiple(true) // This flag should allow multiple + .multiple_occurrences(true) // This flag should allow multiple // occurrences such as "-aaa" or "-a -a" .requires("config") // Says, "If the user uses -a, they MUST // also use this other 'config' arg too" @@ -39,9 +38,9 @@ fn main() { println!("Awesomeness is turned on"); } - // If we set the multiple() option of a flag we can check how many times the user specified + // If we set the multiple option of a flag we can check how many times the user specified // - // Note: if we did not specify the multiple() option, and the user used "awesome" we would get + // Note: if we did not specify the multiple option, and the user used "awesome" we would get // a 1 (no matter how many times they actually used it), or a 0 if they didn't use it at all match matches.occurrences_of("awesome") { 0 => println!("Nothing is awesome"), diff --git a/examples/07_option_args.rs b/examples/07_option_args.rs index 1c5eb438..92f6143c 100644 --- a/examples/07_option_args.rs +++ b/examples/07_option_args.rs @@ -19,7 +19,7 @@ fn main() { .takes_value(true) // MUST be set to true in order to be an "option" argument .short('i') // This argument is triggered with "-i" .long("input") // This argument is triggered with "--input" - .multiple(true) // Set to true if you wish to allow multiple occurrences + .multiple_occurrences(true) // Set to true if you wish to allow multiple occurrences // such as "-i file -i other_file -i third_file" .required(true) // By default this argument MUST be present // NOTE: mutual exclusions take precedence over @@ -44,13 +44,13 @@ fn main() { // We can also get the value for "input" // - // NOTE: If we specified multiple(), this will only return the _FIRST_ + // NOTE: If we specified multiple_occurrences(), this will only return the _FIRST_ // occurrence if let Some(ref in_file) = matches.value_of("input") { println!("An input file: {}", in_file); } - // If we specified the multiple() setting we can get all the values + // If we specified the multiple_occurrences() setting we can get all the values if let Some(in_v) = matches.values_of("input") { for in_file in in_v { println!("An input file: {}", in_file); @@ -59,7 +59,7 @@ fn main() { // We can see how many times the option was used with the occurrences_of() method // - // NOTE: Just like with flags, if we did not specify the multiple() setting this will only + // NOTE: Just like with flags, if we did not specify the multiple_occurrences() setting this will only // return 1 no matter how many times the argument was used (unless it wasn't used at all, in // in which case 0 is returned) println!( diff --git a/examples/20_subcommands.rs b/examples/20_subcommands.rs index 38f1c14b..0421f753 100644 --- a/examples/20_subcommands.rs +++ b/examples/20_subcommands.rs @@ -79,7 +79,7 @@ fn main() { .long("stuff") .about("Stuff to add") .takes_value(true) - .multiple(true), + .multiple_values(true), ), ) .get_matches(); diff --git a/examples/22_stop_parsing_with_--.rs b/examples/22_stop_parsing_with_--.rs index 2ff376f7..68985dd3 100644 --- a/examples/22_stop_parsing_with_--.rs +++ b/examples/22_stop_parsing_with_--.rs @@ -5,7 +5,12 @@ fn main() { let matches = App::new("myprog") .arg(Arg::new("eff").short('f')) .arg(Arg::new("pea").short('p').takes_value(true)) - .arg(Arg::new("slop").takes_value(true).multiple(true).last(true)) + .arg( + Arg::new("slop") + .takes_value(true) + .multiple_values(true) + .last(true), + ) .get_matches(); println!("-f used: {:?}", matches.is_present("eff")); diff --git a/examples/23_flag_subcommands_pacman.rs b/examples/23_flag_subcommands_pacman.rs index 49f337ef..45407445 100644 --- a/examples/23_flag_subcommands_pacman.rs +++ b/examples/23_flag_subcommands_pacman.rs @@ -80,9 +80,9 @@ fn main() { .arg( Arg::new("package") .about("packages") - .multiple(true) .required_unless_present("search") - .takes_value(true), + .takes_value(true) + .multiple_values(true), ), ) .get_matches(); diff --git a/src/build/app/settings.rs b/src/build/app/settings.rs index 812cbe61..dacda7dc 100644 --- a/src/build/app/settings.rs +++ b/src/build/app/settings.rs @@ -338,7 +338,7 @@ pub enum AppSettings { /// .setting(AppSettings::AllowMissingPositional) /// .arg(Arg::new("foo")) /// .arg(Arg::new("bar")) - /// .arg(Arg::new("baz").takes_value(true).multiple(true)) + /// .arg(Arg::new("baz").takes_value(true).multiple_values(true)) /// .get_matches_from(vec![ /// "prog", "foo", "bar", "baz1", "baz2", "baz3" /// ]); @@ -357,7 +357,7 @@ pub enum AppSettings { /// .setting(AppSettings::AllowMissingPositional) /// .arg(Arg::new("foo")) /// .arg(Arg::new("bar")) - /// .arg(Arg::new("baz").takes_value(true).multiple(true)) + /// .arg(Arg::new("baz").takes_value(true).multiple_values(true)) /// .get_matches_from(vec![ /// "prog", "--", "baz1", "baz2", "baz3" /// ]); @@ -476,7 +476,7 @@ pub enum AppSettings { /// let app = App::new("app").subcommand(App::new("sub")).arg( /// Arg::new("arg") /// .long("arg") - /// .multiple(true) + /// .multiple_values(true) /// .takes_value(true), /// ); /// @@ -994,7 +994,7 @@ pub enum AppSettings { /// /// The values of the trailing positional argument will contain all args from itself on. /// - /// **NOTE:** The final positional argument **must** have [`Arg::multiple(true)`] or the usage + /// **NOTE:** The final positional argument **must** have [`Arg::multiple_values(true)`] or the usage /// string equivalent. /// /// # Examples @@ -1009,7 +1009,7 @@ pub enum AppSettings { /// let trail: Vec<&str> = m.values_of("cmd").unwrap().collect(); /// assert_eq!(trail, ["arg1", "-r", "val1"]); /// ``` - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_values(true)`]: Arg::multiple_values() TrailingVarArg, /// Groups flags and options together, presenting a more unified help message diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs index a9af76b0..1ff82c19 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg/mod.rs @@ -1749,7 +1749,7 @@ impl<'help> Arg<'help> { /// assigned in order of evaluation. Utilizing the `index` method allows for setting /// indexes out of order /// - /// **NOTE:** When utilized with [`Arg::multiple(true)`], only the **last** positional argument + /// **NOTE:** When utilized with [`Arg::multiple_values(true)`], only the **last** positional argument /// may be defined as multiple (i.e. with the highest index) /// /// # Panics @@ -1784,7 +1784,7 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::short`]: Arg::short() /// [`Arg::long`]: Arg::long() - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_values(true)`]: Arg::multiple_values() /// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html #[inline] pub fn index(mut self, idx: usize) -> Self { @@ -1793,7 +1793,7 @@ impl<'help> Arg<'help> { } /// Specifies a value that *stops* parsing multiple values of a give argument. By default when - /// one sets [`multiple(true)`] on an argument, clap will continue parsing values for that + /// one sets [`multiple_values(true)`] on an argument, clap will continue parsing values for that /// argument until it reaches another valid argument, or one of the other more specific settings /// for multiple values is used (such as [`min_values`], [`max_values`] or /// [`number_of_values`]). @@ -1809,10 +1809,11 @@ impl<'help> Arg<'help> { /// # use clap::{App, Arg}; /// Arg::new("vals") /// .takes_value(true) - /// .multiple(true) + /// .multiple_values(true) /// .value_terminator(";") /// # ; /// ``` + /// /// The following example uses two arguments, a sequence of commands, and the location in which /// to perform them /// @@ -1821,7 +1822,7 @@ impl<'help> Arg<'help> { /// let m = App::new("prog") /// .arg(Arg::new("cmds") /// .takes_value(true) - /// .multiple(true) + /// .multiple_values(true) /// .allow_hyphen_values(true) /// .value_terminator(";")) /// .arg(Arg::new("location")) @@ -1834,7 +1835,7 @@ impl<'help> Arg<'help> { /// ``` /// [options]: Arg::takes_value() /// [positional arguments]: Arg::index() - /// [`multiple(true)`]: Arg::multiple() + /// [`multiple_values(true)`]: Arg::multiple_values() /// [`min_values`]: Arg::min_values() /// [`number_of_values`]: Arg::number_of_values() /// [`max_values`]: Arg::max_values() @@ -2028,9 +2029,9 @@ impl<'help> Arg<'help> { /// `.number_of_values(3)`, and this argument wouldn't be satisfied unless the user provided /// 3 and only 3 values. /// - /// **NOTE:** Does *not* require [`Arg::multiple(true)`] to be set. Setting - /// [`Arg::multiple(true)`] would allow `-f -f ` where - /// as *not* setting [`Arg::multiple(true)`] would only allow one occurrence of this argument. + /// **NOTE:** Does *not* require [`Arg::multiple_occurrences(true)`] to be set. Setting + /// [`Arg::multiple_occurrences(true)`] would allow `-f -f ` where + /// as *not* setting it would only allow one occurrence of this argument. /// /// # Examples /// @@ -2038,8 +2039,7 @@ impl<'help> Arg<'help> { /// # use clap::{App, Arg}; /// Arg::new("file") /// .short('f') - /// .number_of_values(3) - /// # ; + /// .number_of_values(3); /// ``` /// /// Not supplying the correct number of values is an error @@ -2058,7 +2058,7 @@ impl<'help> Arg<'help> { /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::WrongNumberOfValues); /// ``` - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] pub fn number_of_values(mut self, qty: usize) -> Self { self.num_vals = Some(qty); @@ -2227,10 +2227,10 @@ impl<'help> Arg<'help> { /// `-f ` argument where you wanted up to 3 'files' you would set `.max_values(3)`, and /// this argument would be satisfied if the user provided, 1, 2, or 3 values. /// - /// **NOTE:** This does *not* implicitly set [`Arg::multiple(true)`]. This is because + /// **NOTE:** This does *not* implicitly set [`Arg::multiple_occurrences(true)`]. This is because /// `-o val -o val` is multiple occurrences but a single value and `-o val1 val2` is a single /// occurrence with multiple values. For positional arguments this **does** set - /// [`Arg::multiple(true)`] because there is no way to determine the difference between multiple + /// [`Arg::multiple_occurrences(true)`] because there is no way to determine the difference between multiple /// occurrences and multiple values. /// /// # Examples @@ -2239,8 +2239,7 @@ impl<'help> Arg<'help> { /// # use clap::{App, Arg}; /// Arg::new("file") /// .short('f') - /// .max_values(3) - /// # ; + /// .max_values(3); /// ``` /// /// Supplying less than the maximum number of values is allowed @@ -2278,7 +2277,7 @@ impl<'help> Arg<'help> { /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); /// ``` - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] pub fn max_values(mut self, qty: usize) -> Self { self.max_vals = Some(qty); @@ -2290,10 +2289,10 @@ impl<'help> Arg<'help> { /// `.min_values(2)`, and this argument would be satisfied if the user provided, 2 or more /// values. /// - /// **NOTE:** This does not implicitly set [`Arg::multiple(true)`]. This is because + /// **NOTE:** This does not implicitly set [`Arg::multiple_occurrences(true)`]. This is because /// `-o val -o val` is multiple occurrences but a single value and `-o val1 val2` is a single /// occurrence with multiple values. For positional arguments this **does** set - /// [`Arg::multiple(true)`] because there is no way to determine the difference between multiple + /// [`Arg::multiple_occurrences(true)`] because there is no way to determine the difference between multiple /// occurrences and multiple values. /// /// # Examples @@ -2302,8 +2301,7 @@ impl<'help> Arg<'help> { /// # use clap::{App, Arg}; /// Arg::new("file") /// .short('f') - /// .min_values(3) - /// # ; + /// .min_values(3); /// ``` /// /// Supplying more than the minimum number of values is allowed @@ -2341,7 +2339,7 @@ impl<'help> Arg<'help> { /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::TooFewValues); /// ``` - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] pub fn min_values(mut self, qty: usize) -> Self { self.min_vals = Some(qty); @@ -2393,13 +2391,9 @@ impl<'help> Arg<'help> { /// **Pro Tip:** It may help to use [`Arg::next_line_help(true)`] if there are long, or /// multiple value names in order to not throw off the help text alignment of all options. /// - /// **NOTE:** This implicitly sets [`Arg::number_of_values`] if the number of value names is - /// greater than one. I.e. be aware that the number of "names" you set for the values, will be - /// the *exact* number of values required to satisfy this argument - /// /// **NOTE:** implicitly sets [`Arg::takes_value(true)`] /// - /// **NOTE:** Does *not* require or imply [`Arg::multiple(true)`]. + /// **NOTE:** Does *not* require or imply [`Arg::multiple_values(true)`]. /// /// # Examples /// @@ -2407,8 +2401,7 @@ impl<'help> Arg<'help> { /// # use clap::{App, Arg}; /// Arg::new("speed") /// .short('s') - /// .value_names(&["fast", "slow"]) - /// # ; + /// .value_names(&["fast", "slow"]); /// ``` /// /// ```rust @@ -2421,6 +2414,7 @@ impl<'help> Arg<'help> { /// "prog", "--help" /// ]); /// ``` + /// /// Running the above program produces the following output /// /// ```text @@ -2439,7 +2433,7 @@ impl<'help> Arg<'help> { /// [`Arg::next_line_help(true)`]: Arg::next_line_help() /// [`Arg::number_of_values`]: Arg::number_of_values() /// [`Arg::takes_value(true)`]: Arg::takes_value() - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_values(true)`]: Arg::multiple_values() pub fn value_names(mut self, names: &[&'help str]) -> Self { let mut i = self.val_names.len(); for s in names { @@ -3069,7 +3063,7 @@ impl<'help> Arg<'help> { /// .long("flag") /// .env("MY_FLAG_MULTI") /// .takes_value(true) - /// .multiple(true) + /// .multiple_values(true) /// .use_delimiter(true)) /// .get_matches_from(vec![ /// "prog" @@ -3081,7 +3075,6 @@ impl<'help> Arg<'help> { /// [`ArgMatches::value_of`]: ArgMatches::value_of() /// [`ArgMatches::is_present`]: ArgMatches::is_present() /// [`Arg::takes_value(true)`]: Arg::takes_value() - /// [`Arg::multiple(true)`]: Arg::multiple() /// [`Arg::use_delimiter(true)`]: Arg::use_delimiter() #[inline] pub fn env(self, name: &'help str) -> Self { @@ -3562,6 +3555,7 @@ impl<'help> Arg<'help> { /// assert!(delims.is_present("opt")); /// assert_eq!(delims.values_of("opt").unwrap().collect::>(), ["val1", "val2", "val3"]); /// ``` + /// /// In this next example, we will *not* use a delimiter. Notice it's now an error. /// /// ```rust @@ -3580,6 +3574,7 @@ impl<'help> Arg<'help> { /// let err = res.unwrap_err(); /// assert_eq!(err.kind, ErrorKind::UnknownArgument); /// ``` + /// /// What's happening is `-o` is getting `val1`, and because delimiters are required yet none /// were present, it stops parsing `-o`. At this point it reaches `val2` and because no /// positional arguments have been defined, it's an error of an unexpected argument. @@ -3982,196 +3977,6 @@ impl<'help> Arg<'help> { } } - /// Specifies that the argument may have an unknown number of multiple values. Without any other - /// settings, this argument may appear only *once*. - /// - /// For example, `--opt val1 val2` is allowed, but `--opt val1 val2 --opt val3` is not. - /// - /// **NOTE:** Implicitly sets [`ArgSettings::TakesValue`] - /// - /// **WARNING:** - /// - /// Setting `MultipleValues` for an argument that takes a value, but with no other details can - /// be dangerous in some circumstances. Because multiple values are allowed, - /// `--option val1 val2 val3` is perfectly valid. Be careful when designing a CLI where - /// positional arguments are *also* expected as `clap` will continue parsing *values* until one - /// of the following happens: - /// - /// * It reaches the [maximum number of values] - /// * It reaches a [specific number of values] - /// * It finds another flag or option (i.e. something that starts with a `-`) - /// - /// **WARNING:** - /// - /// When using args with `MultipleValues` and [subcommands], one needs to consider the - /// possibility of an argument value being the same as a valid subcommand. By default `clap` will - /// parse the argument in question as a value *only if* a value is possible at that moment. - /// Otherwise it will be parsed as a subcommand. In effect, this means using `MultipleValues` with no - /// additional parameters and a value that coincides with a subcommand name, the subcommand - /// cannot be called unless another argument is passed between them. - /// - /// As an example, consider a CLI with an option `--ui-paths=...` and subcommand `signer` - /// - /// The following would be parsed as values to `--ui-paths`. - /// - /// ```text - /// $ program --ui-paths path1 path2 signer - /// ``` - /// - /// This is because `--ui-paths` accepts multiple values. `clap` will continue parsing values - /// 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* [`MultipleOccurrences`]. The following are all - /// valid, and `signer` is parsed as a subcommand in the first case, but a value in the second - /// case. - /// - /// ```text - /// $ program --ui-paths path1 signer - /// $ program --ui-paths path1 --ui-paths signer signer - /// ``` - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ArgSettings}; - /// Arg::new("debug") - /// .short('d') - /// .setting(ArgSettings::TakesValue) - /// .setting(ArgSettings::MultipleValues) - /// # ; - /// ``` - /// An example with flags - /// - /// ```rust - /// # use clap::{App, Arg, ArgSettings}; - /// let m = App::new("prog") - /// .arg(Arg::new("verbose") - /// .setting(ArgSettings::MultipleOccurrences) - /// .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::{App, Arg, ArgSettings}; - /// let m = App::new("prog") - /// .arg(Arg::new("file") - /// .setting(ArgSettings::TakesValue) - /// .setting(ArgSettings::MultipleValues) - /// .short('F')) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3" - /// ]); - /// - /// 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"]); - /// ``` - /// Although `MultipleVlaues` has been specified, we cannot use the argument more than once. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind, ArgSettings}; - /// let res = App::new("prog") - /// .arg(Arg::new("file") - /// .setting(ArgSettings::TakesValue) - /// .setting(ArgSettings::MultipleValues) - /// .short('F')) - /// .try_get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" - /// ]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage) - /// ``` - /// - /// A common mistake is to define an option which allows multiple values, and a positional - /// argument. - /// - /// ```rust - /// # use clap::{App, Arg, ArgSettings}; - /// let m = App::new("prog") - /// .arg(Arg::new("file") - /// .setting(ArgSettings::TakesValue) - /// .setting(ArgSettings::MultipleValues) - /// .short('F')) - /// .arg(Arg::new("word") - /// .index(1)) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3", "word" - /// ]); - /// - /// assert!(m.is_present("file")); - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3", "word"]); // wait...what?! - /// assert!(!m.is_present("word")); // but we clearly used word! - /// ``` - /// The problem is `clap` doesn't know when to stop parsing values for "files". This is further - /// compounded by if we'd said `word -F file1 file2` it would have worked fine, so it would - /// appear to only fail sometimes...not good! - /// - /// A solution for the example above is to limit how many values with a [maxium], or [specific] - /// number, or to say [`MultipleOccurrences`] is ok, but multiple values is not. - /// - /// ```rust - /// # use clap::{App, Arg, ArgSettings}; - /// let m = App::new("prog") - /// .arg(Arg::new("file") - /// .setting(ArgSettings::MultipleOccurrences) - /// .setting(ArgSettings::TakesValue) - /// .short('F')) - /// .arg(Arg::new("word") - /// .index(1)) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3", "word" - /// ]); - /// - /// assert!(m.is_present("file")); - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// assert!(m.is_present("word")); - /// assert_eq!(m.value_of("word"), Some("word")); - /// ``` - /// As a final example, let's fix the above error and get a pretty message to the user :) - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind, ArgSettings}; - /// let res = App::new("prog") - /// .arg(Arg::new("file") - /// .setting(ArgSettings::MultipleOccurrences) - /// .setting(ArgSettings::TakesValue) - /// .short('F')) - /// .arg(Arg::new("word") - /// .index(1)) - /// .try_get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3", "word" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - /// [option]: ArgSettings::TakesValue - /// [options]: ArgSettings::TakesValue - /// [subcommands]: App::subcommand() - /// [positionals]: Arg::index() - /// [`Arg::number_of_values(1)`]: Arg::number_of_values() - /// [`MultipleOccurrences`]: ArgSettings::MultipleOccurrences - /// [`MultipleValues`]: ArgSettings::MultipleValues - /// [maximum number of values]: Arg::max_values() - /// [specific number of values]: Arg::number_of_values() - /// [maximum]: Arg::max_values() - /// [specific]: Arg::number_of_values() - #[inline] - pub fn multiple(self, multi: bool) -> Self { - self.multiple_occurrences(multi).multiple_values(multi) - } - /// Don't allow an argument to accept explicitly empty values. An empty value /// must be specified at the command line with an explicit `""`, `''`, or /// `--option=` @@ -4234,7 +4039,177 @@ impl<'help> Arg<'help> { } } - /// @TODO@ @release @docs + /// Specifies that the argument may have an unknown number of multiple values. Without any other + /// settings, this argument may appear only *once*. + /// + /// For example, `--opt val1 val2` is allowed, but `--opt val1 val2 --opt val3` is not. + /// + /// **NOTE:** Setting this requires [`ArgSettings::TakesValue`]. + /// + /// **WARNING:** + /// + /// Setting `MultipleValues` for an argument that takes a value, but with no other details can + /// be dangerous in some circumstances. Because multiple values are allowed, + /// `--option val1 val2 val3` is perfectly valid. Be careful when designing a CLI where + /// positional arguments are *also* expected as `clap` will continue parsing *values* until one + /// of the following happens: + /// + /// * It reaches the [maximum number of values] + /// * It reaches a [specific number of values] + /// * It finds another flag or option (i.e. something that starts with a `-`) + /// + /// **WARNING:** + /// + /// When using args with `MultipleValues` and [`subcommands`], one needs to consider the + /// possibility of an argument value being the same as a valid subcommand. By default `clap` will + /// parse the argument in question as a value *only if* a value is possible at that moment. + /// Otherwise it will be parsed as a subcommand. In effect, this means using `MultipleValues` with no + /// additional parameters and a value that coincides with a subcommand name, the subcommand + /// cannot be called unless another argument is passed between them. + /// + /// As an example, consider a CLI with an option `--ui-paths=...` and subcommand `signer` + /// + /// The following would be parsed as values to `--ui-paths`. + /// + /// ```text + /// $ program --ui-paths path1 path2 signer + /// ``` + /// + /// This is because `--ui-paths` accepts multiple values. `clap` will continue parsing values + /// 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* [`MultipleOccurrences`]. The following are all + /// valid, and `signer` is parsed as a subcommand in the first case, but a value in the second + /// case. + /// + /// ```text + /// $ program --ui-paths path1 signer + /// $ program --ui-paths path1 --ui-paths signer signer + /// ``` + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::new("debug") + /// .short('d') + /// .setting(ArgSettings::TakesValue) + /// .setting(ArgSettings::MultipleValues); + /// ``` + /// + /// An example with options + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::new("file") + /// .setting(ArgSettings::TakesValue) + /// .setting(ArgSettings::MultipleValues) + /// .short('F')) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "file2", "file3" + /// ]); + /// + /// 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"]); + /// ``` + /// + /// Although `MultipleVlaues` has been specified, we cannot use the argument more than once. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::new("file") + /// .setting(ArgSettings::TakesValue) + /// .setting(ArgSettings::MultipleValues) + /// .short('F')) + /// .try_get_matches_from(vec![ + /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage) + /// ``` + /// + /// A common mistake is to define an option which allows multiple values, and a positional + /// argument. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::new("file") + /// .setting(ArgSettings::TakesValue) + /// .setting(ArgSettings::MultipleValues) + /// .short('F')) + /// .arg(Arg::new("word") + /// .index(1)) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "file2", "file3", "word" + /// ]); + /// + /// assert!(m.is_present("file")); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3", "word"]); // wait...what?! + /// assert!(!m.is_present("word")); // but we clearly used word! + /// ``` + /// + /// The problem is `clap` doesn't know when to stop parsing values for "files". This is further + /// compounded by if we'd said `word -F file1 file2` it would have worked fine, so it would + /// 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 [`MultipleOccurrences`] is ok, but multiple values is not. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::new("file") + /// .setting(ArgSettings::MultipleOccurrences) + /// .setting(ArgSettings::TakesValue) + /// .short('F')) + /// .arg(Arg::new("word") + /// .index(1)) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "-F", "file2", "-F", "file3", "word" + /// ]); + /// + /// assert!(m.is_present("file")); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// assert!(m.is_present("word")); + /// assert_eq!(m.value_of("word"), Some("word")); + /// ``` + /// + /// As a final example, let's fix the above error and get a pretty message to the user :) + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::new("file") + /// .setting(ArgSettings::MultipleOccurrences) + /// .setting(ArgSettings::TakesValue) + /// .short('F')) + /// .arg(Arg::new("word") + /// .index(1)) + /// .try_get_matches_from(vec![ + /// "prog", "-F", "file1", "file2", "file3", "word" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// ``` + /// + /// [`subcommands`]: App::subcommand() + /// [`Arg::number_of_values(1)`]: Arg::number_of_values() + /// [`MultipleOccurrences`]: ArgSettings::MultipleOccurrences + /// [`MultipleValues`]: ArgSettings::MultipleValues + /// [maximum number of values]: Arg::max_values() + /// [specific number of values]: Arg::number_of_values() + /// [maximum]: Arg::max_values() + /// [specific]: Arg::number_of_values() #[inline] pub fn multiple_values(self, multi: bool) -> Self { if multi { @@ -4244,8 +4219,7 @@ impl<'help> Arg<'help> { } } - /// Specifies that the argument may appear more than once. - /// For flags, this results + /// 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) @@ -4258,9 +4232,9 @@ impl<'help> Arg<'help> { /// # use clap::{App, Arg, ArgSettings}; /// Arg::new("debug") /// .short('d') - /// .setting(ArgSettings::MultipleOccurrences) - /// # ; + /// .setting(ArgSettings::MultipleOccurrences); /// ``` + /// /// An example with flags /// /// ```rust @@ -4295,17 +4269,6 @@ impl<'help> Arg<'help> { /// let files: Vec<_> = m.values_of("file").unwrap().collect(); /// assert_eq!(files, ["file1", "file2", "file3"]); /// ``` - /// [option]: ArgSettings::TakesValue - /// [options]: ArgSettings::TakesValue - /// [subcommands]: App::subcommand() - /// [positionals]: Arg::index() - /// [`Arg::number_of_values(1)`]: Arg::number_of_values() - /// [`MultipleOccurrences`]: ArgSettings::MultipleOccurrences - /// [`MultipleValues`]: ArgSettings::MultipleValues - /// [maximum number of values]: Arg::max_values() - /// [specific number of values]: Arg::number_of_values() - /// [maximum]: Arg::max_values() - /// [specific]: Arg::number_of_values() #[inline] pub fn multiple_occurrences(self, multi: bool) -> Self { if multi { @@ -4323,21 +4286,22 @@ impl<'help> Arg<'help> { /// ```text /// --foo something -- -v -v -v -b -b -b --baz -q -u -x /// ``` + /// /// Will result in everything after `--` to be considered one raw argument. This behavior /// may not be exactly what you are expecting and using [`AppSettings::TrailingVarArg`] /// may be more appropriate. /// - /// **NOTE:** Implicitly sets [`Arg::takes_value(true)`] [`Arg::multiple(true)`], [`Arg::allow_hyphen_values(true)`], and - /// [`Arg::last(true)`] when set to `true` + /// **NOTE:** Implicitly sets [`Arg::takes_value(true)`] [`Arg::multiple_values(true)`], + /// [`Arg::allow_hyphen_values(true)`], and [`Arg::last(true)`] when set to `true` /// /// [`Arg::takes_value(true)`]: Arg::takes_value() - /// [`Arg::multiple(true)`]: Arg::multiple() + /// [`Arg::multiple_values(true)`]: Arg::multiple_values() /// [`Arg::allow_hyphen_values(true)`]: Arg::allow_hyphen_values() /// [`Arg::last(true)`]: Arg::last() #[inline] pub fn raw(self, raw: bool) -> Self { self.takes_value(raw) - .multiple(raw) + .multiple_values(raw) .allow_hyphen_values(raw) .last(raw) } @@ -4354,9 +4318,9 @@ impl<'help> Arg<'help> { /// ```rust /// # use clap::{App, Arg}; /// Arg::new("debug") - /// .hidden_short_help(true) - /// # ; + /// .hidden_short_help(true); /// ``` + /// /// Setting `hidden_short_help(true)` will hide the argument when displaying short help text /// /// ```rust @@ -4555,16 +4519,17 @@ impl<'help> Arg<'help> { /// Currently this is only supported by the zsh completions generator. /// /// For example, to take a username as argument: + /// /// ``` /// # use clap::{Arg, ValueHint}; /// Arg::new("user") /// .short('u') /// .long("user") - /// .value_hint(ValueHint::Username) - /// # ; + /// .value_hint(ValueHint::Username); /// ``` /// /// To take a full command line and its arguments (for example, when writing a command wrapper): + /// /// ``` /// # use clap::{App, AppSettings, Arg, ValueHint}; /// App::new("prog") @@ -4572,10 +4537,9 @@ impl<'help> Arg<'help> { /// .arg( /// Arg::new("command") /// .takes_value(true) - /// .multiple(true) + /// .multiple_values(true) /// .value_hint(ValueHint::CommandWithArguments) - /// ) - /// # ; + /// ); /// ``` pub fn value_hint(mut self, value_hint: ValueHint) -> Self { self.settings.set(ArgSettings::TakesValue); @@ -4614,11 +4578,10 @@ impl<'help> Arg<'help> { // Used for positionals when printing pub(crate) fn multiple_str(&self) -> &str { - // FIXME: This should probably be > 1 - let mult_vals = self.val_names.len() < 2; + let mult_vals = self.val_names.len() > 1; if (self.is_set(ArgSettings::MultipleValues) || self.is_set(ArgSettings::MultipleOccurrences)) - && mult_vals + && !mult_vals { "..." } else { @@ -4810,11 +4773,13 @@ impl<'help> Display for Arg<'help> { if self.index.is_some() || (self.long.is_none() && self.short.is_none()) { // Positional let mut delim = String::new(); + delim.push(if self.is_set(ArgSettings::RequireDelimiter) { self.val_delim.expect(INTERNAL_ERROR_MSG) } else { ' ' }); + if !self.val_names.is_empty() { write!( f, @@ -4828,9 +4793,9 @@ impl<'help> Display for Arg<'help> { } else { write!(f, "<{}>", self.name)?; } - if self.settings.is_set(ArgSettings::MultipleValues) && self.val_names.len() < 2 { - write!(f, "...")?; - } + + write!(f, "{}", self.multiple_str())?; + return Ok(()); } else if !self.is_set(ArgSettings::TakesValue) { // Flag @@ -4842,17 +4807,20 @@ impl<'help> Display for Arg<'help> { return Ok(()); } + let sep = if self.is_set(ArgSettings::RequireEquals) { "=" } else { " " }; + // Write the name such --long or -l if let Some(l) = self.long { write!(f, "--{}{}", l, sep)?; } else { write!(f, "-{}{}", self.short.unwrap(), sep)?; } + let delim = if self.is_set(ArgSettings::RequireDelimiter) { self.val_delim.expect(INTERNAL_ERROR_MSG) } else { @@ -4861,25 +4829,29 @@ impl<'help> Display for Arg<'help> { // Write the values such as if !self.val_names.is_empty() { + let num = self.val_names.len(); let mut it = self.val_names.iter().peekable(); + while let Some((_, val)) = it.next() { write!(f, "<{}>", val)?; if it.peek().is_some() { write!(f, "{}", delim)?; } } - let num = self.val_names.len(); + if self.is_set(ArgSettings::MultipleValues) && num == 1 { write!(f, "...")?; } } else if let Some(num) = self.num_vals { let mut it = (0..num).peekable(); + while let Some(_) = it.next() { write!(f, "<{}>", self.name)?; if it.peek().is_some() { write!(f, "{}", delim)?; } } + if self.is_set(ArgSettings::MultipleValues) && num == 1 { write!(f, "...")?; } @@ -4888,7 +4860,7 @@ impl<'help> Display for Arg<'help> { f, "<{}>{}", self.name, - if self.is_set(ArgSettings::MultipleOccurrences) { + if self.is_set(ArgSettings::MultipleValues) { "..." } else { "" @@ -5026,11 +4998,21 @@ mod test { // Options #[test] - fn option_display1() { + fn option_display_multiple_occurrences() { let o = Arg::new("opt") .long("option") .takes_value(true) - .multiple(true); + .multiple_occurrences(true); + + assert_eq!(&*format!("{}", o), "--option "); + } + + #[test] + fn option_display_multiple_values() { + let o = Arg::new("opt") + .long("option") + .takes_value(true) + .multiple_values(true); assert_eq!(&*format!("{}", o), "--option ..."); } @@ -5047,7 +5029,7 @@ mod test { let o2 = Arg::new("opt") .short('o') .takes_value(true) - .multiple(true) + .multiple_values(true) .value_names(&["file", "name"]); assert_eq!(&*format!("{}", o2), "-o "); @@ -5098,7 +5080,7 @@ mod test { // Positionals #[test] - fn positiona_display_mult() { + fn positional_display_multiple_values() { let p = Arg::new("pos") .index(1) .setting(ArgSettings::TakesValue) @@ -5107,6 +5089,16 @@ mod test { assert_eq!(&*format!("{}", p), "..."); } + #[test] + fn positional_display_multiple_occurrences() { + let p = Arg::new("pos") + .index(1) + .setting(ArgSettings::TakesValue) + .setting(ArgSettings::MultipleOccurrences); + + assert_eq!(&*format!("{}", p), "..."); + } + #[test] fn positional_display_required() { let p2 = Arg::new("pos").index(1).setting(ArgSettings::Required); diff --git a/src/build/arg/value_hint.rs b/src/build/arg/value_hint.rs index 3dbd5bd8..34886a99 100644 --- a/src/build/arg/value_hint.rs +++ b/src/build/arg/value_hint.rs @@ -45,11 +45,11 @@ pub enum ValueHint { /// common when writing shell wrappers that execute anther command, for example `sudo` or `env`. /// /// This hint is special, the argument must be a positional argument and have - /// [`.multiple(true)`] and App must use [`AppSettings::TrailingVarArg`]. The result is that the + /// [`.multiple_values(true)`] and App must use [`AppSettings::TrailingVarArg`]. The result is that the /// command line `my_app ls -la /` will be parsed as `["ls", "-la", "/"]` and clap won't try to /// parse the `-la` argument itself. /// - /// [`.multiple(true)`]: Arg::multiple() + /// [`.multiple_values(true)`]: Arg::multiple_values() CommandWithArguments, /// Name of a local operating system user. Username, diff --git a/src/macros.rs b/src/macros.rs index 5b746db7..502f15f8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -246,7 +246,8 @@ macro_rules! app_from_crate { /// /// * A single hyphen followed by a character (such as `-c`) sets the [`Arg::short`] /// * A double hyphen followed by a character or word (such as `--config`) sets [`Arg::long`] -/// * Three dots (`...`) sets [`Arg::multiple(true)`] +/// * Three dots (`...`) sets [`Arg::multiple_values(true)`] +/// * Three dots (`...`) sets [`Arg::multiple_occurrences(true)`] /// * Angled brackets after either a short or long will set [`Arg::value_name`] and /// `Arg::required(true)` such as `--config ` = `Arg::value_name("FILE")` and /// `Arg::required(true)` @@ -287,7 +288,8 @@ macro_rules! app_from_crate { /// /// [`Arg::short`]: Arg::short() /// [`Arg::long`]: Arg::long() -/// [`Arg::multiple(true)`]: Arg::multiple() +/// [`Arg::multiple_values(true)`]: Arg::multiple_values() +/// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() /// [`Arg::value_name`]: Arg::value_name() /// [`Arg::min_values(min)`]: Arg::min_values() /// [`Arg::max_values(max)`]: Arg::max_values() @@ -461,7 +463,7 @@ macro_rules! clap_app { $crate::clap_app!{ @arg ($arg.value_name(stringify!($var))) (+) $($tail)* } }; (@arg ($arg:expr) $modes:tt ... $($tail:tt)*) => { - $crate::clap_app!{ @arg ($arg) $modes +multiple +takes_value $($tail)* } + $crate::clap_app!{ @arg ($arg) $modes +multiple_values +takes_value $($tail)* } }; // Shorthand magic (@arg ($arg:expr) $modes:tt #{$n:expr, $m:expr} $($tail:tt)*) => { diff --git a/src/parse/errors.rs b/src/parse/errors.rs index 82053148..80b22302 100644 --- a/src/parse/errors.rs +++ b/src/parse/errors.rs @@ -173,8 +173,6 @@ pub enum ErrorKind { /// # use clap::{App, Arg, ErrorKind}; /// let result = App::new("prog") /// .arg(Arg::new("arg") - /// .takes_value(true) - /// .multiple(true) /// .max_values(2)) /// .try_get_matches_from(vec!["prog", "too", "many", "values"]); /// assert!(result.is_err()); @@ -285,7 +283,7 @@ pub enum ErrorKind { /// let result = App::new("prog") /// .arg(Arg::new("debug") /// .long("debug") - /// .multiple(false)) + /// .multiple_occurrences(false)) /// .try_get_matches_from(vec!["prog", "--debug", "--debug"]); /// assert!(result.is_err()); /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage); diff --git a/src/parse/matches/arg_matches.rs b/src/parse/matches/arg_matches.rs index b715e47f..8d98e00a 100644 --- a/src/parse/matches/arg_matches.rs +++ b/src/parse/matches/arg_matches.rs @@ -40,8 +40,7 @@ pub(crate) struct SubCommand { /// .takes_value(true)) /// .arg(Arg::new("debug") /// .short('d') -/// .takes_value(true) -/// .multiple(true)) +/// .multiple_occurrences(true)) /// .arg(Arg::new("cfg") /// .short('c') /// .takes_value(true)) @@ -64,7 +63,7 @@ pub(crate) struct SubCommand { /// // 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(true) method, otherwise it will only return 1 or 0. +/// // 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"); /// } else { @@ -219,7 +218,7 @@ impl ArgMatches { /// # use clap::{App, Arg}; /// let m = App::new("myprog") /// .arg(Arg::new("output") - /// .multiple(true) + /// .multiple_values(true) /// .short('o') /// .takes_value(true)) /// .get_matches_from(vec![ @@ -683,8 +682,7 @@ impl ArgMatches { /// .short('z')) /// .arg(Arg::new("option") /// .short('o') - /// .takes_value(true) - /// .multiple(true)) + /// .takes_value(true)) /// .get_matches_from(vec!["myapp", "-fzFoval"]); /// // ARGV idices: ^0 ^1 /// // clap idices: ^1,2,3^5 @@ -704,9 +702,8 @@ impl ArgMatches { /// let m = App::new("myapp") /// .arg(Arg::new("option") /// .short('o') - /// .takes_value(true) /// .use_delimiter(true) - /// .multiple(true)) + /// .multiple_values(true)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV idices: ^0 ^1 /// // clap idices: ^2 ^3 ^4 @@ -744,9 +741,8 @@ impl ArgMatches { /// let m = App::new("myapp") /// .arg(Arg::new("option") /// .short('o') - /// .takes_value(true) /// .use_delimiter(true) - /// .multiple(true)) + /// .multiple_values(true)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV idices: ^0 ^1 /// // clap idices: ^2 ^3 ^4 @@ -764,7 +760,7 @@ impl ArgMatches { /// .arg(Arg::new("option") /// .short('o') /// .takes_value(true) - /// .multiple(true)) + /// .multiple_occurrences(true)) /// .arg(Arg::new("flag") /// .short('f') /// .multiple_occurrences(true)) @@ -787,7 +783,7 @@ impl ArgMatches { /// .arg(Arg::new("option") /// .short('o') /// .takes_value(true) - /// .multiple(true)) + /// .multiple_values(true)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV idices: ^0 ^1 /// // clap idices: ^2 @@ -972,7 +968,7 @@ impl ArgMatches { /// let m = App::new("myapp") /// .arg(Arg::new("output") /// .short('o') -/// .multiple(true) +/// .multiple_values(true) /// .takes_value(true)) /// .get_matches_from(vec!["myapp", "-o", "val1", "val2"]); /// @@ -1121,7 +1117,7 @@ impl Default for OsValues<'_> { /// let m = App::new("myapp") /// .arg(Arg::new("output") /// .short('o') -/// .multiple(true) +/// .multiple_values(true) /// .takes_value(true)) /// .get_matches_from(vec!["myapp", "-o", "val1", "val2"]); /// diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 27e1c6f5..d6fd399e 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -147,7 +147,7 @@ impl<'help, 'app> Parser<'help, 'app> { num_p ); - // Next we verify that only the highest index has a .multiple(true) (if any) + // Next we verify that only the highest index has a .multiple_values(true) (if any) let only_highest = |a: &Arg| { a.is_set(ArgSettings::MultipleValues) && (a.index.unwrap_or(0) != highest_idx) }; @@ -171,7 +171,7 @@ impl<'help, 'app> Parser<'help, 'app> { || last.is_set(ArgSettings::Last); assert!( ok, - "When using a positional argument with .multiple(true) that is *not the \ + "When using a positional argument with .multiple_values(true) that is *not the \ last* positional argument, the last positional argument (i.e. the one \ with the highest index) *must* have .required(true) or .last(true) set." ); @@ -182,7 +182,7 @@ impl<'help, 'app> Parser<'help, 'app> { assert!( ok, "Only the last positional argument, or second to last positional \ - argument may be set to .multiple(true)" + argument may be set to .multiple_values(true)" ); // Next we check how many have both Multiple and not a specific number of values set @@ -198,7 +198,7 @@ impl<'help, 'app> Parser<'help, 'app> { && count == 2); assert!( ok, - "Only one positional argument with .multiple(true) set is allowed per \ + "Only one positional argument with .multiple_values(true) set is allowed per \ command, unless the second one also has .last(true) set" ); } @@ -456,7 +456,7 @@ impl<'help, 'app> Parser<'help, 'app> { let is_second_to_last = pos_counter + 1 == positional_count; // The last positional argument, or second to last positional - // argument may be set to .multiple(true) + // argument may be set to .multiple_values(true) let low_index_mults = is_second_to_last && self.app.get_positionals().any(|a| { a.is_set(ArgSettings::MultipleValues) diff --git a/tests/delimiters.rs b/tests/delimiters.rs index 4873e7d4..3b4777b4 100644 --- a/tests/delimiters.rs +++ b/tests/delimiters.rs @@ -116,7 +116,7 @@ fn opt_eq_mult_def_delim() { Arg::new("option") .long("opt") .takes_value(true) - .multiple(true) + .multiple_values(true) .use_delimiter(true), ) .try_get_matches_from(vec!["", "--opt=val1,val2,val3"]); diff --git a/tests/env.rs b/tests/env.rs index 5717a2c6..636c4533 100644 --- a/tests/env.rs +++ b/tests/env.rs @@ -189,7 +189,7 @@ fn multiple_one() { .env("CLP_TEST_ENV_MO") .takes_value(true) .use_delimiter(true) - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec![""]); @@ -210,7 +210,7 @@ fn multiple_three() { .env("CLP_TEST_ENV_MULTI1") .takes_value(true) .use_delimiter(true) - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec![""]); @@ -233,7 +233,7 @@ fn multiple_no_delimiter() { Arg::from("[arg] 'some opt'") .env("CLP_TEST_ENV_MULTI2") .takes_value(true) - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec![""]); diff --git a/tests/flag_subcommands.rs b/tests/flag_subcommands.rs index 425c80d4..acb261de 100644 --- a/tests/flag_subcommands.rs +++ b/tests/flag_subcommands.rs @@ -202,13 +202,7 @@ fn flag_subcommand_short_after_long_arg() { .short_flag('S') .arg(Arg::new("clean").short('c')), ) - .arg( - Arg::new("arg") - .long("arg") - .takes_value(true) - .multiple(false) - .global(true), - ) + .arg(Arg::new("arg").long("arg").takes_value(true)) .get_matches_from(vec!["pacman", "--arg", "foo", "-Sc"]); let subm = m.subcommand_matches("sync"); assert!(subm.is_some()); diff --git a/tests/global_args.rs b/tests/global_args.rs index 308f21d8..4630d8bc 100644 --- a/tests/global_args.rs +++ b/tests/global_args.rs @@ -17,9 +17,8 @@ fn issue_1076() { Arg::new("GLOBAL_FLAG") .long("global-flag") .about("Specifies something needed by the subcommands") - .takes_value(true) - .multiple(true) - .global(true), + .global(true) + .takes_value(true), ) .subcommand(App::new("outer").subcommand(App::new("inner"))); let _ = app.try_get_matches_from_mut(vec!["myprog"]); diff --git a/tests/grouped_values.rs b/tests/grouped_values.rs index 0e1848e5..c49a22a2 100644 --- a/tests/grouped_values.rs +++ b/tests/grouped_values.rs @@ -9,7 +9,8 @@ fn grouped_value_works() { Arg::new("option") .long("option") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .get_matches_from(&[ "cli", @@ -39,7 +40,8 @@ fn issue_1026() { Arg::new("target") .long("target") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .get_matches_from(&[ "backup", "-s", "server", "-u", "user", "--target", "target1", "file1", "file2", @@ -65,7 +67,8 @@ fn grouped_value_long_flag_delimiter() { .long("option") .takes_value(true) .use_delimiter(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .get_matches_from(vec![ "myapp", @@ -93,7 +96,8 @@ fn grouped_value_short_flag_delimiter() { .short('o') .takes_value(true) .use_delimiter(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"]); let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); @@ -110,7 +114,8 @@ fn grouped_value_positional_arg() { Arg::new("pos") .about("multiple positionals") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .get_matches_from(vec![ "myprog", "val1", "val2", "val3", "val4", "val5", "val6", @@ -130,7 +135,8 @@ fn grouped_value_multiple_positional_arg() { Arg::new("pos2") .about("multiple positionals") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .get_matches_from(vec![ "myprog", "val1", "val2", "val3", "val4", "val5", "val6", @@ -150,7 +156,8 @@ fn grouped_value_multiple_positional_arg_last_multiple() { Arg::new("pos2") .about("multiple positionals") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .last(true), ) .get_matches_from(vec![ diff --git a/tests/help.rs b/tests/help.rs index 1fad729d..a79ab2c7 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -175,7 +175,7 @@ FLAGS: OPTIONS: -o, --opt tests options"; -// Using number_of_values(1) with multiple(true) misaligns help message +// Using number_of_values(1) with multiple_values(true) misaligns help message static ISSUE_760: &str = "ctest 0.1 USAGE: @@ -1213,7 +1213,7 @@ fn issue_702_multiple_values() { .arg( Arg::new("arg2") .takes_value(true) - .multiple(true) + .multiple_values(true) .about("some option"), ) .arg( @@ -1235,7 +1235,7 @@ fn issue_702_multiple_values() { .about("a label") .short('l') .long("label") - .multiple(true) + .multiple_values(true) .takes_value(true), ); assert!(utils::compare_output(app, "myapp --help", ISSUE_702, false)); @@ -1269,7 +1269,7 @@ fn issue_760() { .short('o') .long("option") .takes_value(true) - .multiple(true) + .multiple_values(true) .number_of_values(1), ) .arg( @@ -1520,7 +1520,7 @@ fn last_arg_mult_usage() { .arg( Arg::new("ARGS") .takes_value(true) - .multiple(true) + .multiple_values(true) .last(true) .about("some"), ); @@ -1536,7 +1536,7 @@ fn last_arg_mult_usage_req() { .arg( Arg::new("ARGS") .takes_value(true) - .multiple(true) + .multiple_values(true) .last(true) .required(true) .about("some"), @@ -1559,7 +1559,7 @@ fn last_arg_mult_usage_req_with_sc() { .arg( Arg::new("ARGS") .takes_value(true) - .multiple(true) + .multiple_values(true) .last(true) .required(true) .about("some"), @@ -1583,7 +1583,7 @@ fn last_arg_mult_usage_with_sc() { .arg( Arg::new("ARGS") .takes_value(true) - .multiple(true) + .multiple_values(true) .last(true) .about("some"), ) @@ -2000,7 +2000,7 @@ fn issue_1364_no_short_options() { Arg::new("files") .value_name("FILES") .takes_value(true) - .multiple(true), + .multiple_values(true), ); assert!(utils::compare_output(app, "demo -h", ISSUE_1364, false)); diff --git a/tests/indices.rs b/tests/indices.rs index 1702d4d3..94935957 100644 --- a/tests/indices.rs +++ b/tests/indices.rs @@ -9,13 +9,14 @@ fn indices_mult_opts() { Arg::new("exclude") .short('e') .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .arg( Arg::new("include") .short('i') .takes_value(true) - .multiple(true), + .multiple_values(true), ) .get_matches_from(vec!["ind", "-e", "A", "B", "-i", "B", "C", "-e", "C"]); @@ -36,13 +37,14 @@ fn index_mult_opts() { Arg::new("exclude") .short('e') .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .arg( Arg::new("include") .short('i') .takes_value(true) - .multiple(true), + .multiple_values(true), ) .get_matches_from(vec!["ind", "-e", "A", "B", "-i", "B", "C", "-e", "C"]); @@ -152,7 +154,7 @@ fn indices_mult_opt_value_delim_eq() { .short('o') .takes_value(true) .use_delimiter(true) - .multiple(true), + .multiple_values(true), ) .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); assert_eq!( @@ -168,7 +170,7 @@ fn indices_mult_opt_value_no_delim_eq() { Arg::new("option") .short('o') .takes_value(true) - .multiple(true), + .multiple_values(true), ) .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2]); diff --git a/tests/multiple_values.rs b/tests/multiple_values.rs index 0352ccb2..49a8ac57 100644 --- a/tests/multiple_values.rs +++ b/tests/multiple_values.rs @@ -8,7 +8,8 @@ fn option_long() { .long("option") .about("multiple options") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .try_get_matches_from(vec![ "", "--option", "val1", "--option", "val2", "--option", "val3", @@ -33,7 +34,8 @@ fn option_short() { .short('o') .about("multiple options") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2", "-o", "val3"]); @@ -57,7 +59,8 @@ fn option_mixed() { .short('o') .about("multiple options") .takes_value(true) - .multiple(true), + .multiple_values(true) + .multiple_occurrences(true), ) .try_get_matches_from(vec![ "", "-o", "val1", "--option", "val2", "--option", "val3", "-o", "val4", @@ -81,9 +84,8 @@ fn option_exact_exact() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .number_of_values(3), + .number_of_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2", "-o", "val3"]); @@ -105,7 +107,6 @@ fn option_exact_exact_not_mult() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) .number_of_values(3), ) .try_get_matches_from(vec!["", "-o", "val1", "val2", "val3"]); @@ -128,9 +129,8 @@ fn option_exact_exact_mult() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .number_of_values(3), + .number_of_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec![ "", "-o", "val1", "val2", "val3", "-o", "val4", "val5", "val6", @@ -154,9 +154,8 @@ fn option_exact_less() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .number_of_values(3), + .number_of_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2"]); @@ -171,9 +170,8 @@ fn option_exact_more() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .number_of_values(3), + .number_of_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec![ "", "-o", "val1", "-o", "val2", "-o", "val3", "-o", "val4", @@ -190,9 +188,8 @@ fn option_min_exact() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .min_values(3), + .min_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2", "-o", "val3"]); @@ -214,9 +211,8 @@ fn option_min_less() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .min_values(3), + .min_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2"]); @@ -232,9 +228,8 @@ fn option_short_min_more_mult_occurs() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .min_values(3), + .min_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec![ "", "pos", "-o", "val1", "-o", "val2", "-o", "val3", "-o", "val4", @@ -261,8 +256,6 @@ fn option_short_min_more_single_occur() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) .min_values(3), ) .try_get_matches_from(vec!["", "pos", "-o", "val1", "val2", "val3", "val4"]); @@ -287,9 +280,8 @@ fn option_max_exact() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .max_values(3), + .max_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2", "-o", "val3"]); @@ -311,9 +303,8 @@ fn option_max_less() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .max_values(3), + .max_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2"]); @@ -335,9 +326,8 @@ fn option_max_more() { Arg::new("option") .short('o') .about("multiple options") - .takes_value(true) - .multiple(true) - .max_values(3), + .max_values(3) + .multiple_occurrences(true), ) .try_get_matches_from(vec![ "", "-o", "val1", "-o", "val2", "-o", "val3", "-o", "val4", @@ -354,7 +344,7 @@ fn positional() { Arg::new("pos") .about("multiple positionals") .takes_value(true) - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec!["myprog", "val1", "val2", "val3"]); @@ -512,10 +502,8 @@ fn sep_long_equals() { .arg( Arg::new("option") .long("option") - .use_delimiter(true) .about("multiple options") - .takes_value(true) - .multiple(true), + .use_delimiter(true), ) .try_get_matches_from(vec!["", "--option=val1,val2,val3"]); @@ -536,10 +524,8 @@ fn sep_long_space() { .arg( Arg::new("option") .long("option") - .use_delimiter(true) .about("multiple options") - .takes_value(true) - .multiple(true), + .use_delimiter(true), ) .try_get_matches_from(vec!["", "--option", "val1,val2,val3"]); @@ -561,9 +547,7 @@ fn sep_short_equals() { Arg::new("option") .short('o') .about("multiple options") - .use_delimiter(true) - .takes_value(true) - .multiple(true), + .use_delimiter(true), ) .try_get_matches_from(vec!["", "-o=val1,val2,val3"]); @@ -585,9 +569,7 @@ fn sep_short_space() { Arg::new("option") .short('o') .about("multiple options") - .use_delimiter(true) - .takes_value(true) - .multiple(true), + .use_delimiter(true), ) .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); @@ -609,9 +591,7 @@ fn sep_short_no_space() { Arg::new("option") .short('o') .about("multiple options") - .use_delimiter(true) - .takes_value(true) - .multiple(true), + .use_delimiter(true), ) .try_get_matches_from(vec!["", "-oval1,val2,val3"]); @@ -632,9 +612,7 @@ fn sep_positional() { .arg( Arg::new("option") .about("multiple options") - .takes_value(true) - .use_delimiter(true) - .multiple(true), + .use_delimiter(true), ) .try_get_matches_from(vec!["", "val1,val2,val3"]); @@ -656,7 +634,6 @@ fn different_sep() { Arg::new("option") .long("option") .about("multiple options") - .takes_value(true) .value_delimiter(";"), ) .try_get_matches_from(vec!["", "--option=val1;val2;val3"]); @@ -719,6 +696,7 @@ fn no_sep_positional() { .arg( Arg::new("option") .about("multiple options") + .takes_value(true) .use_delimiter(false), ) .try_get_matches_from(vec!["", "val1,val2,val3"]); @@ -737,12 +715,16 @@ fn req_delimiter_long() { .arg( Arg::new("option") .long("option") - .multiple(true) + .multiple_values(true) .use_delimiter(true) - .require_delimiter(true) - .takes_value(true), + .require_delimiter(true), + ) + .arg( + Arg::new("args") + .takes_value(true) + .multiple_values(true) + .index(1), ) - .arg(Arg::new("args").takes_value(true).multiple(true).index(1)) .try_get_matches_from(vec!["", "--option", "val1", "val2", "val3"]); assert!(m.is_ok()); @@ -766,12 +748,16 @@ fn req_delimiter_long_with_equal() { .arg( Arg::new("option") .long("option") - .multiple(true) + .multiple_values(true) .use_delimiter(true) - .require_delimiter(true) - .takes_value(true), + .require_delimiter(true), + ) + .arg( + Arg::new("args") + .takes_value(true) + .multiple_values(true) + .index(1), ) - .arg(Arg::new("args").takes_value(true).multiple(true).index(1)) .try_get_matches_from(vec!["", "--option=val1", "val2", "val3"]); assert!(m.is_ok()); @@ -795,12 +781,16 @@ fn req_delimiter_short_with_space() { .arg( Arg::new("option") .short('o') - .multiple(true) + .multiple_values(true) .use_delimiter(true) - .require_delimiter(true) - .takes_value(true), + .require_delimiter(true), + ) + .arg( + Arg::new("args") + .takes_value(true) + .multiple_values(true) + .index(1), ) - .arg(Arg::new("args").takes_value(true).multiple(true).index(1)) .try_get_matches_from(vec!["", "-o", "val1", "val2", "val3"]); assert!(m.is_ok()); @@ -824,12 +814,16 @@ fn req_delimiter_short_with_no_space() { .arg( Arg::new("option") .short('o') - .multiple(true) + .multiple_values(true) .use_delimiter(true) - .require_delimiter(true) - .takes_value(true), + .require_delimiter(true), + ) + .arg( + Arg::new("args") + .takes_value(true) + .multiple_values(true) + .index(1), ) - .arg(Arg::new("args").takes_value(true).multiple(true).index(1)) .try_get_matches_from(vec!["", "-oval1", "val2", "val3"]); assert!(m.is_ok()); @@ -853,12 +847,16 @@ fn req_delimiter_short_with_equal() { .arg( Arg::new("option") .short('o') - .multiple(true) + .multiple_values(true) .use_delimiter(true) - .require_delimiter(true) - .takes_value(true), + .require_delimiter(true), + ) + .arg( + Arg::new("args") + .takes_value(true) + .multiple_values(true) + .index(1), ) - .arg(Arg::new("args").takes_value(true).multiple(true).index(1)) .try_get_matches_from(vec!["", "-o=val1", "val2", "val3"]); assert!(m.is_ok()); @@ -883,12 +881,17 @@ fn req_delimiter_complex() { Arg::new("option") .long("option") .short('o') - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) - .require_delimiter(true) - .takes_value(true), + .require_delimiter(true), + ) + .arg( + Arg::new("args") + .takes_value(true) + .multiple_values(true) + .index(1), ) - .arg(Arg::new("args").takes_value(true).multiple(true).index(1)) .try_get_matches_from(vec![ "", "val1", @@ -943,7 +946,7 @@ fn req_delimiter_complex() { #[cfg(debug_assertions)] #[test] #[should_panic = "When using a positional argument with \ -.multiple(true) that is *not the last* positional argument, the last \ +.multiple_values(true) that is *not the last* positional argument, the last \ positional argument (i.e. the one with the highest index) *must* have \ .required(true) or .last(true) set."] fn low_index_positional_not_required() { @@ -953,7 +956,7 @@ fn low_index_positional_not_required() { .index(1) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("target").index(2)) .try_get_matches_from(vec![""]); @@ -962,7 +965,7 @@ fn low_index_positional_not_required() { // This tests a programmer error and will only succeed with debug_assertions #[cfg(debug_assertions)] #[test] -#[should_panic = "Only one positional argument with .multiple(true) \ +#[should_panic = "Only one positional argument with .multiple_values(true) \ set is allowed per command, unless the second one also has .last(true) set"] fn low_index_positional_last_multiple_too() { let _ = App::new("lip") @@ -971,14 +974,14 @@ fn low_index_positional_last_multiple_too() { .index(1) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .arg( Arg::new("target") .index(2) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec![""]); } @@ -987,7 +990,7 @@ fn low_index_positional_last_multiple_too() { #[cfg(debug_assertions)] #[test] #[should_panic = "Only the last positional argument, or second to \ -last positional argument may be set to .multiple(true)"] +last positional argument may be set to .multiple_values(true)"] fn low_index_positional_too_far_back() { let _ = App::new("lip") .arg( @@ -995,7 +998,7 @@ fn low_index_positional_too_far_back() { .index(1) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("target").required(true).index(2)) .arg(Arg::new("target2").required(true).index(3)) @@ -1010,7 +1013,7 @@ fn low_index_positional() { .index(1) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("target").index(2).required(true)) .try_get_matches_from(vec!["lip", "file1", "file2", "file3", "target"]); @@ -1039,7 +1042,7 @@ fn low_index_positional_in_subcmd() { .index(1) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("target").index(2).required(true)), ) @@ -1068,7 +1071,7 @@ fn low_index_positional_with_option() { .required(true) .index(1) .takes_value(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("target").index(2).required(true)) .arg(Arg::new("opt").long("option").takes_value(true)) @@ -1099,7 +1102,7 @@ fn low_index_positional_with_flag() { .index(1) .takes_value(true) .required(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("target").index(2).required(true)) .arg(Arg::new("flg").long("flag")) @@ -1128,7 +1131,7 @@ fn multiple_value_terminator_option() { .short('f') .value_terminator(";") .takes_value(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("other")) .try_get_matches_from(vec!["lip", "-f", "val1", "val2", ";", "otherval"]); @@ -1154,7 +1157,7 @@ fn multiple_value_terminator_option_other_arg() { .short('f') .value_terminator(";") .takes_value(true) - .multiple(true), + .multiple_values(true), ) .arg(Arg::new("other")) .arg(Arg::new("flag").short('F')) @@ -1179,7 +1182,7 @@ fn multiple_vals_with_hyphen() { .arg( Arg::new("cmds") .takes_value(true) - .multiple(true) + .multiple_values(true) .allow_hyphen_values(true) .value_terminator(";"), ) @@ -1205,13 +1208,7 @@ fn multiple_vals_with_hyphen() { #[test] fn issue_1480_max_values_consumes_extra_arg_1() { let res = App::new("prog") - .arg( - Arg::new("field") - .takes_value(true) - .multiple(false) - .max_values(1) - .long("field"), - ) + .arg(Arg::new("field").max_values(1).long("field")) .arg(Arg::new("positional").required(true).index(1)) .try_get_matches_from(vec!["prog", "--field", "1", "file"]); @@ -1221,13 +1218,7 @@ fn issue_1480_max_values_consumes_extra_arg_1() { #[test] fn issue_1480_max_values_consumes_extra_arg_2() { let res = App::new("prog") - .arg( - Arg::new("field") - .takes_value(true) - .multiple(false) - .max_values(1) - .long("field"), - ) + .arg(Arg::new("field").max_values(1).long("field")) .try_get_matches_from(vec!["prog", "--field", "1", "2"]); assert!(res.is_err()); @@ -1237,13 +1228,7 @@ fn issue_1480_max_values_consumes_extra_arg_2() { #[test] fn issue_1480_max_values_consumes_extra_arg_3() { let res = App::new("prog") - .arg( - Arg::new("field") - .takes_value(true) - .multiple(false) - .max_values(1) - .long("field"), - ) + .arg(Arg::new("field").max_values(1).long("field")) .try_get_matches_from(vec!["prog", "--field", "1", "2", "3"]); assert!(res.is_err()); diff --git a/tests/positionals.rs b/tests/positionals.rs index 2dcb68e4..96f66ad6 100644 --- a/tests/positionals.rs +++ b/tests/positionals.rs @@ -107,7 +107,7 @@ fn positional_multiple() { Arg::new("positional") .index(1) .takes_value(true) - .multiple(true), + .multiple_values(true), ]) .try_get_matches_from(vec!["", "-f", "test1", "test2", "test3"]); assert!(r.is_ok(), "{:#?}", r); @@ -128,7 +128,7 @@ fn positional_multiple_3() { Arg::new("positional") .index(1) .takes_value(true) - .multiple(true), + .multiple_values(true), ]) .try_get_matches_from(vec!["", "test1", "test2", "test3", "--flag"]); assert!(r.is_ok(), "{:#?}", r); diff --git a/tests/possible_values.rs b/tests/possible_values.rs index 53ddeff5..4f175692 100644 --- a/tests/possible_values.rs +++ b/tests/possible_values.rs @@ -74,7 +74,7 @@ fn possible_values_of_positional_multiple() { .takes_value(true) .possible_value("test123") .possible_value("test321") - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec!["myprog", "test123", "test321"]); @@ -97,7 +97,7 @@ fn possible_values_of_positional_multiple_fail() { .takes_value(true) .possible_value("test123") .possible_value("test321") - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec!["myprog", "test123", "notest"]); @@ -150,7 +150,7 @@ fn possible_values_of_option_multiple() { .takes_value(true) .possible_value("test123") .possible_value("test321") - .multiple(true), + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "--option", "test123", "--option", "test321"]); @@ -174,7 +174,7 @@ fn possible_values_of_option_multiple_fail() { .takes_value(true) .possible_value("test123") .possible_value("test321") - .multiple(true), + .multiple_occurrences(true), ) .try_get_matches_from(vec!["", "--option", "test123", "--option", "notest"]); @@ -259,7 +259,7 @@ fn case_insensitive_multiple() { .takes_value(true) .possible_value("test123") .possible_value("test321") - .multiple(true) + .multiple_values(true) .case_insensitive(true), ) .try_get_matches_from(vec!["pv", "--option", "TeSt123", "teST123", "tESt321"]); @@ -281,7 +281,7 @@ fn case_insensitive_multiple_fail() { .takes_value(true) .possible_value("test123") .possible_value("test321") - .multiple(true), + .multiple_values(true), ) .try_get_matches_from(vec!["pv", "--option", "test123", "teST123", "test321"]); diff --git a/tests/subcommands.rs b/tests/subcommands.rs index f12cd826..ae5f9408 100644 --- a/tests/subcommands.rs +++ b/tests/subcommands.rs @@ -362,7 +362,12 @@ fn issue_1161_multiple_hyphen_hyphen() { let res = App::new("myprog") .arg(Arg::new("eff").short('f')) .arg(Arg::new("pea").short('p').takes_value(true)) - .arg(Arg::new("slop").takes_value(true).multiple(true).last(true)) + .arg( + Arg::new("slop") + .takes_value(true) + .multiple_values(true) + .last(true), + ) .try_get_matches_from(vec![ "-f", "-p=bob", From e5e20b389eeb6072e2416e8f4ab079e652119678 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Wed, 16 Jun 2021 08:27:04 +0100 Subject: [PATCH 2/2] Forbid multiple_occurrences for positional args --- src/build/arg/debug_asserts.rs | 11 ++++++++++- src/build/usage_parser.rs | 33 +++++++++------------------------ tests/grouped_values.rs | 7 ++----- tests/help.rs | 1 - tests/positionals.rs | 11 +++++++++++ 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/build/arg/debug_asserts.rs b/src/build/arg/debug_asserts.rs index 9b35c711..1deab32a 100644 --- a/src/build/arg/debug_asserts.rs +++ b/src/build/arg/debug_asserts.rs @@ -29,12 +29,21 @@ pub(crate) fn assert_arg(arg: &Arg) { if arg.index.is_some() { assert!( - arg.short.is_none() && arg.long.is_none(), + !arg.has_switch(), "Argument '{}' is a positional argument and can't have short or long name versions", arg.name ); } + // Positionals should not have multiple_occurrences + if arg.is_positional() { + assert!( + !arg.is_set(ArgSettings::MultipleOccurrences), + "Argument '{}' is a positional argument and can't be set as multiple occurrences", + arg.name + ); + } + if arg.is_set(ArgSettings::Required) { assert!( arg.default_vals.is_empty(), diff --git a/src/build/usage_parser.rs b/src/build/usage_parser.rs index 9ac5fff1..e5256d9d 100644 --- a/src/build/usage_parser.rs +++ b/src/build/usage_parser.rs @@ -73,6 +73,7 @@ impl<'help> UsageParser<'help> { // We had a positional and need to set mult vals too arg.settings.set(ArgSettings::TakesValue); arg.settings.set(ArgSettings::MultipleValues); + arg.settings.unset(ArgSettings::MultipleOccurrences); } debug!("UsageParser::parse: vals...{:?}", arg.val_names); arg @@ -1181,9 +1182,7 @@ mod test { let a = Arg::from("[pos]... 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some help info"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(!a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1194,9 +1193,7 @@ mod test { let a = Arg::from("[pos]... 'some help\' info'"); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some help' info"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(!a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1207,9 +1204,7 @@ mod test { let a = Arg::from("[pos]... 'some \'help\' info'"); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some 'help' info"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(!a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1223,9 +1218,7 @@ mod test { ); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some help\ninfo"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(!a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1239,9 +1232,7 @@ mod test { ); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some help' stuff\ninfo"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(!a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1252,9 +1243,7 @@ mod test { let a = Arg::from("... 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some help info"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1276,9 +1265,7 @@ mod test { fn pos_mult() { let a = Arg::from("[pos]..."); assert_eq!(a.name, "pos"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(!a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); @@ -1289,9 +1276,7 @@ mod test { let a = Arg::from("... @a 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.about.unwrap(), "some help info"); - assert!( - a.is_set(ArgSettings::MultipleValues) && a.is_set(ArgSettings::MultipleOccurrences) - ); + assert!(a.is_set(ArgSettings::MultipleValues)); assert!(a.is_set(ArgSettings::Required)); assert!(a.val_names.is_empty()); assert!(a.num_vals.is_none()); diff --git a/tests/grouped_values.rs b/tests/grouped_values.rs index c49a22a2..dd35ac01 100644 --- a/tests/grouped_values.rs +++ b/tests/grouped_values.rs @@ -114,8 +114,7 @@ fn grouped_value_positional_arg() { Arg::new("pos") .about("multiple positionals") .takes_value(true) - .multiple_values(true) - .multiple_occurrences(true), + .multiple_values(true), ) .get_matches_from(vec![ "myprog", "val1", "val2", "val3", "val4", "val5", "val6", @@ -135,8 +134,7 @@ fn grouped_value_multiple_positional_arg() { Arg::new("pos2") .about("multiple positionals") .takes_value(true) - .multiple_values(true) - .multiple_occurrences(true), + .multiple_values(true), ) .get_matches_from(vec![ "myprog", "val1", "val2", "val3", "val4", "val5", "val6", @@ -157,7 +155,6 @@ fn grouped_value_multiple_positional_arg_last_multiple() { .about("multiple positionals") .takes_value(true) .multiple_values(true) - .multiple_occurrences(true) .last(true), ) .get_matches_from(vec![ diff --git a/tests/help.rs b/tests/help.rs index a79ab2c7..39a56927 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -760,7 +760,6 @@ fn args_with_last_usage() { .about("Any arguments you wish to pass to the being profiled.") .setting(ArgSettings::TakesValue) .setting(ArgSettings::MultipleValues) - .setting(ArgSettings::MultipleOccurrences) .setting(ArgSettings::Last) .value_name("ARGS"), ); diff --git a/tests/positionals.rs b/tests/positionals.rs index 96f66ad6..d085b316 100644 --- a/tests/positionals.rs +++ b/tests/positionals.rs @@ -296,3 +296,14 @@ fn positional_arg_with_short() { .arg(Arg::new("arg").index(1).short('a')) .try_get_matches(); } + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument 'arg' is a positional argument and can't be set as multiple occurrences"] +fn positional_arg_with_multiple_occurrences() { + use clap::{App, Arg}; + + let _ = App::new("test") + .arg(Arg::new("arg").multiple_occurrences(true)) + .try_get_matches(); +}