fix!: Replaced min_values (tota) with number_of_values (per occurrence)

This commit is contained in:
Ed Page 2022-07-28 21:40:40 -05:00
parent ccf35ff70c
commit 30f5b11d06
8 changed files with 47 additions and 115 deletions

View file

@ -23,7 +23,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `Arg::number_of_values` now applies per occurrence rather than the average across all occurrences
- `number_of_values(0)` no longer implies `takes_value(true).multiple_values(true)`
- `number_of_values(1)` no longer implies `multiple_values(true)`
- Remove `Arg::min_values` (across all occurrences) with `Arg::number_of_values` (per occurrence)
- 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)
- `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)

View file

@ -32,7 +32,7 @@ macro_rules! create_app {
--multvalsmo <s> "Tests multiple values, not mult occs"
).multiple_values(true).required(false).value_names(&["one", "two"]),
arg!(--minvals2 <minvals> ... "Tests 2 min vals").number_of_values(2..).multiple_values(true).required(false),
arg!(--maxvals3 <maxvals> ... "Tests 3 max vals").max_values(3).multiple_values(true).required(false),
arg!(--maxvals3 <maxvals> ... "Tests 3 max vals").number_of_values(1..=3).multiple_values(true).required(false),
])
.subcommand(
Command::new("subcmd")
@ -132,7 +132,7 @@ pub fn build_from_builder(c: &mut Criterion) {
.multiple_values(true)
.action(ArgAction::Append)
.help("Tests 3 max vals")
.max_values(3),
.number_of_values(1..=3),
)
.subcommand(
Command::new("subcmd")

View file

@ -74,7 +74,6 @@ pub struct Arg<'help> {
pub(crate) disp_ord: Option<usize>,
pub(crate) val_names: Vec<&'help str>,
pub(crate) num_vals: Option<ValuesRange>,
pub(crate) max_vals: Option<usize>,
pub(crate) val_delim: Option<char>,
pub(crate) default_vals: Vec<&'help OsStr>,
pub(crate) default_vals_ifs: Vec<(Id, ArgPredicate<'help>, Option<&'help OsStr>)>,
@ -1066,9 +1065,9 @@ impl<'help> Arg<'help> {
///
/// [`subcommands`]: crate::Command::subcommand()
/// [`Arg::number_of_values(1)`]: Arg::number_of_values()
/// [maximum number of values]: Arg::max_values()
/// [maximum number of values]: Arg::number_of_values()
/// [specific number of values]: Arg::number_of_values()
/// [maximum]: Arg::max_values()
/// [maximum]: Arg::number_of_values()
/// [specific]: Arg::number_of_values()
#[inline]
#[must_use]
@ -1236,63 +1235,6 @@ impl<'help> Arg<'help> {
.multiple_values(qty.is_multiple())
}
/// The *maximum* number of values are for this argument.
///
/// For example, if you had a
/// `-f <file>` 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.
///
/// # Examples
///
/// ```rust
/// # use clap::{Command, Arg};
/// Arg::new("file")
/// .short('f')
/// .max_values(3);
/// ```
///
/// Supplying less than the maximum number of values is allowed
///
/// ```rust
/// # use clap::{Command, Arg, ArgAction};
/// let res = Command::new("prog")
/// .arg(Arg::new("file")
/// .action(ArgAction::Set)
/// .max_values(3)
/// .short('F'))
/// .try_get_matches_from(vec![
/// "prog", "-F", "file1", "file2"
/// ]);
///
/// assert!(res.is_ok());
/// let m = res.unwrap();
/// let files: Vec<_> = m.get_many::<String>("file").unwrap().collect();
/// assert_eq!(files, ["file1", "file2"]);
/// ```
///
/// Supplying more than the maximum number of values is an error
///
/// ```rust
/// # use clap::{Command, Arg, ErrorKind, ArgAction};
/// let res = Command::new("prog")
/// .arg(Arg::new("file")
/// .action(ArgAction::Set)
/// .max_values(2)
/// .short('F'))
/// .try_get_matches_from(vec![
/// "prog", "-F", "file1", "file2", "file3"
/// ]);
///
/// assert!(res.is_err());
/// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument);
/// ```
#[inline]
#[must_use]
pub fn max_values(mut self, qty: usize) -> Self {
self.max_vals = Some(qty);
self.takes_value(true).multiple_values(true)
}
/// Placeholder for the argument's value in the help message / usage.
///
/// This name is cosmetic only; the name is **not** used to access arguments.
@ -1791,7 +1733,7 @@ impl<'help> Arg<'help> {
/// By default when
/// 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 [`max_values`] or [`number_of_values`]).
/// for multiple values is used (such as [`number_of_values`]).
///
/// **NOTE:** This setting only applies to [options] and [positional arguments]
///
@ -1832,7 +1774,6 @@ impl<'help> Arg<'help> {
/// [positional arguments]: Arg::index()
/// [`multiple_values(true)`]: Arg::multiple_values()
/// [`number_of_values`]: Arg::number_of_values()
/// [`max_values`]: Arg::max_values()
#[inline]
#[must_use]
pub fn value_terminator(mut self, term: &'help str) -> Self {
@ -4543,7 +4484,6 @@ impl<'help> fmt::Debug for Arg<'help> {
.field("disp_ord", &self.disp_ord)
.field("val_names", &self.val_names)
.field("num_vals", &self.num_vals)
.field("max_vals", &self.max_vals)
.field("val_delim", &self.val_delim)
.field("default_vals", &self.default_vals)
.field("default_vals_ifs", &self.default_vals_ifs)

View file

@ -96,7 +96,7 @@ pub enum ErrorKind {
ValueValidation,
/// Occurs when a user provides more values for an argument than were defined by setting
/// [`Arg::max_values`].
/// [`Arg::number_of_values`].
///
/// # Examples
///
@ -104,12 +104,14 @@ pub enum ErrorKind {
/// # use clap::{Command, Arg, ErrorKind};
/// let result = Command::new("prog")
/// .arg(Arg::new("arg")
/// .max_values(2))
/// .try_get_matches_from(vec!["prog", "too", "many", "values"]);
/// .number_of_values(1..=2)
/// .use_value_delimiter(true)
/// .require_value_delimiter(true))
/// .try_get_matches_from(vec!["prog", "too,many,values"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyValues);
/// ```
/// [`Arg::max_values`]: crate::Arg::max_values()
/// [`Arg::number_of_values`]: crate::Arg::number_of_values()
TooManyValues,
/// Occurs when the user provides fewer values for an argument than were defined by setting

View file

@ -222,9 +222,6 @@ impl ArgMatcher {
expected, num_pending
);
expected.accepts_more(num_pending)
} else if let Some(num) = o.max_vals {
debug!("ArgMatcher::needs_more_vals: max_vals...{}", num);
current_num < num
} else {
o.is_multiple_values_set()
}

View file

@ -314,25 +314,6 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
}
}
}
if let Some(num) = a.max_vals {
debug!("Validator::validate_arg_num_vals: max_vals set...{}", num);
if ma.num_vals() > num {
debug!("Validator::validate_arg_num_vals: Sending error TooManyValues");
return Err(Error::too_many_values(
self.cmd,
ma.raw_vals_flatten()
.last()
.expect(INTERNAL_ERROR_MSG)
.to_str()
.expect(INVALID_UTF8)
.to_string(),
a.to_string(),
Usage::new(self.cmd)
.required(&self.required)
.create_usage_with_title(&[]),
));
}
}
Ok(())
}

View file

@ -298,10 +298,10 @@ fn option_max_exact() {
Arg::new("option")
.short('o')
.help("multiple options")
.max_values(3)
.action(ArgAction::Append),
.number_of_values(1..=3)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "-o", "val1", "-o", "val2", "-o", "val3"]);
.try_get_matches_from(vec!["", "-o", "val1", "val2", "val3"]);
assert!(m.is_ok(), "{}", m.unwrap_err());
let m = m.unwrap();
@ -323,10 +323,10 @@ fn option_max_less() {
Arg::new("option")
.short('o')
.help("multiple options")
.max_values(3)
.action(ArgAction::Append),
.number_of_values(1..=3)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "-o", "val1", "-o", "val2"]);
.try_get_matches_from(vec!["", "-o", "val1", "val2"]);
assert!(m.is_ok(), "{}", m.unwrap_err());
let m = m.unwrap();
@ -348,8 +348,8 @@ fn option_max_zero() {
Arg::new("option")
.short('o')
.help("multiple options")
.max_values(3)
.action(ArgAction::Append),
.number_of_values(1..=3)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "-o"]);
@ -364,8 +364,8 @@ fn option_max_zero_eq() {
Arg::new("option")
.short('o')
.help("multiple options")
.max_values(3)
.action(ArgAction::Append),
.number_of_values(1..=3)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "-o="]);
@ -389,15 +389,14 @@ fn option_max_more() {
Arg::new("option")
.short('o')
.help("multiple options")
.max_values(3)
.action(ArgAction::Append),
.number_of_values(1..=3)
.action(ArgAction::Set),
)
.try_get_matches_from(vec![
"", "-o", "val1", "-o", "val2", "-o", "val3", "-o", "val4",
]);
.try_get_matches_from(vec!["", "-o", "val1", "val2", "val3", "val4"]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::TooManyValues);
// Can end up being TooManyValues or UnknownArgument
assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
}
#[test]
@ -580,7 +579,11 @@ fn positional_min_more() {
#[test]
fn positional_max_exact() {
let m = Command::new("multiple_values")
.arg(Arg::new("pos").help("multiple positionals").max_values(3))
.arg(
Arg::new("pos")
.help("multiple positionals")
.number_of_values(1..=3),
)
.try_get_matches_from(vec!["myprog", "val1", "val2", "val3"]);
assert!(m.is_ok(), "{}", m.unwrap_err());
@ -599,7 +602,11 @@ fn positional_max_exact() {
#[test]
fn positional_max_less() {
let m = Command::new("multiple_values")
.arg(Arg::new("pos").help("multiple positionals").max_values(3))
.arg(
Arg::new("pos")
.help("multiple positionals")
.number_of_values(1..=3),
)
.try_get_matches_from(vec!["myprog", "val1", "val2"]);
assert!(m.is_ok(), "{}", m.unwrap_err());
@ -618,7 +625,11 @@ fn positional_max_less() {
#[test]
fn positional_max_more() {
let m = Command::new("multiple_values")
.arg(Arg::new("pos").help("multiple positionals").max_values(3))
.arg(
Arg::new("pos")
.help("multiple positionals")
.number_of_values(1..=3),
)
.try_get_matches_from(vec!["myprog", "val1", "val2", "val3", "val4"]);
assert!(m.is_err());
@ -1454,7 +1465,7 @@ fn multiple_vals_with_hyphen() {
#[test]
fn issue_1480_max_values_consumes_extra_arg_1() {
let res = Command::new("prog")
.arg(Arg::new("field").max_values(1).long("field"))
.arg(Arg::new("field").number_of_values(..=1).long("field"))
.arg(Arg::new("positional").required(true).index(1))
.try_get_matches_from(vec!["prog", "--field", "1", "file"]);
@ -1464,7 +1475,7 @@ fn issue_1480_max_values_consumes_extra_arg_1() {
#[test]
fn issue_1480_max_values_consumes_extra_arg_2() {
let res = Command::new("prog")
.arg(Arg::new("field").max_values(1).long("field"))
.arg(Arg::new("field").number_of_values(..=1).long("field"))
.try_get_matches_from(vec!["prog", "--field", "1", "2"]);
assert!(res.is_err());
@ -1474,7 +1485,7 @@ fn issue_1480_max_values_consumes_extra_arg_2() {
#[test]
fn issue_1480_max_values_consumes_extra_arg_3() {
let res = Command::new("prog")
.arg(Arg::new("field").max_values(1).long("field"))
.arg(Arg::new("field").number_of_values(..=1).long("field"))
.try_get_matches_from(vec!["prog", "--field", "1", "2", "3"]);
assert!(res.is_err());

View file

@ -83,7 +83,7 @@ pub fn complex_app() -> Command<'static> {
.number_of_values(2..),
arg!(--maxvals3 <maxvals> "Tests 3 max vals")
.required(false)
.max_values(3),
.number_of_values(1..=3),
arg!(--optvaleq <optval> "Tests optional value, require = sign")
.required(false)
.number_of_values(0..=1)