From b47464955c5f8d3ded18099556c7c044d7c92656 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 29 Jul 2022 14:51:02 -0500 Subject: [PATCH 1/4] refactor: Switch to getter for number_of_values --- src/builder/arg.rs | 4 ++-- src/parser/validator.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 7425368fcec..70928cc3b8f 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -4531,7 +4531,7 @@ pub(crate) fn render_arg_val(arg: &Arg) -> String { let mut extra_values = false; debug_assert!(arg.is_takes_value_set()); - let num_vals = arg.num_vals.unwrap_or_else(|| { + let num_vals = arg.get_num_vals().unwrap_or_else(|| { if arg.is_multiple_values_set() { (1..).into() } else { @@ -4547,7 +4547,7 @@ pub(crate) fn render_arg_val(arg: &Arg) -> String { } rendered.push_str(&arg_name); } - extra_values |= arg.num_vals.is_none() && arg.is_multiple_values_set(); + extra_values |= arg.get_num_vals().is_none() && arg.is_multiple_values_set(); extra_values |= min < num_vals.max_values(); } else { debug_assert!(1 < val_names.len()); diff --git a/src/parser/validator.rs b/src/parser/validator.rs index 4f64215c58a..173c325e450 100644 --- a/src/parser/validator.rs +++ b/src/parser/validator.rs @@ -270,7 +270,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { )); } - if let Some(expected) = a.num_vals { + if let Some(expected) = a.get_num_vals() { if let Some(expected) = expected.num_values() { if expected != actual { debug!( From 39e5a86391c12cba06b2843492faea60763ca8ab Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 29 Jul 2022 14:54:31 -0500 Subject: [PATCH 2/4] fix: Update positional for new num_vals --- src/builder/debug_asserts.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs index 552000d93ab..42e5b1a7007 100644 --- a/src/builder/debug_asserts.rs +++ b/src/builder/debug_asserts.rs @@ -533,7 +533,9 @@ fn _verify_positionals(cmd: &Command) -> bool { // Next we check how many have both Multiple and not a specific number of values set let count = cmd .get_positionals() - .filter(|p| p.is_multiple_values_set() && p.num_vals.is_none()) + .filter(|p| { + p.is_multiple_values_set() && !p.num_vals.map(|r| r.is_fixed()).unwrap_or(false) + }) .count(); let ok = count <= 1 || (last.is_last_set() From e2a6bbfa95ed79590f405b440d49c223addf3cee Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 29 Jul 2022 15:04:03 -0500 Subject: [PATCH 3/4] refactor(parser): Simplify calculation for more values --- src/parser/arg_matcher.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/parser/arg_matcher.rs b/src/parser/arg_matcher.rs index a2599ea2e4a..436e9914301 100644 --- a/src/parser/arg_matcher.rs +++ b/src/parser/arg_matcher.rs @@ -197,20 +197,16 @@ impl ArgMatcher { } pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool { - let num_resolved = self.get(&o.id).map(|ma| ma.num_vals()).unwrap_or(0); let num_pending = self .pending .as_ref() .and_then(|p| (p.id == o.id).then(|| p.raw_vals.len())) .unwrap_or(0); - let current_num = num_resolved + num_pending; debug!( - "ArgMatcher::needs_more_vals: o={}, resolved={}, pending={}", - o.name, num_resolved, num_pending + "ArgMatcher::needs_more_vals: o={}, pending={}", + o.name, num_pending ); - if current_num == 0 { - true - } else if let Some(expected) = o.num_vals { + if let Some(expected) = o.get_num_vals() { debug!( "ArgMatcher::needs_more_vals: expected={}, actual={}", expected, num_pending From 470531b515a4fd1abf69fdeabaeeae19a67355db Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 29 Jul 2022 07:23:42 -0500 Subject: [PATCH 4/4] fix!: Replace `multiple_values` with `number_of_values` This reduces ambiguity in how the different "multiple" parts of the API interact and lowrs the amount of API surface area users have to dig through to use clap. For now, this is only a matter of cleaning up the public API. Cleaning up the implementation is the next step. --- CHANGELOG.md | 1 + clap_bench/benches/03_complex.rs | 15 +- clap_bench/benches/04_new_help.rs | 4 +- clap_bench/benches/05_ripgrep.rs | 2 +- clap_bench/benches/06_rustup.rs | 2 +- clap_complete/examples/completion.rs | 2 +- clap_complete/tests/common.rs | 4 +- clap_complete/tests/snapshots/basic.bash | 2 +- .../tests/snapshots/feature_sample.bash | 2 +- clap_complete/tests/snapshots/quoting.bash | 2 +- .../tests/snapshots/special_commands.bash | 2 +- .../tests/snapshots/sub_subcommands.bash | 4 +- clap_complete_fig/tests/common.rs | 4 +- clap_derive/src/derives/args.rs | 4 +- clap_mangen/tests/common.rs | 2 +- examples/escaped-positional.rs | 2 +- examples/pacman.rs | 8 +- src/builder/arg.rs | 210 ++---------------- src/builder/command.rs | 13 +- src/builder/debug_asserts.rs | 8 +- src/builder/value_hint.rs | 4 +- src/macros.rs | 2 +- src/parser/matches/arg_matches.rs | 20 +- tests/builder/app_settings.rs | 2 +- tests/builder/delimiters.rs | 4 +- tests/builder/env.rs | 6 +- tests/builder/flag_subcommands.rs | 12 +- tests/builder/grouped_values.rs | 18 +- tests/builder/groups.rs | 2 +- tests/builder/help.rs | 51 ++--- tests/builder/indices.rs | 12 +- tests/builder/multiple_occurrences.rs | 2 +- tests/builder/multiple_values.rs | 62 +++--- tests/builder/opts.rs | 10 +- tests/builder/positionals.rs | 6 +- tests/builder/possible_values.rs | 8 +- tests/builder/subcommands.rs | 2 +- tests/builder/utils.rs | 4 +- tests/derive/options.rs | 4 +- 39 files changed, 170 insertions(+), 354 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b247fd1cb32..f9ade3d6a89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `number_of_values(1)` no longer implies `multiple_values(true)` - Remove `Arg::min_values` (across all occurrences) with `Arg::number_of_values(N..)` (per occurrence) - Remove `Arg::max_values` (across all occurrences) with `Arg::number_of_values(1..=M)` (per occurrence) +- Remove `Arg::multiple_values(true)` with `Arg::number_of_values(1..)` and `Arg::multiple_values(false)` with `Arg::number_of_values(0)` - `ArgAction::SetTrue` and `ArgAction::SetFalse` now prioritize `Arg::default_missing_value` over their standard behavior - *(help)* Make `DeriveDisplayOrder` the default and removed the setting. To sort help, set `next_display_order(None)` (#2808) - *(help)* Subcommand display order respects `Command::next_display_order` instead of `DeriveDisplayOrder` and using its own initial display order value (#2808) diff --git a/clap_bench/benches/03_complex.rs b/clap_bench/benches/03_complex.rs index c22c279e957..bd9583f45a0 100644 --- a/clap_bench/benches/03_complex.rs +++ b/clap_bench/benches/03_complex.rs @@ -30,9 +30,9 @@ macro_rules! create_app { arg!(--multvals "Tests multiple values not mult occs").required(false).value_names(&["one", "two"]), arg!( --multvalsmo "Tests multiple values, not mult occs" - ).multiple_values(true).required(false).value_names(&["one", "two"]), - arg!(--minvals2 ... "Tests 2 min vals").number_of_values(2..).multiple_values(true).required(false), - arg!(--maxvals3 ... "Tests 3 max vals").number_of_values(1..=3).multiple_values(true).required(false), + ).required(false).value_names(&["one", "two"]), + arg!(--minvals2 ... "Tests 2 min vals").number_of_values(2..).required(false), + arg!(--maxvals3 ... "Tests 3 max vals").number_of_values(1..=3).required(false), ]) .subcommand( Command::new("subcmd") @@ -57,7 +57,7 @@ pub fn build_from_builder(c: &mut Criterion) { .help("tests options") .short('o') .long("option") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .arg(Arg::new("positional").help("tests positionals").index(1)) @@ -99,7 +99,7 @@ pub fn build_from_builder(c: &mut Criterion) { ) .arg( Arg::new("positional3") - .multiple_values(true) + .number_of_values(1..) .help("tests positionals with specific values") .index(4) .value_parser(POS3_VALS), @@ -113,7 +113,6 @@ pub fn build_from_builder(c: &mut Criterion) { .arg( Arg::new("multvalsmo") .long("multvalsmo") - .multiple_values(true) .action(ArgAction::Append) .help("Tests multiple values, not mult occs") .value_names(&["one", "two"]), @@ -121,7 +120,6 @@ pub fn build_from_builder(c: &mut Criterion) { .arg( Arg::new("minvals") .long("minvals2") - .multiple_values(true) .action(ArgAction::Append) .help("Tests 2 min vals") .number_of_values(2..), @@ -129,7 +127,6 @@ pub fn build_from_builder(c: &mut Criterion) { .arg( Arg::new("maxvals") .long("maxvals3") - .multiple_values(true) .action(ArgAction::Append) .help("Tests 3 max vals") .number_of_values(1..=3), @@ -143,7 +140,7 @@ pub fn build_from_builder(c: &mut Criterion) { Arg::new("scoption") .short('o') .long("option") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append) .help("tests options"), ) diff --git a/clap_bench/benches/04_new_help.rs b/clap_bench/benches/04_new_help.rs index 513c34374ec..cbc5d36cf31 100644 --- a/clap_bench/benches/04_new_help.rs +++ b/clap_bench/benches/04_new_help.rs @@ -118,7 +118,7 @@ fn app_example7<'c>() -> Command<'c> { .arg( Arg::new("input") .help("the input file to use") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append) .required(true) .short('i') @@ -135,7 +135,7 @@ fn app_example8<'c>() -> Command<'c> { .arg( Arg::new("input") .help("the input file to use") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append) .required(true) .short('i') diff --git a/clap_bench/benches/05_ripgrep.rs b/clap_bench/benches/05_ripgrep.rs index 3fd83b514e8..519471aa02a 100644 --- a/clap_bench/benches/05_ripgrep.rs +++ b/clap_bench/benches/05_ripgrep.rs @@ -323,7 +323,7 @@ where "type-list", "version", ])) - .arg(arg("path").multiple_values(true)) + .arg(arg("path").number_of_values(1..)) .arg( flag("regexp") .short('e') diff --git a/clap_bench/benches/06_rustup.rs b/clap_bench/benches/06_rustup.rs index 127c69f0151..96196411bd3 100644 --- a/clap_bench/benches/06_rustup.rs +++ b/clap_bench/benches/06_rustup.rs @@ -236,7 +236,7 @@ fn build_cli() -> Command<'static> { .after_help(RUN_HELP) .trailing_var_arg(true) .arg(Arg::new("toolchain").required(true)) - .arg(Arg::new("command").required(true).multiple_values(true)), + .arg(Arg::new("command").required(true).number_of_values(1..)), ) .subcommand( Command::new("which") diff --git a/clap_complete/examples/completion.rs b/clap_complete/examples/completion.rs index 5dd5b3f9361..e506222736e 100644 --- a/clap_complete/examples/completion.rs +++ b/clap_complete/examples/completion.rs @@ -68,7 +68,7 @@ fn build_cli() -> Command<'static> { ) .arg( Arg::new("command_with_args") - .multiple_values(true) + .number_of_values(1..) .value_hint(ValueHint::CommandWithArguments), ) .arg( diff --git a/clap_complete/tests/common.rs b/clap_complete/tests/common.rs index 13af2fcc55d..f1d0323883a 100644 --- a/clap_complete/tests/common.rs +++ b/clap_complete/tests/common.rs @@ -64,7 +64,7 @@ pub fn special_commands_command(name: &'static str) -> clap::Command<'static> { .require_equals(true) .help("the other case to test"), ) - .arg(clap::Arg::new("path").multiple_values(true)), + .arg(clap::Arg::new("path").number_of_values(1..)), ) .subcommand(clap::Command::new("some-cmd-with-hyphens").alias("hyphen")) .subcommand(clap::Command::new("some-hidden-cmd").hide(true)) @@ -220,7 +220,7 @@ pub fn value_hint_command(name: &'static str) -> clap::Command<'static> { .arg( clap::Arg::new("command_with_args") .action(clap::ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .value_hint(clap::ValueHint::CommandWithArguments), ) .arg( diff --git a/clap_complete/tests/snapshots/basic.bash b/clap_complete/tests/snapshots/basic.bash index 2a5b11c7069..0aa37b4cfd8 100644 --- a/clap_complete/tests/snapshots/basic.bash +++ b/clap_complete/tests/snapshots/basic.bash @@ -39,7 +39,7 @@ _my-app() { return 0 ;; my__app__help) - opts="-c ..." + opts="-c [...]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/clap_complete/tests/snapshots/feature_sample.bash b/clap_complete/tests/snapshots/feature_sample.bash index 98918ca47f3..af529269b62 100644 --- a/clap_complete/tests/snapshots/feature_sample.bash +++ b/clap_complete/tests/snapshots/feature_sample.bash @@ -39,7 +39,7 @@ _my-app() { return 0 ;; my__app__help) - opts="..." + opts="[...]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/clap_complete/tests/snapshots/quoting.bash b/clap_complete/tests/snapshots/quoting.bash index ead2b13b8bb..a4ae43a2009 100644 --- a/clap_complete/tests/snapshots/quoting.bash +++ b/clap_complete/tests/snapshots/quoting.bash @@ -138,7 +138,7 @@ _my-app() { return 0 ;; my__app__help) - opts="..." + opts="[...]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/clap_complete/tests/snapshots/special_commands.bash b/clap_complete/tests/snapshots/special_commands.bash index 33f99ad04a3..bb603b5b11b 100644 --- a/clap_complete/tests/snapshots/special_commands.bash +++ b/clap_complete/tests/snapshots/special_commands.bash @@ -48,7 +48,7 @@ _my-app() { return 0 ;; my__app__help) - opts="..." + opts="[...]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/clap_complete/tests/snapshots/sub_subcommands.bash b/clap_complete/tests/snapshots/sub_subcommands.bash index 85729508196..c96561de46d 100644 --- a/clap_complete/tests/snapshots/sub_subcommands.bash +++ b/clap_complete/tests/snapshots/sub_subcommands.bash @@ -45,7 +45,7 @@ _my-app() { return 0 ;; my__app__help) - opts="..." + opts="[...]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -73,7 +73,7 @@ _my-app() { return 0 ;; my__app__some_cmd__help) - opts="..." + opts="[...]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/clap_complete_fig/tests/common.rs b/clap_complete_fig/tests/common.rs index 13af2fcc55d..f1d0323883a 100644 --- a/clap_complete_fig/tests/common.rs +++ b/clap_complete_fig/tests/common.rs @@ -64,7 +64,7 @@ pub fn special_commands_command(name: &'static str) -> clap::Command<'static> { .require_equals(true) .help("the other case to test"), ) - .arg(clap::Arg::new("path").multiple_values(true)), + .arg(clap::Arg::new("path").number_of_values(1..)), ) .subcommand(clap::Command::new("some-cmd-with-hyphens").alias("hyphen")) .subcommand(clap::Command::new("some-hidden-cmd").hide(true)) @@ -220,7 +220,7 @@ pub fn value_hint_command(name: &'static str) -> clap::Command<'static> { .arg( clap::Arg::new("command_with_args") .action(clap::ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .value_hint(clap::ValueHint::CommandWithArguments), ) .arg( diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index 81de7a01764..689a2418ee5 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -267,7 +267,7 @@ pub fn gen_augment( if attrs.is_positional() { quote_spanned! { ty.span()=> .value_name(#value_name) - .multiple_values(true) // action won't be sufficient for getting multiple + .number_of_values(1..) // action won't be sufficient for getting multiple #value_parser #action } @@ -284,7 +284,7 @@ pub fn gen_augment( if attrs.is_positional() { quote_spanned! { ty.span()=> .value_name(#value_name) - .multiple_values(true) // action won't be sufficient for getting multiple + .number_of_values(1..) // action won't be sufficient for getting multiple #value_parser #action } diff --git a/clap_mangen/tests/common.rs b/clap_mangen/tests/common.rs index 55c5c5fcd6b..025ffef923e 100644 --- a/clap_mangen/tests/common.rs +++ b/clap_mangen/tests/common.rs @@ -216,7 +216,7 @@ pub fn value_hint_command(name: &'static str) -> clap::Command<'static> { .arg( clap::Arg::new("command_with_args") .action(clap::ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .value_hint(clap::ValueHint::CommandWithArguments), ) .arg( diff --git a/examples/escaped-positional.rs b/examples/escaped-positional.rs index 02420010022..e0505b9d339 100644 --- a/examples/escaped-positional.rs +++ b/examples/escaped-positional.rs @@ -11,7 +11,7 @@ fn main() { .arg( // Indicates that `slop` is only accessible after `--`. arg!(slop: [SLOP]) - .multiple_values(true) + .number_of_values(1..) .last(true) .value_parser(value_parser!(String)), ) diff --git a/examples/pacman.rs b/examples/pacman.rs index 6f02c30e0c8..f532bc68cce 100644 --- a/examples/pacman.rs +++ b/examples/pacman.rs @@ -22,7 +22,7 @@ fn main() { .help("search locally installed packages for matching strings") .conflicts_with("info") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg( Arg::new("info") @@ -31,7 +31,7 @@ fn main() { .conflicts_with("search") .help("view package information") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ), ) // Sync subcommand @@ -48,7 +48,7 @@ fn main() { .long("search") .conflicts_with("info") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .help("search remote repositories for matching strings"), ) .arg( @@ -64,7 +64,7 @@ fn main() { .help("packages") .required_unless_present("search") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ), ) .get_matches(); diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 70928cc3b8f..da037358d71 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -411,8 +411,8 @@ impl<'help> Arg<'help> { /// **NOTE:** This is only meant to be used for positional arguments and shouldn't to be used /// with [`Arg::short`] or [`Arg::long`]. /// - /// **NOTE:** When utilized with [`Arg::multiple_values(true)`], only the **last** positional argument - /// may be defined as multiple (i.e. with the highest index) + /// **NOTE:** When utilized with [`Arg::number_of_values(1..)`], only the **last** positional argument + /// may be defined as having a variable number of arguments (i.e. with the highest index) /// /// # Panics /// @@ -447,7 +447,7 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::short`]: Arg::short() /// [`Arg::long`]: Arg::long() - /// [`Arg::multiple_values(true)`]: Arg::multiple_values() + /// [`Arg::number_of_values(true)`]: Arg::number_of_values() /// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html /// [`Command`]: crate::Command #[inline] @@ -808,7 +808,7 @@ impl<'help> Arg<'help> { /// assert_eq!(m.get_one::("mode").unwrap(), "fast"); /// ``` /// [`Arg::value_delimiter(char)`]: Arg::value_delimiter() - /// [multiple values]: Arg::multiple_values + /// [multiple values]: Arg::number_of_values #[inline] #[must_use] pub fn takes_value(self, yes: bool) -> Self { @@ -905,170 +905,9 @@ impl<'help> Arg<'help> { self } - /// Specifies that the argument may have an unknown number of 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 [`Arg::takes_value`]. - /// - /// **WARNING:** - /// - /// Setting `multiple_values` 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 `-`) - /// - It reaches a [value terminator][Arg::value_terminator] - /// - /// Alternatively, [require a delimiter between values][Arg::require_value_delimiter]. - /// - /// **WARNING:** - /// - /// When using args with `multiple_values` 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 `multiple_values` 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* [`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. - /// - /// ```text - /// $ program --ui-paths path1 signer - /// $ program --ui-paths path1 --ui-paths signer signer - /// ``` - /// - /// # Examples - /// - /// An example with options - /// - /// ```rust - /// # use clap::{Command, Arg, ArgAction}; - /// let m = Command::new("prog") - /// .arg(Arg::new("file") - /// .action(ArgAction::Set) - /// .multiple_values(true) - /// .short('F')) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3" - /// ]); - /// - /// assert!(m.contains_id("file")); - /// let files: Vec<_> = m.get_many::("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// ``` - /// - /// Although `multiple_values` has been specified, the last argument still wins - /// - /// ```rust - /// # use clap::{Command, Arg, error::ErrorKind, ArgAction}; - /// let m = Command::new("prog") - /// .arg(Arg::new("file") - /// .action(ArgAction::Set) - /// .multiple_values(true) - /// .short('F')) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" - /// ]); - /// - /// assert!(m.contains_id("file")); - /// let files: Vec<_> = m.get_many::("file").unwrap().collect(); - /// assert_eq!(files, ["file3"]); - /// ``` - /// - /// A common mistake is to define an option which allows multiple values, and a positional - /// argument. - /// - /// ```rust - /// # use clap::{Command, Arg, ArgAction}; - /// let m = Command::new("prog") - /// .arg(Arg::new("file") - /// .action(ArgAction::Set) - /// .multiple_values(true) - /// .short('F')) - /// .arg(Arg::new("word")) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3", "word" - /// ]); - /// - /// assert!(m.contains_id("file")); - /// let files: Vec<_> = m.get_many::("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3", "word"]); // wait...what?! - /// assert!(!m.contains_id("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 [`ArgAction::Append`] is ok, but multiple values is not. - /// - /// ```rust - /// # use clap::{Command, Arg, ArgAction}; - /// let m = Command::new("prog") - /// .arg(Arg::new("file") - /// .action(ArgAction::Set) - /// .action(ArgAction::Append) - /// .short('F')) - /// .arg(Arg::new("word")) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3", "word" - /// ]); - /// - /// assert!(m.contains_id("file")); - /// let files: Vec<_> = m.get_many::("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// assert_eq!(m.get_one::("word").unwrap(), "word"); - /// ``` - /// - /// As a final example, let's fix the above error and get a pretty message to the user :) - /// - /// ```rust - /// # use clap::{Command, Arg, error::ErrorKind, ArgAction}; - /// let res = Command::new("prog") - /// .arg(Arg::new("file") - /// .action(ArgAction::Set) - /// .action(ArgAction::Append) - /// .short('F')) - /// .arg(Arg::new("word")) - /// .try_get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3", "word" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); - /// ``` - /// - /// [`subcommands`]: crate::Command::subcommand() - /// [`Arg::number_of_values(1)`]: Arg::number_of_values() - /// [maximum number of values]: Arg::number_of_values() - /// [specific number of values]: Arg::number_of_values() - /// [maximum]: Arg::number_of_values() - /// [specific]: Arg::number_of_values() #[inline] #[must_use] - pub fn multiple_values(self, yes: bool) -> Self { + fn multiple_values(self, yes: bool) -> Self { if yes { self.setting(ArgSettings::MultipleValues) } else { @@ -1222,7 +1061,6 @@ impl<'help> Arg<'help> { /// assert_eq!(m.get_one::("word").unwrap(), "word"); /// ``` /// [`Arg::value_delimiter(char)`]: Arg::value_delimiter() - /// [multiple values]: Arg::multiple_values #[inline] #[must_use] pub fn number_of_values(mut self, qty: impl Into) -> Self { @@ -1298,7 +1136,7 @@ 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:** implicitly sets [`Arg::action(ArgAction::Set)`] and [`Arg::multiple_values(true)`]. + /// **NOTE:** implicitly sets [`Arg::action(ArgAction::Set)`] and [`Arg::number_of_values(1..)`]. /// /// # Examples /// @@ -1336,7 +1174,7 @@ impl<'help> Arg<'help> { /// [`Arg::next_line_help(true)`]: Arg::next_line_help() /// [`Arg::number_of_values`]: Arg::number_of_values() /// [`Arg::action(ArgAction::Set)`]: Arg::takes_value() - /// [`Arg::multiple_values(true)`]: Arg::multiple_values() + /// [`Arg::number_of_values(1..)`]: Arg::number_of_values() #[must_use] pub fn value_names(mut self, names: &[&'help str]) -> Self { self.val_names = names.to_vec(); @@ -1368,7 +1206,7 @@ impl<'help> Arg<'help> { /// .arg( /// Arg::new("command") /// .action(ArgAction::Set) - /// .multiple_values(true) + /// .number_of_values(1..) /// .value_hint(ValueHint::CommandWithArguments) /// ); /// ``` @@ -1417,7 +1255,7 @@ impl<'help> Arg<'help> { /// .long("option") /// .action(ArgAction::Set) /// .ignore_case(true) - /// .multiple_values(true) + /// .number_of_values(1..) /// .value_parser(["test123", "test321"])) /// .get_matches_from(vec![ /// "pv", "--option", "TeSt123", "teST123", "tESt321" @@ -1441,7 +1279,7 @@ impl<'help> Arg<'help> { /// **NOTE:** Setting this requires [`Arg::takes_value`] /// /// **WARNING**: Take caution when using this setting combined with - /// [`Arg::multiple_values`], as this becomes ambiguous `$ prog --arg -- -- val`. All + /// [`Arg::number_of_values`], as this becomes ambiguous `$ prog --arg -- -- val`. All /// three `--, --, val` will be values when the user may have thought the second `--` would /// constitute the normal, "Only positional args follow" idiom. /// @@ -1666,7 +1504,7 @@ impl<'help> Arg<'help> { /// .action(ArgAction::Set) /// .use_value_delimiter(true) /// .require_value_delimiter(true) - /// .multiple_values(true)) + /// .number_of_values(1..)) /// .get_matches_from(vec![ /// "prog", "-o", "val1,val2,val3", /// ]); @@ -1707,7 +1545,7 @@ impl<'help> Arg<'help> { /// .arg(Arg::new("opt") /// .short('o') /// .action(ArgAction::Set) - /// .multiple_values(true)) + /// .number_of_values(1..)) /// .get_matches_from(vec![ /// "prog", "-o", "val1", "val2", "val3", /// ]); @@ -1728,7 +1566,7 @@ impl<'help> Arg<'help> { /// Sentinel to **stop** parsing multiple values of a given argument. /// /// By default when - /// one sets [`multiple_values(true)`] on an argument, clap will continue parsing values for that + /// one sets [`number_of_values(1..)`] 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 [`number_of_values`]). /// @@ -1743,7 +1581,7 @@ impl<'help> Arg<'help> { /// # use clap::{Command, Arg, ArgAction}; /// Arg::new("vals") /// .action(ArgAction::Set) - /// .multiple_values(true) + /// .number_of_values(1..) /// .value_terminator(";") /// # ; /// ``` @@ -1756,7 +1594,7 @@ impl<'help> Arg<'help> { /// let m = Command::new("prog") /// .arg(Arg::new("cmds") /// .action(ArgAction::Set) - /// .multiple_values(true) + /// .number_of_values(1..) /// .allow_hyphen_values(true) /// .value_terminator(";")) /// .arg(Arg::new("location")) @@ -1769,7 +1607,7 @@ impl<'help> Arg<'help> { /// ``` /// [options]: Arg::takes_value() /// [positional arguments]: Arg::index() - /// [`multiple_values(true)`]: Arg::multiple_values() + /// [`number_of_values(1..)`]: Arg::number_of_values() /// [`number_of_values`]: Arg::number_of_values() #[inline] #[must_use] @@ -1793,11 +1631,11 @@ impl<'help> Arg<'help> { /// may not be exactly what you are expecting and using [`crate::Command::trailing_var_arg`] /// may be more appropriate. /// - /// **NOTE:** Implicitly sets [`Arg::action(ArgAction::Set)`] [`Arg::multiple_values(true)`], + /// **NOTE:** Implicitly sets [`Arg::action(ArgAction::Set)`] [`Arg::number_of_values(1..)`], /// [`Arg::allow_hyphen_values(true)`], and [`Arg::last(true)`] when set to `true`. /// /// [`Arg::action(ArgAction::Set)`]: Arg::takes_value() - /// [`Arg::multiple_values(true)`]: Arg::multiple_values() + /// [`Arg::number_of_values(1..)`]: Arg::number_of_values() /// [`Arg::allow_hyphen_values(true)`]: Arg::allow_hyphen_values() /// [`Arg::last(true)`]: Arg::last() #[inline] @@ -2170,7 +2008,7 @@ impl<'help> Arg<'help> { /// .long("flag") /// .env("MY_FLAG_MULTI") /// .action(ArgAction::Set) - /// .multiple_values(true) + /// .number_of_values(1..) /// .use_value_delimiter(true)) /// .get_matches_from(vec![ /// "prog" @@ -4131,7 +3969,7 @@ impl<'help> Arg<'help> { self.is_set(ArgSettings::Required) } - /// Report whether [`Arg::multiple_values`] is set + /// Report whether [`Arg::number_of_values`] allows multiple values pub fn is_multiple_values_set(&self) -> bool { self.is_set(ArgSettings::MultipleValues) } @@ -4659,7 +4497,7 @@ mod test { let mut o = Arg::new("opt") .long("option") .action(ArgAction::Set) - .multiple_values(true); + .number_of_values(1..); o._build(); assert_eq!(o.to_string(), "--option ..."); @@ -4737,12 +4575,12 @@ mod test { fn option_display3() { let mut o = Arg::new("opt") .short('o') - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Set) .value_names(&["file", "name"]); o._build(); - assert_eq!(o.to_string(), "-o "); + assert_eq!(o.to_string(), "-o ..."); } #[test] @@ -4795,7 +4633,7 @@ mod test { #[test] fn positional_display_multiple_values() { - let mut p = Arg::new("pos").index(1).multiple_values(true); + let mut p = Arg::new("pos").index(1).number_of_values(1..); p._build(); assert_eq!(p.to_string(), "..."); diff --git a/src/builder/command.rs b/src/builder/command.rs index 9d25a79ff7f..2187d727086 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -2016,8 +2016,7 @@ impl<'help> Command<'help> { /// /// The values of the trailing positional argument will contain all args from itself on. /// - /// **NOTE:** The final positional argument **must** have [`Arg::multiple_values(true)`] or the usage - /// string equivalent. + /// **NOTE:** The final positional argument **must** have [`Arg::number_of_values(..)`]. /// /// # Examples /// @@ -2031,7 +2030,7 @@ impl<'help> Command<'help> { /// let trail: Vec<_> = m.get_many::("cmd").unwrap().collect(); /// assert_eq!(trail, ["arg1", "-r", "val1"]); /// ``` - /// [`Arg::multiple_values(true)`]: crate::Arg::multiple_values() + /// [`Arg::number_of_values(true)`]: crate::Arg::number_of_values() pub fn trailing_var_arg(self, yes: bool) -> Self { if yes { self.setting(AppSettings::TrailingVarArg) @@ -2117,7 +2116,7 @@ impl<'help> Command<'help> { /// .allow_missing_positional(true) /// .arg(Arg::new("foo")) /// .arg(Arg::new("bar")) - /// .arg(Arg::new("baz").action(ArgAction::Set).multiple_values(true)) + /// .arg(Arg::new("baz").action(ArgAction::Set).number_of_values(1..)) /// .get_matches_from(vec![ /// "prog", "foo", "bar", "baz1", "baz2", "baz3" /// ]); @@ -2136,7 +2135,7 @@ impl<'help> Command<'help> { /// .allow_missing_positional(true) /// .arg(Arg::new("foo")) /// .arg(Arg::new("bar")) - /// .arg(Arg::new("baz").action(ArgAction::Set).multiple_values(true)) + /// .arg(Arg::new("baz").action(ArgAction::Set).number_of_values(1..)) /// .get_matches_from(vec![ /// "prog", "--", "baz1", "baz2", "baz3" /// ]); @@ -2832,7 +2831,7 @@ impl<'help> Command<'help> { /// let cmd = Command::new("cmd").subcommand(Command::new("sub")).arg( /// Arg::new("arg") /// .long("arg") - /// .multiple_values(true) + /// .number_of_values(1..) /// .action(ArgAction::Set), /// ); /// @@ -4255,7 +4254,7 @@ To change `help`s short, call `cmd.arg(Arg::new(\"help\")...)`.", Arg::new("subcommand") .index(1) .action(ArgAction::Append) - .multiple_values(true) + .number_of_values(..) .value_name("SUBCOMMAND") .help("The subcommand whose help message to display"), ); diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs index 42e5b1a7007..ae4bbf74b82 100644 --- a/src/builder/debug_asserts.rs +++ b/src/builder/debug_asserts.rs @@ -131,7 +131,7 @@ pub(crate) fn assert_app(cmd: &Command) { panic!( "Command {}: Argument '{}' has the same index as '{}' \ and they are both positional arguments\n\n\t \ - Use Arg::multiple_values(true) to allow one \ + Use Arg::number_of_values(1..) to allow one \ positional argument to take multiple values", cmd.get_name(), first.name, @@ -517,7 +517,7 @@ fn _verify_positionals(cmd: &Command) -> bool { || last.is_last_set(); assert!( ok, - "When using a positional argument with .multiple_values(true) that is *not the \ + "When using a positional argument with .number_of_values(1..) 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." ); @@ -527,7 +527,7 @@ fn _verify_positionals(cmd: &Command) -> bool { assert!( ok, "Only the last positional argument, or second to last positional \ - argument may be set to .multiple_values(true)" + argument may be set to .number_of_values(1..)" ); // Next we check how many have both Multiple and not a specific number of values set @@ -544,7 +544,7 @@ fn _verify_positionals(cmd: &Command) -> bool { && count == 2); assert!( ok, - "Only one positional argument with .multiple_values(true) set is allowed per \ + "Only one positional argument with .number_of_values(1..) set is allowed per \ command, unless the second one also has .last(true) set" ); } diff --git a/src/builder/value_hint.rs b/src/builder/value_hint.rs index 7c35d1eb3e5..5097f1aff1b 100644 --- a/src/builder/value_hint.rs +++ b/src/builder/value_hint.rs @@ -48,12 +48,12 @@ 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_values(true)`] and Command must use [`Command::trailing_var_arg(true)`]. The result is that the + /// [`.number_of_values(1..)`] and Command must use [`Command::trailing_var_arg(true)`]. 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. /// /// [`Command::trailing_var_arg(true)`]: crate::Command::trailing_var_arg - /// [`.multiple_values(true)`]: crate::Arg::multiple_values() + /// [`.number_of_values(1..)`]: crate::Arg::number_of_values() CommandWithArguments, /// Name of a local operating system user. Username, diff --git a/src/macros.rs b/src/macros.rs index 426189e6199..ca13ffa60d6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -412,7 +412,7 @@ macro_rules! arg_impl { @arg ({ if $arg.get_long().is_none() && $arg.get_short().is_none() { - $arg.multiple_values(true) + $arg.number_of_values(1..) // Allow collecting arguments interleaved with flags .action($crate::ArgAction::Append) } else if $arg.is_takes_value_set() { diff --git a/src/parser/matches/arg_matches.rs b/src/parser/matches/arg_matches.rs index c2b6f2e8135..c4ce0e9838b 100644 --- a/src/parser/matches/arg_matches.rs +++ b/src/parser/matches/arg_matches.rs @@ -263,9 +263,8 @@ impl ArgMatches { /// let mut m = Command::new("myprog") /// .arg(Arg::new("file") /// .action(ArgAction::Append) - /// .multiple_values(true) - /// .required(true) - /// .action(ArgAction::Set)) + /// .number_of_values(1..) + /// .required(true)) /// .get_matches_from(vec![ /// "myprog", "file1.txt", "file2.txt", "file3.txt", "file4.txt", /// ]); @@ -545,7 +544,7 @@ impl ArgMatches { /// .arg(Arg::new("option") /// .short('o') /// .use_value_delimiter(true) - /// .multiple_values(true)) + /// .number_of_values(1..)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV indices: ^0 ^1 /// // clap indices: ^2 ^3 ^4 @@ -586,8 +585,7 @@ impl ArgMatches { /// let m = Command::new("myapp") /// .arg(Arg::new("option") /// .short('o') - /// .use_value_delimiter(true) - /// .multiple_values(true)) + /// .use_value_delimiter(true)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV indices: ^0 ^1 /// // clap indices: ^2 ^3 ^4 @@ -628,7 +626,7 @@ impl ArgMatches { /// .arg(Arg::new("option") /// .short('o') /// .action(ArgAction::Set) - /// .multiple_values(true)) + /// .number_of_values(1..)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV indices: ^0 ^1 /// // clap indices: ^2 @@ -1328,7 +1326,7 @@ impl<'a> Default for GroupedValues<'a> { /// let m = Command::new("myapp") /// .arg(Arg::new("output") /// .short('o') -/// .multiple_values(true) +/// .number_of_values(1..) /// .action(ArgAction::Set)) /// .get_matches_from(vec!["myapp", "-o", "val1", "val2"]); /// @@ -1424,7 +1422,7 @@ mod tests { .arg( crate::Arg::new("POTATO") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .required(true), ) .try_get_matches_from(["test", "one"]) @@ -1441,7 +1439,7 @@ mod tests { .arg( crate::Arg::new("POTATO") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .value_parser(crate::builder::ValueParser::os_string()) .required(true), ) @@ -1459,7 +1457,7 @@ mod tests { .arg( crate::Arg::new("POTATO") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .required(true), ) .try_get_matches_from(["test", "one"]) diff --git a/tests/builder/app_settings.rs b/tests/builder/app_settings.rs index 23bf93eb9ae..46e49d81433 100644 --- a/tests/builder/app_settings.rs +++ b/tests/builder/app_settings.rs @@ -1173,7 +1173,7 @@ fn aaos_opts_mult() { let res = Command::new("posix") .arg( arg!(--opt ... "some option") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(vec![ diff --git a/tests/builder/delimiters.rs b/tests/builder/delimiters.rs index 78f416bfc2d..91c296e52d5 100644 --- a/tests/builder/delimiters.rs +++ b/tests/builder/delimiters.rs @@ -87,7 +87,7 @@ fn opt_s_no_space_mult_no_delim() { Arg::new("option") .short('o') .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); @@ -108,7 +108,7 @@ fn opt_eq_mult_def_delim() { Arg::new("option") .long("opt") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true), ) .try_get_matches_from(vec!["", "--opt=val1,val2,val3"]); diff --git a/tests/builder/env.rs b/tests/builder/env.rs index 71184d9259b..ffc09f1fa90 100644 --- a/tests/builder/env.rs +++ b/tests/builder/env.rs @@ -230,7 +230,7 @@ fn multiple_one() { .env("CLP_TEST_ENV_MO") .action(ArgAction::Set) .use_value_delimiter(true) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec![""]); @@ -256,7 +256,7 @@ fn multiple_three() { .env("CLP_TEST_ENV_MULTI1") .action(ArgAction::Set) .use_value_delimiter(true) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec![""]); @@ -281,7 +281,7 @@ fn multiple_no_delimiter() { arg!([arg] "some opt") .env("CLP_TEST_ENV_MULTI2") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec![""]); diff --git a/tests/builder/flag_subcommands.rs b/tests/builder/flag_subcommands.rs index 0b53597dd84..04fbe56e727 100644 --- a/tests/builder/flag_subcommands.rs +++ b/tests/builder/flag_subcommands.rs @@ -526,7 +526,7 @@ fn flag_subcommand_long_short_normal_usage_string() { .help("search locally installed packages for matching strings") .conflicts_with("info") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg( Arg::new("info") @@ -535,7 +535,7 @@ fn flag_subcommand_long_short_normal_usage_string() { .conflicts_with("search") .help("view package information") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ), ); utils::assert_output(cmd, "pacman -Qh", FLAG_SUBCOMMAND_HELP, false); @@ -574,7 +574,7 @@ fn flag_subcommand_long_normal_usage_string() { .help("search locally installed packages for matching strings") .conflicts_with("info") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg( Arg::new("info") @@ -583,7 +583,7 @@ fn flag_subcommand_long_normal_usage_string() { .conflicts_with("search") .help("view package information") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ), ); utils::assert_output( @@ -627,7 +627,7 @@ fn flag_subcommand_short_normal_usage_string() { .help("search locally installed packages for matching strings") .conflicts_with("info") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg( Arg::new("info") @@ -636,7 +636,7 @@ fn flag_subcommand_short_normal_usage_string() { .conflicts_with("search") .help("view package information") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ), ); utils::assert_output( diff --git a/tests/builder/grouped_values.rs b/tests/builder/grouped_values.rs index d740a2ff6bc..56646630119 100644 --- a/tests/builder/grouped_values.rs +++ b/tests/builder/grouped_values.rs @@ -9,7 +9,7 @@ fn grouped_value_works() { Arg::new("option") .long("option") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(&[ @@ -41,7 +41,7 @@ fn issue_1026() { Arg::new("target") .long("target") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(&[ @@ -69,7 +69,7 @@ fn grouped_value_long_flag_delimiter() { .long("option") .action(ArgAction::Set) .use_value_delimiter(true) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(vec![ @@ -99,7 +99,7 @@ fn grouped_value_short_flag_delimiter() { .short('o') .action(ArgAction::Set) .use_value_delimiter(true) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"]) @@ -118,7 +118,7 @@ fn grouped_value_positional_arg() { Arg::new("pos") .help("multiple positionals") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec![ "myprog", "val1", "val2", "val3", "val4", "val5", "val6", @@ -139,7 +139,7 @@ fn grouped_value_multiple_positional_arg() { Arg::new("pos2") .help("multiple positionals") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec![ "myprog", "val1", "val2", "val3", "val4", "val5", "val6", @@ -160,7 +160,7 @@ fn grouped_value_multiple_positional_arg_last_multiple() { Arg::new("pos2") .help("multiple positionals") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true), ) .try_get_matches_from(vec![ @@ -177,7 +177,7 @@ fn grouped_value_multiple_positional_arg_last_multiple() { #[test] fn grouped_interleaved_positional_values() { let cmd = clap::Command::new("foo") - .arg(clap::Arg::new("pos").multiple_values(true)) + .arg(clap::Arg::new("pos").number_of_values(1..)) .arg( clap::Arg::new("flag") .short('f') @@ -200,7 +200,7 @@ fn grouped_interleaved_positional_values() { #[test] fn grouped_interleaved_positional_occurrences() { let cmd = clap::Command::new("foo") - .arg(clap::Arg::new("pos").multiple_values(true)) + .arg(clap::Arg::new("pos").number_of_values(1..)) .arg( clap::Arg::new("flag") .short('f') diff --git a/tests/builder/groups.rs b/tests/builder/groups.rs index c009ab09056..5b193c9c609 100644 --- a/tests/builder/groups.rs +++ b/tests/builder/groups.rs @@ -133,7 +133,7 @@ fn group_required_flags_empty() { #[test] fn group_multi_value_single_arg() { let res = Command::new("group") - .arg(arg!(-c --color "some option").multiple_values(true)) + .arg(arg!(-c --color "some option").number_of_values(1..)) .arg(arg!(-h --hostname "another option").required(false)) .group(ArgGroup::new("grp").args(&["hostname", "color"])) .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]); diff --git a/tests/builder/help.rs b/tests/builder/help.rs index 45d5f158d02..73a6e87899d 100644 --- a/tests/builder/help.rs +++ b/tests/builder/help.rs @@ -67,7 +67,7 @@ fn help_multi_subcommand_error() { -o --option "tests options" ) .required(false) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ), ), @@ -107,13 +107,13 @@ OPTIONS: .arg( Arg::new("FIRST") .help("First") - .multiple_values(true) + .number_of_values(1..) .required(true), ) .arg( Arg::new("SECOND") .help("Second") - .multiple_values(true) + .number_of_values(1..) .required(true) .last(true), ); @@ -172,7 +172,7 @@ OPTIONS: Arg::new("pass through args") .help("Any arguments you wish to pass to the being profiled.") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true) .value_name("ARGS"), ); @@ -342,7 +342,7 @@ fn multi_level_sc_help() { -o --option "tests options" ) .required(false) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ), ), @@ -981,7 +981,7 @@ OPTIONS: .arg( Arg::new("arg2") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .help("some option"), ) .arg( @@ -1003,7 +1003,7 @@ OPTIONS: .help("a label") .short('l') .long("label") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Set), ); utils::assert_output(cmd, "myapp --help", ISSUE_702, false); @@ -1043,23 +1043,6 @@ OPTIONS: utils::assert_output(cmd, "myapp --help", LONG_ABOUT, false); } -#[test] -#[should_panic = "Argument option: mismatch between `number_of_values` (1) and `multiple_values`"] -fn number_of_values_conflicts_with_multiple_values() { - Command::new("ctest") - .version("0.1") - .arg( - Arg::new("option") - .help("tests options") - .short('o') - .long("option") - .action(ArgAction::Set) - .number_of_values(1) - .multiple_values(true), - ) - .build(); -} - static RIPGREP_USAGE: &str = "ripgrep 0.5 USAGE: @@ -1359,7 +1342,7 @@ OPTIONS: .arg( Arg::new("ARGS") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true) .help("some"), ); @@ -1390,7 +1373,7 @@ OPTIONS: .arg( Arg::new("ARGS") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true) .required(true) .help("some"), @@ -1428,7 +1411,7 @@ SUBCOMMANDS: .arg( Arg::new("ARGS") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true) .required(true) .help("some"), @@ -1467,7 +1450,7 @@ SUBCOMMANDS: .arg( Arg::new("ARGS") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true) .help("some"), ) @@ -1882,7 +1865,7 @@ OPTIONS: Arg::new("files") .value_name("FILES") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ); utils::assert_output(cmd, "demo -h", ISSUE_1364, false); @@ -2411,7 +2394,7 @@ fn missing_positional_final_multiple() { .allow_missing_positional(true) .arg(Arg::new("foo")) .arg(Arg::new("bar")) - .arg(Arg::new("baz").action(ArgAction::Set).multiple_values(true)); + .arg(Arg::new("baz").action(ArgAction::Set).number_of_values(1..)); utils::assert_output( cmd, "test --help", @@ -2438,7 +2421,7 @@ fn positional_multiple_values_is_dotted() { Arg::new("foo") .required(true) .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ); utils::assert_output( cmd, @@ -2462,7 +2445,7 @@ OPTIONS: .required(true) .action(ArgAction::Set) .value_name("BAR") - .multiple_values(true), + .number_of_values(1..), ); utils::assert_output( cmd, @@ -2488,7 +2471,7 @@ fn positional_multiple_occurrences_is_dotted() { Arg::new("foo") .required(true) .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ); utils::assert_output( @@ -2513,7 +2496,7 @@ OPTIONS: .required(true) .action(ArgAction::Set) .value_name("BAR") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ); utils::assert_output( diff --git a/tests/builder/indices.rs b/tests/builder/indices.rs index 5b5f0369648..b670ab63d05 100644 --- a/tests/builder/indices.rs +++ b/tests/builder/indices.rs @@ -7,14 +7,14 @@ fn indices_mult_opts() { Arg::new("exclude") .short('e') .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .arg( Arg::new("include") .short('i') .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["ind", "-e", "A", "B", "-i", "B", "C", "-e", "C"]) .unwrap(); @@ -36,14 +36,14 @@ fn index_mult_opts() { Arg::new("exclude") .short('e') .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .arg( Arg::new("include") .short('i') .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["ind", "-e", "A", "B", "-i", "B", "C", "-e", "C"]) .unwrap(); @@ -136,7 +136,7 @@ fn indices_mult_opt_value_delim_eq() { .short('o') .action(ArgAction::Set) .use_value_delimiter(true) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["myapp", "-o=val1,val2,val3"]) .unwrap(); @@ -153,7 +153,7 @@ fn indices_mult_opt_value_no_delim_eq() { Arg::new("option") .short('o') .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["myapp", "-o=val1,val2,val3"]) .unwrap(); diff --git a/tests/builder/multiple_occurrences.rs b/tests/builder/multiple_occurrences.rs index ae520001fbd..9c3d939d801 100644 --- a/tests/builder/multiple_occurrences.rs +++ b/tests/builder/multiple_occurrences.rs @@ -30,7 +30,7 @@ fn multiple_occurrences_of_flags_short() { fn multiple_occurrences_of_positional() { let cmd = Command::new("test").arg( Arg::new("multi") - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ); diff --git a/tests/builder/multiple_values.rs b/tests/builder/multiple_values.rs index 5b11141f9cc..2e764315e7a 100644 --- a/tests/builder/multiple_values.rs +++ b/tests/builder/multiple_values.rs @@ -8,7 +8,7 @@ fn option_long() { .long("option") .help("multiple options") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(vec![ @@ -36,7 +36,7 @@ fn option_short() { .short('o') .help("multiple options") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(vec!["", "-o", "val1", "-o", "val2", "-o", "val3"]); @@ -63,7 +63,7 @@ fn option_mixed() { .short('o') .help("multiple options") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .try_get_matches_from(vec![ @@ -448,7 +448,7 @@ fn positional() { Arg::new("pos") .help("multiple positionals") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["myprog", "val1", "val2", "val3"]); @@ -875,14 +875,14 @@ fn req_delimiter_long() { .arg( Arg::new("option") .long("option") - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true) .require_value_delimiter(true), ) .arg( Arg::new("args") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .index(1), ) .try_get_matches_from(vec!["", "--option", "val1", "val2", "val3"]); @@ -913,14 +913,14 @@ fn req_delimiter_long_with_equal() { .arg( Arg::new("option") .long("option") - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true) .require_value_delimiter(true), ) .arg( Arg::new("args") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .index(1), ) .try_get_matches_from(vec!["", "--option=val1", "val2", "val3"]); @@ -951,14 +951,14 @@ fn req_delimiter_short_with_space() { .arg( Arg::new("option") .short('o') - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true) .require_value_delimiter(true), ) .arg( Arg::new("args") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .index(1), ) .try_get_matches_from(vec!["", "-o", "val1", "val2", "val3"]); @@ -989,14 +989,14 @@ fn req_delimiter_short_with_no_space() { .arg( Arg::new("option") .short('o') - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true) .require_value_delimiter(true), ) .arg( Arg::new("args") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .index(1), ) .try_get_matches_from(vec!["", "-oval1", "val2", "val3"]); @@ -1027,14 +1027,14 @@ fn req_delimiter_short_with_equal() { .arg( Arg::new("option") .short('o') - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true) .require_value_delimiter(true), ) .arg( Arg::new("args") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .index(1), ) .try_get_matches_from(vec!["", "-o=val1", "val2", "val3"]); @@ -1066,12 +1066,12 @@ fn req_delimiter_complex() { Arg::new("option") .long("option") .short('o') - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append) .use_value_delimiter(true) .require_value_delimiter(true), ) - .arg(Arg::new("args").multiple_values(true).index(1)) + .arg(Arg::new("args").number_of_values(1..).index(1)) .try_get_matches_from(vec![ "", "val1", @@ -1131,7 +1131,7 @@ fn req_delimiter_complex() { #[cfg(debug_assertions)] #[test] #[should_panic = "When using a positional argument with \ -.multiple_values(true) that is *not the last* positional argument, the last \ +.number_of_values(1..) 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() { @@ -1141,7 +1141,7 @@ fn low_index_positional_not_required() { .index(1) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("target").index(2)) .try_get_matches_from(vec![""]); @@ -1150,7 +1150,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_values(true) \ +#[should_panic = "Only one positional argument with .number_of_values(1..) \ set is allowed per command, unless the second one also has .last(true) set"] fn low_index_positional_last_multiple_too() { let _ = Command::new("lip") @@ -1159,14 +1159,14 @@ fn low_index_positional_last_multiple_too() { .index(1) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .arg( Arg::new("target") .index(2) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec![""]); } @@ -1175,7 +1175,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_values(true)"] +last positional argument may be set to .number_of_values(1..)"] fn low_index_positional_too_far_back() { let _ = Command::new("lip") .arg( @@ -1183,7 +1183,7 @@ fn low_index_positional_too_far_back() { .index(1) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("target").required(true).index(2)) .arg(Arg::new("target2").required(true).index(3)) @@ -1198,7 +1198,7 @@ fn low_index_positional() { .index(1) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("target").index(2).required(true)) .try_get_matches_from(vec!["lip", "file1", "file2", "file3", "target"]); @@ -1231,7 +1231,7 @@ fn low_index_positional_in_subcmd() { .index(1) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("target").index(2).required(true)), ) @@ -1264,7 +1264,7 @@ fn low_index_positional_with_option() { .required(true) .index(1) .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("target").index(2).required(true)) .arg(Arg::new("opt").long("option").action(ArgAction::Set)) @@ -1302,7 +1302,7 @@ fn low_index_positional_with_flag() { .index(1) .action(ArgAction::Set) .required(true) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("target").index(2).required(true)) .arg(Arg::new("flg").long("flag").action(ArgAction::SetTrue)) @@ -1333,7 +1333,7 @@ fn low_index_positional_with_extra_flags() { .arg(Arg::new("yes").long("yes").action(ArgAction::SetTrue)) .arg(Arg::new("one").long("one").action(ArgAction::Set)) .arg(Arg::new("two").long("two").action(ArgAction::Set)) - .arg(Arg::new("input").multiple_values(true).required(true)) + .arg(Arg::new("input").number_of_values(1..).required(true)) .arg(Arg::new("output").required(true)); let m = cmd.try_get_matches_from([ "test", "--one", "1", "--two", "2", "3", "4", "5", "6", "7", "8", @@ -1370,7 +1370,7 @@ fn multiple_value_terminator_option() { .short('f') .value_terminator(";") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("other")) .try_get_matches_from(vec!["lip", "-f", "val1", "val2", ";", "otherval"]); @@ -1401,7 +1401,7 @@ fn multiple_value_terminator_option_other_arg() { .short('f') .value_terminator(";") .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ) .arg(Arg::new("other")) .arg(Arg::new("flag").short('F').action(ArgAction::SetTrue)) @@ -1432,7 +1432,7 @@ fn multiple_vals_with_hyphen() { .arg( Arg::new("cmds") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .allow_hyphen_values(true) .value_terminator(";"), ) diff --git a/tests/builder/opts.rs b/tests/builder/opts.rs index 0c6ba9f0708..92916731c99 100644 --- a/tests/builder/opts.rs +++ b/tests/builder/opts.rs @@ -177,7 +177,7 @@ fn opts_using_short() { #[test] fn lots_o_vals() { let r = Command::new("opts") - .arg(arg!(o: -o "some opt").multiple_values(true)) + .arg(arg!(o: -o "some opt").number_of_values(1..)) .try_get_matches_from(vec![ "", "-o", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", @@ -338,7 +338,7 @@ fn multiple_vals_pos_arg_delim() { let r = Command::new("mvae") .arg( arg!(o: -o "some opt") - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true), ) .arg(arg!([file] "some file")) @@ -380,7 +380,7 @@ fn require_delims() { let r = Command::new("mvae") .arg( arg!(o: -o "some opt") - .multiple_values(true) + .number_of_values(1..) .use_value_delimiter(true) .require_value_delimiter(true), ) @@ -408,7 +408,7 @@ fn leading_hyphen_pass() { let r = Command::new("mvae") .arg( arg!(o: -o "some opt") - .multiple_values(true) + .number_of_values(1..) .allow_hyphen_values(true), ) .try_get_matches_from(vec!["", "-o", "-2", "3"]); @@ -439,7 +439,7 @@ fn leading_hyphen_with_flag_after() { let r = Command::new("mvae") .arg( arg!(o: -o "some opt") - .multiple_values(true) + .number_of_values(1..) .allow_hyphen_values(true), ) .arg(arg!(f: -f "some flag").action(ArgAction::SetTrue)) diff --git a/tests/builder/positionals.rs b/tests/builder/positionals.rs index 7490a238b7d..41e24d15ed7 100644 --- a/tests/builder/positionals.rs +++ b/tests/builder/positionals.rs @@ -121,7 +121,7 @@ fn positional_multiple() { Arg::new("positional") .index(1) .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ]) .try_get_matches_from(vec!["", "-f", "test1", "test2", "test3"]); assert!(r.is_ok(), "{:#?}", r); @@ -145,7 +145,7 @@ fn positional_multiple_3() { Arg::new("positional") .index(1) .action(ArgAction::Set) - .multiple_values(true), + .number_of_values(1..), ]) .try_get_matches_from(vec!["", "test1", "test2", "test3", "--flag"]); assert!(r.is_ok(), "{:#?}", r); @@ -330,7 +330,7 @@ fn ignore_hyphen_values_on_last() { let cmd = clap::Command::new("foo") .arg( clap::Arg::new("cmd") - .multiple_values(true) + .number_of_values(1..) .last(true) .allow_hyphen_values(true), ) diff --git a/tests/builder/possible_values.rs b/tests/builder/possible_values.rs index a65f75a1d7a..a0e17a7ce0d 100644 --- a/tests/builder/possible_values.rs +++ b/tests/builder/possible_values.rs @@ -90,7 +90,7 @@ fn possible_values_of_positional_multiple() { .index(1) .action(ArgAction::Set) .value_parser(["test123", "test321"]) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["myprog", "test123", "test321"]); @@ -115,7 +115,7 @@ fn possible_values_of_positional_multiple_fail() { .index(1) .action(ArgAction::Set) .value_parser(["test123", "test321"]) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["myprog", "test123", "notest"]); @@ -394,7 +394,7 @@ fn ignore_case_multiple() { .long("option") .action(ArgAction::Set) .value_parser(["test123", "test321"]) - .multiple_values(true) + .number_of_values(1..) .ignore_case(true), ) .try_get_matches_from(vec!["pv", "--option", "TeSt123", "teST123", "tESt321"]); @@ -419,7 +419,7 @@ fn ignore_case_multiple_fail() { .long("option") .action(ArgAction::Set) .value_parser(["test123", "test321"]) - .multiple_values(true), + .number_of_values(1..), ) .try_get_matches_from(vec!["pv", "--option", "test123", "teST123", "test321"]); diff --git a/tests/builder/subcommands.rs b/tests/builder/subcommands.rs index 879d92cb79f..cb7d7bb873f 100644 --- a/tests/builder/subcommands.rs +++ b/tests/builder/subcommands.rs @@ -304,7 +304,7 @@ fn issue_1161_multiple_hyphen_hyphen() { .arg( Arg::new("slop") .action(ArgAction::Set) - .multiple_values(true) + .number_of_values(1..) .last(true), ) .try_get_matches_from(vec![ diff --git a/tests/builder/utils.rs b/tests/builder/utils.rs index 55ab1ebe774..9cf31c1a304 100644 --- a/tests/builder/utils.rs +++ b/tests/builder/utils.rs @@ -49,7 +49,7 @@ pub fn complex_app() -> Command<'static> { -o --option "tests options" ) .required(false) - .multiple_values(true) + .number_of_values(1..) .action(ArgAction::Append), ) .arg(arg!([positional] "tests positionals")) @@ -100,7 +100,7 @@ pub fn complex_app() -> Command<'static> { .arg( arg!(-o --option "tests options") .required(false) - .multiple_values(true), + .number_of_values(1..), ) .arg(arg!(-s --subcmdarg "tests other args").required(false)) .arg(arg!([scpositional] "tests positionals")), diff --git a/tests/derive/options.rs b/tests/derive/options.rs index 1ebbf6d7a97..d41dab2dc33 100644 --- a/tests/derive/options.rs +++ b/tests/derive/options.rs @@ -369,7 +369,7 @@ fn vec_type_with_required() { fn vec_type_with_multiple_values_only() { #[derive(Parser, PartialEq, Debug)] struct Opt { - #[clap(short, long, multiple_values(true))] + #[clap(short, long, number_of_values(1..))] arg: Vec, } assert_eq!( @@ -429,7 +429,7 @@ fn option_vec_type() { fn option_vec_type_structopt_behavior() { #[derive(Parser, PartialEq, Debug)] struct Opt { - #[clap(short, long, multiple_values(true), number_of_values(0..))] + #[clap(short, long, number_of_values(0..))] arg: Option>, } assert_eq!(