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,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<A>(&self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
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<A>(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b> {
debugln!("fn=validate_value; val={:?}", val);

View file

@ -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::<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).
///
/// **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"));

View file

@ -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 <value>`
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()),
}
}