feat(Settings): adds new setting to stop delimiting values with -- or TrailingVarArg

One can now use `AppSettings::DontDelimitTrailingValues` to stop clap's default behavior of
delimiting values, even when `--` or `TrailingVarArg` is used. This is useful when passing other
flags and commands through to another program.

Closes #511
This commit is contained in:
Kevin K 2016-06-05 01:09:07 -04:00
parent 7db45f78d8
commit fc3e0f5afd
3 changed files with 71 additions and 44 deletions

View file

@ -112,27 +112,26 @@ macro_rules! parse_positional {
$_self:ident,
$p:ident,
$arg_os:ident,
$pos_only:ident,
$pos_counter:ident,
$matcher:ident
) => {
debugln!("macro=parse_positional!;");
validate_multiples!($_self, $p, $matcher);
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
return Err(e);
}
if !$pos_only &&
if !$_self.trailing_vals &&
($_self.settings.is_set(AppSettings::TrailingVarArg) &&
$pos_counter == $_self.positionals.len()) {
$pos_only = true;
$_self.trailing_vals = true;
}
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
return Err(e);
}
$matcher.inc_occurrence_of($p.name);
let _ = $_self.groups_for_arg($p.name)
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
arg_post_processing!($_self, $p, $matcher);
// Only increment the positional counter if it doesn't allow multiples
// Only increment the positional counter if it doesn't allow multiples
if !$p.settings.is_set(ArgSettings::Multiple) {
$pos_counter += 1;
}

View file

@ -51,6 +51,7 @@ pub struct Parser<'a, 'b>
settings: AppFlags,
pub g_settings: Vec<AppSettings>,
pub meta: AppMeta<'b>,
trailing_vals: bool,
}
impl<'a, 'b> Default for Parser<'a, 'b> {
@ -72,6 +73,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
g_settings: vec![],
settings: AppFlags::new(),
meta: AppMeta::new(),
trailing_vals: false,
}
}
}
@ -456,7 +458,6 @@ impl<'a, 'b> Parser<'a, 'b>
// necessary
self.create_help_and_version();
let mut pos_only = false;
let mut subcmd_name: Option<String> = None;
let mut needs_val_of: Option<&str> = None;
let mut pos_counter = 1;
@ -475,7 +476,7 @@ impl<'a, 'b> Parser<'a, 'b>
};
// Has the user already passed '--'?
if !pos_only {
if !self.trailing_vals {
// Does the arg match a subcommand name, or any of it's aliases (if defined)
let pos_sc = self.subcommands
.iter()
@ -501,7 +502,7 @@ impl<'a, 'b> Parser<'a, 'b>
if arg_os.len_() == 2 {
// The user has passed '--' which means only positional args follow no
// matter what they start with
pos_only = true;
self.trailing_vals = true;
continue;
}
@ -564,7 +565,7 @@ impl<'a, 'b> Parser<'a, 'b>
}
if let Some(p) = self.positionals.get(pos_counter) {
parse_positional!(self, p, arg_os, pos_only, pos_counter, matcher);
parse_positional!(self, p, arg_os, pos_counter, matcher);
} else {
if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
let mut sc_m = ArgMatcher::new();
@ -1131,13 +1132,17 @@ impl<'a, 'b> Parser<'a, 'b>
{
debugln!("fn=add_val_to_arg;");
let mut ret = None;
if let Some(delim) = arg.val_delim() {
if val.is_empty_() {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
} else {
for v in val.split(delim as u32 as u8) {
ret = try!(self.add_single_val_to_arg(arg, v, matcher));
if !(self.trailing_vals && self.is_set(AppSettings::DontDelimitTrailingValues)) {
if let Some(delim) = arg.val_delim() {
if val.is_empty_() {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
} else {
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));
}
} else {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
@ -1679,6 +1684,7 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
settings: self.settings.clone(),
g_settings: self.g_settings.clone(),
meta: self.meta.clone(),
trailing_vals: self.trailing_vals,
}
}
}

View file

@ -3,32 +3,33 @@ use std::ascii::AsciiExt;
bitflags! {
flags Flags: u32 {
const SC_NEGATE_REQS = 0b00000000000000000000000001,
const SC_REQUIRED = 0b00000000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000100,
const GLOBAL_VERSION = 0b00000000000000000000001000,
const VERSIONLESS_SC = 0b00000000000000000000010000,
const UNIFIED_HELP = 0b00000000000000000000100000,
const WAIT_ON_ERROR = 0b00000000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b00000000000000000010000000,
const NEEDS_LONG_HELP = 0b00000000000000000100000000,
const NEEDS_LONG_VERSION = 0b00000000000000001000000000,
const NEEDS_SC_HELP = 0b00000000000000010000000000,
const DISABLE_VERSION = 0b00000000000000100000000000,
const HIDDEN = 0b00000000000001000000000000,
const TRAILING_VARARG = 0b00000000000010000000000000,
const NO_BIN_NAME = 0b00000000000100000000000000,
const ALLOW_UNK_SC = 0b00000000001000000000000000,
const UTF8_STRICT = 0b00000000010000000000000000,
const UTF8_NONE = 0b00000000100000000000000000,
const LEADING_HYPHEN = 0b00000001000000000000000000,
const NO_POS_VALUES = 0b00000010000000000000000000,
const NEXT_LINE_HELP = 0b00000100000000000000000000,
const DERIVE_DISP_ORDER = 0b00001000000000000000000000,
const COLORED_HELP = 0b00010000000000000000000000,
const COLOR_ALWAYS = 0b00100000000000000000000000,
const COLOR_AUTO = 0b01000000000000000000000000,
const COLOR_NEVER = 0b10000000000000000000000000,
const SC_NEGATE_REQS = 0b000000000000000000000000001,
const SC_REQUIRED = 0b000000000000000000000000010,
const A_REQUIRED_ELSE_HELP = 0b000000000000000000000000100,
const GLOBAL_VERSION = 0b000000000000000000000001000,
const VERSIONLESS_SC = 0b000000000000000000000010000,
const UNIFIED_HELP = 0b000000000000000000000100000,
const WAIT_ON_ERROR = 0b000000000000000000001000000,
const SC_REQUIRED_ELSE_HELP= 0b000000000000000000010000000,
const NEEDS_LONG_HELP = 0b000000000000000000100000000,
const NEEDS_LONG_VERSION = 0b000000000000000001000000000,
const NEEDS_SC_HELP = 0b000000000000000010000000000,
const DISABLE_VERSION = 0b000000000000000100000000000,
const HIDDEN = 0b000000000000001000000000000,
const TRAILING_VARARG = 0b000000000000010000000000000,
const NO_BIN_NAME = 0b000000000000100000000000000,
const ALLOW_UNK_SC = 0b000000000001000000000000000,
const UTF8_STRICT = 0b000000000010000000000000000,
const UTF8_NONE = 0b000000000100000000000000000,
const LEADING_HYPHEN = 0b000000001000000000000000000,
const NO_POS_VALUES = 0b000000010000000000000000000,
const NEXT_LINE_HELP = 0b000000100000000000000000000,
const DERIVE_DISP_ORDER = 0b000001000000000000000000000,
const COLORED_HELP = 0b000010000000000000000000000,
const COLOR_ALWAYS = 0b000100000000000000000000000,
const COLOR_AUTO = 0b001000000000000000000000000,
const COLOR_NEVER = 0b010000000000000000000000000,
const DONT_DELIM_TRAIL = 0b100000000000000000000000000,
}
}
@ -79,7 +80,8 @@ impl AppFlags {
DeriveDisplayOrder => DERIVE_DISP_ORDER,
ColorAlways => COLOR_ALWAYS,
ColorAuto => COLOR_AUTO,
ColorNever => COLOR_NEVER
ColorNever => COLOR_NEVER,
DontDelimitTrailingValues => DONT_DELIM_TRAIL
}
}
@ -551,6 +553,24 @@ pub enum AppSettings {
/// .get_matches();
/// ```
ColorNever,
/// Disables the automatic delimiting of values when `--` or [`AppSettings::TrailingVarArg`]
/// was used.
///
/// **NOTE:** The same thing can be done manually by setting the final positional argument to
/// [`Arg::use_delimiter(false)`]. Using this setting is safer, because it's easier to locate
/// when making changes.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::DontDelimitTrailingValues)
/// .get_matches();
/// ```
/// [`AppSettings::TrailingVarArg`]: ./enum.AppSettings.html#variant.TrailingVarArg
/// [`Arg::use_delimiter(false)`]: ./struct.Arg.html#method.use_delimiter
DontDelimitTrailingValues,
#[doc(hidden)]
NeedsLongVersion,
#[doc(hidden)]
@ -633,6 +653,8 @@ mod test {
AppSettings::ColoredHelp);
assert_eq!("hidden".parse::<AppSettings>().unwrap(),
AppSettings::Hidden);
assert_eq!("dontdelimittrailingvalues".parse::<AppSettings>().unwrap(),
AppSettings::DontDelimitTrailingValues);
assert!("hahahaha".parse::<AppSettings>().is_err());
}
}