feat: adds support for turning off the value delimiter

Closes #352
This commit is contained in:
Kevin K 2016-01-26 10:51:47 -05:00
parent 7b0ff38fe7
commit 35ad17a282
3 changed files with 96 additions and 20 deletions

View file

@ -1058,12 +1058,24 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
where A: AnyArg<'a, 'b> + Display { where A: AnyArg<'a, 'b> + Display {
debugln!("fn=add_val_to_arg;"); debugln!("fn=add_val_to_arg;");
let mut ret = None; let mut ret = None;
for v in val.split(arg.val_delim().unwrap_or(',') as u32 as u8) { 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));
}
} else {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
}
Ok(ret)
}
fn add_single_val_to_arg<A>(&self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b>
{
debugln!("adding val: {:?}", v); debugln!("adding val: {:?}", v);
matcher.add_val_to(&*arg.name(), v); matcher.add_val_to(arg.name(), v);
// Increment or create the group "args" // Increment or create the group "args"
if let Some(grps) = self.groups_for_arg(&*arg.name()) { if let Some(grps) = self.groups_for_arg(arg.name()) {
for grp in grps { for grp in grps {
matcher.add_val_to(&*grp, v); matcher.add_val_to(&*grp, v);
} }
@ -1071,9 +1083,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
// The validation must come AFTER inserting into 'matcher' or the usage string // The validation must come AFTER inserting into 'matcher' or the usage string
// can't be built // can't be built
ret = try!(self.validate_value(arg, v, matcher)); self.validate_value(arg, v, matcher)
}
Ok(ret)
} }
fn validate_value<A>(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult<Option<&'a str>> fn validate_value<A>(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult<Option<&'a str>>

View file

@ -179,6 +179,7 @@ impl<'a, 'b> Arg<'a, 'b> {
"max_values" => a.max_values(v.as_i64().unwrap() as u8), "max_values" => a.max_values(v.as_i64().unwrap() as u8),
"min_values" => a.min_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()), "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_delimiter" => a.value_delimiter(v.as_str().unwrap()),
"value_names" => { "value_names" => {
for ys in v.as_vec().unwrap() { for ys in v.as_vec().unwrap() {
@ -815,8 +816,65 @@ impl<'a, 'b> Arg<'a, 'b> {
self 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::<Vec<_>>(), ["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). /// 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)` /// **NOTE:** implicitly sets `Arg::takes_value(true)`
/// ///
/// # Examples /// # Examples
@ -837,6 +895,7 @@ impl<'a, 'b> Arg<'a, 'b> {
/// ``` /// ```
pub fn value_delimiter(mut self, d: &str) -> Self { pub fn value_delimiter(mut self, d: &str) -> Self {
self = self.set(ArgSettings::TakesValue); self = self.set(ArgSettings::TakesValue);
self = self.set(ArgSettings::UseValueDelimiter);
self.val_delim = Some(d.chars() self.val_delim = Some(d.chars()
.nth(0) .nth(0)
.expect("Failed to get value_delimiter from arg")); .expect("Failed to get value_delimiter from arg"));

View file

@ -3,12 +3,13 @@ use std::ascii::AsciiExt;
bitflags! { bitflags! {
flags Flags: u8 { flags Flags: u8 {
const REQUIRED = 0b000001, const REQUIRED = 0b0000001,
const MULTIPLE = 0b000010, const MULTIPLE = 0b0000010,
const EMPTY_VALS = 0b000100, const EMPTY_VALS = 0b0000100,
const GLOBAL = 0b001000, const GLOBAL = 0b0001000,
const HIDDEN = 0b010000, const HIDDEN = 0b0010000,
const TAKES_VAL = 0b100000, const TAKES_VAL = 0b0100000,
const USE_DELIM = 0b1000000,
} }
} }
@ -17,7 +18,7 @@ pub struct ArgFlags(Flags);
impl ArgFlags { impl ArgFlags {
pub fn new() -> Self { pub fn new() -> Self {
ArgFlags(EMPTY_VALS) ArgFlags(EMPTY_VALS | USE_DELIM)
} }
pub fn set(&mut self, s: ArgSettings) { pub fn set(&mut self, s: ArgSettings) {
@ -28,6 +29,7 @@ impl ArgFlags {
ArgSettings::Global => self.0.insert(GLOBAL), ArgSettings::Global => self.0.insert(GLOBAL),
ArgSettings::Hidden => self.0.insert(HIDDEN), ArgSettings::Hidden => self.0.insert(HIDDEN),
ArgSettings::TakesValue => self.0.insert(TAKES_VAL), 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::Global => self.0.remove(GLOBAL),
ArgSettings::Hidden => self.0.remove(HIDDEN), ArgSettings::Hidden => self.0.remove(HIDDEN),
ArgSettings::TakesValue => self.0.remove(TAKES_VAL), 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::Global => self.0.contains(GLOBAL),
ArgSettings::Hidden => self.0.contains(HIDDEN), ArgSettings::Hidden => self.0.contains(HIDDEN),
ArgSettings::TakesValue => self.0.contains(TAKES_VAL), ArgSettings::TakesValue => self.0.contains(TAKES_VAL),
ArgSettings::UseValueDelimiter => self.0.contains(USE_DELIM),
} }
} }
} }
@ -76,6 +80,8 @@ pub enum ArgSettings {
Hidden, Hidden,
/// The argument accepts a value, such as `--option <value>` /// The argument accepts a value, such as `--option <value>`
TakesValue, TakesValue,
/// Determines if the argument allows values to be grouped via a delimter
UseValueDelimiter,
} }
impl FromStr for ArgSettings { impl FromStr for ArgSettings {
@ -88,6 +94,7 @@ impl FromStr for ArgSettings {
"emptyvalues" => Ok(ArgSettings::EmptyValues), "emptyvalues" => Ok(ArgSettings::EmptyValues),
"hidden" => Ok(ArgSettings::Hidden), "hidden" => Ok(ArgSettings::Hidden),
"takesvalue" => Ok(ArgSettings::TakesValue), "takesvalue" => Ok(ArgSettings::TakesValue),
"usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter),
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()), _ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
} }
} }