mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
feat(Arg): adds new setting Arg::require_delimiter
which requires val delimiter to parse multiple values
Using this setting requires a value delimiter be present in order to parse multiple values. Otherwise it is assumed no values follow, and moves on to the next arg with a clean slate. These examples demonstrate what happens when `require_delimiter(true)` is used. Notice everything works in this first example, as we use a delimiter, as expected. ```rust let delims = App::new("reqdelims") .arg(Arg::with_name("opt") .short("o") .takes_value(true) .multiple(true) .require_delimiter(true)) // Simulate "$ reqdelims -o val1,val2,val3" .get_matches_from(vec![ "reqdelims", "-o", "val1,val2,val3", ]); assert!(delims.is_present("opt")); assert_eq!(delims.values_of("opt").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]); ``` In this next example, we will *not* use a delimiter. Notice it's now an error. ```rust let res = App::new("reqdelims") .arg(Arg::with_name("opt") .short("o") .takes_value(true) .multiple(true) .require_delimiter(true)) // Simulate "$ reqdelims -o val1 val2 val3" .get_matches_from_safe(vec![ "reqdelims", "-o", "val1", "val2", "val3", ]); assert!(res.is_err()); 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. In this final example, we contrast the above with `clap`'s default behavior where the above is *not* an error. ```rust let delims = App::new("reqdelims") .arg(Arg::with_name("opt") .short("o") .takes_value(true) .multiple(true)) // Simulate "$ reqdelims -o val1 val2 val3" .get_matches_from(vec![ "reqdelims", "-o", "val1", "val2", "val3", ]); assert!(delims.is_present("opt")); assert_eq!(delims.values_of("opt").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]); ```
This commit is contained in:
parent
40017ed091
commit
920b5595ed
3 changed files with 73 additions and 12 deletions
|
@ -1256,9 +1256,9 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
ret = try!(self.add_single_val_to_arg(arg, v, matcher));
|
||||
}
|
||||
// If there was a delimiter used, we're not looking for more values
|
||||
if val.contains_byte(delim as u32 as u8) {
|
||||
if val.contains_byte(delim as u32 as u8) || arg.is_set(ArgSettings::RequireDelimiter) {
|
||||
ret = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
|
||||
|
|
|
@ -1919,6 +1919,62 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Specifies whether or not an argument should allow grouping of multiple values via a
|
||||
/// delimiter. I.e. should `--option=val1,val2,val3` be parsed as three values (`val1`, `val2`,
|
||||
/// and `val3`) or as a single value (`val1,val2,val3`). Defaults to using `,` (comma) as the
|
||||
/// value delimiter for all arguments that accept values (options and positional arguments)
|
||||
///
|
||||
/// **NOTE:** The default is `true`. Setting the value to `true` will reset any previous use of
|
||||
/// [`Arg::value_delimiter`] back to the default of `,` (comma).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following example shows the default behavior.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let delims = App::new("delims")
|
||||
/// .arg(Arg::with_name("option")
|
||||
/// .long("option")
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "delims",
|
||||
/// "--option=val1,val2,val3",
|
||||
/// ]);
|
||||
///
|
||||
/// assert!(delims.is_present("option"));
|
||||
/// assert_eq!(delims.occurrences_of("option"), 1);
|
||||
/// assert_eq!(delims.values_of("option").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]);
|
||||
/// ```
|
||||
/// The next example shows the difference when turning delimiters off.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let nodelims = App::new("nodelims")
|
||||
/// .arg(Arg::with_name("option")
|
||||
/// .long("option")
|
||||
/// .use_delimiter(false)
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "nodelims",
|
||||
/// "--option=val1,val2,val3",
|
||||
/// ]);
|
||||
///
|
||||
/// assert!(nodelims.is_present("option"));
|
||||
/// assert_eq!(nodelims.occurrences_of("option"), 1);
|
||||
/// assert_eq!(nodelims.value_of("option").unwrap(), "val1,val2,val3");
|
||||
/// ```
|
||||
/// [`Arg::value_delimiter`]: ./struct.Arg.html#method.value_delimiter
|
||||
pub fn require_delimiter(mut self, d: bool) -> Self {
|
||||
if d {
|
||||
self.setb(ArgSettings::UseValueDelimiter);
|
||||
self.set(ArgSettings::RequireDelimiter)
|
||||
} else {
|
||||
self.unsetb(ArgSettings::UseValueDelimiter);
|
||||
self.unset(ArgSettings::RequireDelimiter)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the separator to use when values are clumped together, defaults to `,` (comma).
|
||||
///
|
||||
/// **NOTE:** implicitly sets [`Arg::use_delimiter(true)`]
|
||||
|
|
|
@ -3,15 +3,16 @@ use std::ascii::AsciiExt;
|
|||
|
||||
bitflags! {
|
||||
flags Flags: u16 {
|
||||
const REQUIRED = 0b000000001,
|
||||
const MULTIPLE = 0b000000010,
|
||||
const EMPTY_VALS = 0b000000100,
|
||||
const GLOBAL = 0b000001000,
|
||||
const HIDDEN = 0b000010000,
|
||||
const TAKES_VAL = 0b000100000,
|
||||
const USE_DELIM = 0b001000000,
|
||||
const NEXT_LINE_HELP = 0b010000000,
|
||||
const R_UNLESS_ALL = 0b100000000,
|
||||
const REQUIRED = 0b0000000001,
|
||||
const MULTIPLE = 0b0000000010,
|
||||
const EMPTY_VALS = 0b0000000100,
|
||||
const GLOBAL = 0b0000001000,
|
||||
const HIDDEN = 0b0000010000,
|
||||
const TAKES_VAL = 0b0000100000,
|
||||
const USE_DELIM = 0b0001000000,
|
||||
const NEXT_LINE_HELP = 0b0010000000,
|
||||
const R_UNLESS_ALL = 0b0100000000,
|
||||
const REQ_DELIM = 0b1000000000,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +34,8 @@ impl ArgFlags {
|
|||
TakesValue => TAKES_VAL,
|
||||
UseValueDelimiter => USE_DELIM,
|
||||
NextLineHelp => NEXT_LINE_HELP,
|
||||
RequiredUnlessAll => R_UNLESS_ALL
|
||||
RequiredUnlessAll => R_UNLESS_ALL,
|
||||
RequireDelimiter => REQ_DELIM
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +69,8 @@ pub enum ArgSettings {
|
|||
UseValueDelimiter,
|
||||
/// Prints the help text on the line after the argument
|
||||
NextLineHelp,
|
||||
/// Requires the use of a value delimiter for all multiple values
|
||||
RequireDelimiter,
|
||||
#[doc(hidden)]
|
||||
RequiredUnlessAll,
|
||||
}
|
||||
|
@ -84,6 +88,7 @@ impl FromStr for ArgSettings {
|
|||
"usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter),
|
||||
"nextlinehelp" => Ok(ArgSettings::NextLineHelp),
|
||||
"requiredunlessall" => Ok(ArgSettings::RequiredUnlessAll),
|
||||
"requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
|
||||
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue