From 508db850a87c2e251cf6b6ddead9ad56b29f9e57 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 26 Jan 2016 10:51:47 -0500 Subject: [PATCH] feat: adds support for turning off the value delimiter Closes #352 --- src/app/parser.rs | 36 +++++++++++++++++---------- src/args/arg.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++ src/args/settings.rs | 21 ++++++++++------ 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/src/app/parser.rs b/src/app/parser.rs index e895eeae4e40..4a637637de6d 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -1058,24 +1058,34 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { where A: AnyArg<'a, 'b> + Display { debugln!("fn=add_val_to_arg;"); let mut ret = None; - for v in val.split(arg.val_delim().unwrap_or(',') as u32 as u8) { - debugln!("adding val: {:?}", v); - matcher.add_val_to(&*arg.name(), v); - - // Increment or create the group "args" - if let Some(grps) = self.groups_for_arg(&*arg.name()) { - for grp in grps { - matcher.add_val_to(&*grp, v); - } + if let Some(delim) = arg.val_delim() { + for v in val.split(delim as u32 as u8) { + ret = try!(self.add_single_val_to_arg(arg, v, matcher)); } - - // The validation must come AFTER inserting into 'matcher' or the usage string - // can't be built - ret = try!(self.validate_value(arg, v, matcher)); + } else { + ret = try!(self.add_single_val_to_arg(arg, val, matcher)); } Ok(ret) } + fn add_single_val_to_arg(&self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult> + where A: AnyArg<'a, 'b> + { + debugln!("adding val: {:?}", v); + matcher.add_val_to(arg.name(), v); + + // Increment or create the group "args" + if let Some(grps) = self.groups_for_arg(arg.name()) { + for grp in grps { + matcher.add_val_to(&*grp, v); + } + } + + // The validation must come AFTER inserting into 'matcher' or the usage string + // can't be built + self.validate_value(arg, v, matcher) + } + fn validate_value(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult> where A: AnyArg<'a, 'b> { debugln!("fn=validate_value; val={:?}", val); diff --git a/src/args/arg.rs b/src/args/arg.rs index e89de2aaa2b1..a466a04cca31 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -179,6 +179,7 @@ impl<'a, 'b> Arg<'a, 'b> { "max_values" => a.max_values(v.as_i64().unwrap() as u8), "min_values" => a.min_values(v.as_i64().unwrap() as u8), "value_name" => a.value_name(v.as_str().unwrap()), + "use_delimiter" => a.use_delimiter(v.as_bool().unwrap()), "value_delimiter" => a.value_delimiter(v.as_str().unwrap()), "value_names" => { for ys in v.as_vec().unwrap() { @@ -815,8 +816,65 @@ impl<'a, 'b> Arg<'a, 'b> { self } + /// Specifies whether or not an arugment should allow grouping of multiple values via a + /// delimter. I.e. shoulde `--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 defalt 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. + /// + /// ```no_run + /// # 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::>(), ["val1", "val2", "val3"]); + /// ``` + /// The next example shows the difference when turning delimiters off. + /// + /// ```no_run + /// # 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"); + /// ``` + pub fn use_delimiter(mut self, d: bool) -> Self { + if d { + self.val_delim = Some(','); + self.set(ArgSettings::UseValueDelimiter) + } else { + self.val_delim = None; + self.unset(ArgSettings::UseValueDelimiter) + } + } + /// Specifies the separator to use when values are clumped together, defaults to `,` (comma). /// + /// **NOTE:** implicitly sets `Arg::use_delimiter(true)` + /// /// **NOTE:** implicitly sets `Arg::takes_value(true)` /// /// # Examples @@ -837,6 +895,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` pub fn value_delimiter(mut self, d: &str) -> Self { self = self.set(ArgSettings::TakesValue); + self = self.set(ArgSettings::UseValueDelimiter); self.val_delim = Some(d.chars() .nth(0) .expect("Failed to get value_delimiter from arg")); diff --git a/src/args/settings.rs b/src/args/settings.rs index a6fcfff54112..f2f138401611 100644 --- a/src/args/settings.rs +++ b/src/args/settings.rs @@ -3,12 +3,13 @@ use std::ascii::AsciiExt; bitflags! { flags Flags: u8 { - const REQUIRED = 0b000001, - const MULTIPLE = 0b000010, - const EMPTY_VALS = 0b000100, - const GLOBAL = 0b001000, - const HIDDEN = 0b010000, - const TAKES_VAL = 0b100000, + const REQUIRED = 0b0000001, + const MULTIPLE = 0b0000010, + const EMPTY_VALS = 0b0000100, + const GLOBAL = 0b0001000, + const HIDDEN = 0b0010000, + const TAKES_VAL = 0b0100000, + const USE_DELIM = 0b1000000, } } @@ -17,7 +18,7 @@ pub struct ArgFlags(Flags); impl ArgFlags { pub fn new() -> Self { - ArgFlags(EMPTY_VALS) + ArgFlags(EMPTY_VALS | USE_DELIM) } pub fn set(&mut self, s: ArgSettings) { @@ -28,6 +29,7 @@ impl ArgFlags { ArgSettings::Global => self.0.insert(GLOBAL), ArgSettings::Hidden => self.0.insert(HIDDEN), ArgSettings::TakesValue => self.0.insert(TAKES_VAL), + ArgSettings::UseValueDelimiter => self.0.insert(USE_DELIM), } } @@ -39,6 +41,7 @@ impl ArgFlags { ArgSettings::Global => self.0.remove(GLOBAL), ArgSettings::Hidden => self.0.remove(HIDDEN), ArgSettings::TakesValue => self.0.remove(TAKES_VAL), + ArgSettings::UseValueDelimiter => self.0.remove(USE_DELIM), } } @@ -50,6 +53,7 @@ impl ArgFlags { ArgSettings::Global => self.0.contains(GLOBAL), ArgSettings::Hidden => self.0.contains(HIDDEN), ArgSettings::TakesValue => self.0.contains(TAKES_VAL), + ArgSettings::UseValueDelimiter => self.0.contains(USE_DELIM), } } } @@ -76,6 +80,8 @@ pub enum ArgSettings { Hidden, /// The argument accepts a value, such as `--option ` TakesValue, + /// Determines if the argument allows values to be grouped via a delimter + UseValueDelimiter, } impl FromStr for ArgSettings { @@ -88,6 +94,7 @@ impl FromStr for ArgSettings { "emptyvalues" => Ok(ArgSettings::EmptyValues), "hidden" => Ok(ArgSettings::Hidden), "takesvalue" => Ok(ArgSettings::TakesValue), + "usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter), _ => Err("unknown ArgSetting, cannot convert from str".to_owned()), } }