Skip to content

Commit

Permalink
api(Arg::value_terminator): adds the ability to terminate multiple va…
Browse files Browse the repository at this point in the history
…lues with a given value

One can now specificy a value termintaor that will stop the parsing of multiple values upon
reaching this special value.

Closes #782
  • Loading branch information
kbknapp committed Jan 1, 2017
1 parent 3ca4a08 commit 02b16c3
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
fn required_unless(&self) -> Option<&[&'e str]> { None }
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
fn is_set(&self, _: ArgSettings) -> bool { false }
fn val_terminator(&self) -> Option<&'e str> {None}
fn set(&mut self, _: ArgSettings) {
unreachable!("App struct does not support AnyArg::set, this is a bug!")
}
Expand Down
5 changes: 5 additions & 0 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,11 @@ impl<'a, 'b> Parser<'a, 'b>
{
debugln!("Parser::add_single_val_to_arg;");
debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v);
if let Some(t) = arg.val_terminator() {
if t == v {
return Ok(None);
}
}
matcher.add_val_to(arg.name(), v);

// Increment or create the group "args"
Expand Down
1 change: 1 addition & 0 deletions src/args/any_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display {
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e str>, &'e str)>>;
fn longest_filter(&self) -> bool;
fn kind(&self) -> ArgKind;
fn val_terminator(&self) -> Option<&'e str>;
}

pub trait DispOrder {
Expand Down
54 changes: 54 additions & 0 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ pub struct Arg<'a, 'b>
pub r_unless: Option<Vec<&'a str>>,
#[doc(hidden)]
pub r_ifs: Option<Vec<(&'a str, &'b str)>>,
#[doc(hidden)]
pub val_terminator: Option<&'b str>,
}

impl<'a, 'b> Default for Arg<'a, 'b> {
Expand Down Expand Up @@ -113,6 +115,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> {
disp_ord: 999,
r_unless: None,
r_ifs: None,
val_terminator: None,
}
}
}
Expand Down Expand Up @@ -1819,6 +1822,55 @@ impl<'a, 'b> Arg<'a, 'b> {
}
}

/// Specifies a value that *stops* parsing multiple values of a give argument. By default when
/// one sets [`multiple(true)`] on an argument, clap will continue parsing values for that
/// argument until it reaches another valid argument, or one of the other more specific settings
/// for multiple values is used (such as [`min_values`], [`max_values`] or
/// [`number_of_values`]).
///
/// **NOTE:** This setting only applies to [options] and [positional arguments]
///
/// **NOTE:** When the terminator is passed in on the command line, it is **not** stored as one
/// of the vaues
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg};
/// Arg::with_name("vals")
/// .takes_value(true)
/// .multiple(true)
/// .value_terminator(";")
/// # ;
/// ```
/// The following example uses two arguments, a sequence of commands, and the location in which
/// to perform them
///
/// ```rust
/// # use clap::{App, Arg};
/// let m = App::new("do")
/// .arg(Arg::with_name("cmds")
/// .multiple(true)
/// .hyphen_values(true)
/// .terminator(";"))
/// .arg(Arg::with_name("location"))
/// .get_matches_from(vec!["do", "find", "-type", "f", "-name", "special", ";", "/home/clap"]);
/// let cmds: Vec<_> = m.values_of("cmds").unwrap().collect();
/// assert_eq!(&cmds, &["find", "-type", "f", "-name", "special", ";"]);
/// assert_eq!(m.value_of("location"), Some("/home/clap"));
/// ```
/// [options]: ./struct.Arg.html#method.takes_value
/// [positional arguments]: ./struct.Arg.html#method.index
/// [`multiple(true)`]: ./struct.Arg.html#method.multiple
/// [`min_values`]: ./struct.Arg.html#method.min_values
/// [`number_of_values`]: ./struct.Arg.html#method.number_of_values
/// [`max_values`]: ./struct.Arg.html#method.max_values
pub fn value_terminator(mut self, term: &'b str) -> Self {
self.setb(ArgSettings::TakesValue);
self.val_terminator = Some(term);
self
}

/// Specifies that an argument can be matched to all child [`SubCommand`]s.
///
/// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands)
Expand Down Expand Up @@ -3153,6 +3205,7 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> {
disp_ord: a.disp_ord,
r_unless: a.r_unless.clone(),
r_ifs: a.r_ifs.clone(),
val_terminator: a.val_terminator.clone(),
}
}
}
Expand Down Expand Up @@ -3184,6 +3237,7 @@ impl<'a, 'b> Clone for Arg<'a, 'b> {
disp_ord: self.disp_ord,
r_unless: self.r_unless.clone(),
r_ifs: self.r_ifs.clone(),
val_terminator: self.val_terminator.clone(),
}
}
}
1 change: 1 addition & 0 deletions src/args/arg_builder/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn long(&self) -> Option<&'e str> { self.s.long }
fn val_delim(&self) -> Option<char> { None }
fn help(&self) -> Option<&'e str> { self.b.help }
fn val_terminator(&self) -> Option<&'e str> {None}
fn default_val(&self) -> Option<&'n str> { None }
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e str>, &'e str)>> {None}
fn longest_filter(&self) -> bool { self.s.long.is_some() }
Expand Down
1 change: 1 addition & 0 deletions src/args/arg_builder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn has_switch(&self) -> bool { true }
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
fn val_terminator(&self) -> Option<&'e str> { self.v.terminator }
fn num_vals(&self) -> Option<u64> { self.v.num_vals }
fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
Expand Down
1 change: 1 addition & 0 deletions src/args/arg_builder/positional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
fn has_switch(&self) -> bool { false }
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
fn val_terminator(&self) -> Option<&'e str> { self.v.terminator }
fn num_vals(&self) -> Option<u64> { self.v.num_vals }
fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
Expand Down
3 changes: 3 additions & 0 deletions src/args/arg_builder/valued.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct Valued<'a, 'b>
pub val_delim: Option<char>,
pub default_val: Option<&'a str>,
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b str>, &'b str)>>,
pub terminator: Option<&'b str>,
}

impl<'n, 'e> Default for Valued<'n, 'e> {
Expand All @@ -35,6 +36,7 @@ impl<'n, 'e> Default for Valued<'n, 'e> {
val_delim: Some(','),
default_val: None,
default_vals_ifs: None,
terminator: None,
}
}
}
Expand All @@ -52,6 +54,7 @@ impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Valued<'n, 'e> {
val_delim: a.val_delim,
default_val: a.default_val,
default_vals_ifs: a.default_vals_ifs.clone(),
terminator: a.val_terminator.clone(),
};
if let Some(ref vec) = a.val_names {
if vec.len() > 1 {
Expand Down

0 comments on commit 02b16c3

Please sign in to comment.