Skip to content

Commit

Permalink
feat: adds support for turning off the value delimiter
Browse files Browse the repository at this point in the history
Closes #352
  • Loading branch information
kbknapp committed Jan 28, 2016
1 parent eeee71e commit 508db85
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 20 deletions.
36 changes: 23 additions & 13 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
59 changes: 59 additions & 0 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand All @@ -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"));
Expand Down
21 changes: 14 additions & 7 deletions src/args/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand All @@ -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) {
Expand All @@ -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),
}
}

Expand All @@ -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),
}
}

Expand All @@ -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),
}
}
}
Expand All @@ -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 {
Expand All @@ -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()),
}
}
Expand Down

0 comments on commit 508db85

Please sign in to comment.