From 27018b1821a4bcd5235cfe92abe71b3c99efc24d Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 1 Oct 2015 14:16:13 -0400 Subject: [PATCH] feat(Trailing VarArg): adds opt-in setting for final arg being vararg Closes #278 --- src/app/app.rs | 22 +++++--------------- src/app/settings.rs | 50 +++++++++++++++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/app/app.rs b/src/app/app.rs index b7107a167eb..17230492db6 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -2354,9 +2354,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Some(matches.args.keys().map(|k| *k).collect()))); } - if let Some(ref p_vals) = p.possible_vals { - if !p_vals.contains(&arg_slice) { return Err(self.possible_values_error(arg_slice, &p.to_string(), p_vals, matches)); @@ -2380,14 +2378,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } } - if !p.settings.is_set(&ArgSettings::EmptyValues) && matches.args.contains_key(p.name) && - arg_slice.is_empty() { - return Err(self.report_error(format!("The argument '{}' does not \ - allow empty values, but one was found.", - Format::Warning(p.to_string())), - ClapErrorType::EmptyValue, - Some(matches.args.keys().map(|k| *k).collect()))); - } // Check if it's already existing and update if so... if let Some(ref mut pos) = matches.args.get_mut(p.name) { done = true; @@ -2397,6 +2387,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ vals.insert(len, arg_slice.to_owned()); } } + + if !pos_only && (self.settings.is_set(&AppSettings::TrailingVarArg) && + pos_counter == self.positionals_idx.len() as u8) { + pos_only = true; + } } else { // Only increment the positional counter if it doesn't allow multiples pos_counter += 1; @@ -2417,13 +2412,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } let mut bm = BTreeMap::new(); - if !p.settings.is_set(&ArgSettings::EmptyValues) && arg_slice.is_empty() { - return Err(self.report_error(format!("The argument '{}' does not \ - allow empty values, but one was found.", - Format::Warning(p.to_string())), - ClapErrorType::EmptyValue, - Some(matches.args.keys().map(|k| *k).collect()))); - } if let Some(ref vtor) = p.validator { let f = &*vtor; if let Err(ref e) = f(arg_slice.to_owned()) { diff --git a/src/app/settings.rs b/src/app/settings.rs index 812ea1ad5f1..6ed5d6c9ead 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -3,19 +3,20 @@ use std::ascii::AsciiExt; bitflags! { flags Flags: u32 { - const SC_NEGATE_REQS = 0b0000000000001, - const SC_REQUIRED = 0b0000000000010, - const A_REQUIRED_ELSE_HELP = 0b0000000000100, - const GLOBAL_VERSION = 0b0000000001000, - const VERSIONLESS_SC = 0b0000000010000, - const UNIFIED_HELP = 0b0000000100000, - const WAIT_ON_ERROR = 0b0000001000000, - const SC_REQUIRED_ELSE_HELP= 0b0000010000000, - const NEEDS_LONG_HELP = 0b0000100000000, - const NEEDS_LONG_VERSION = 0b0001000000000, - const NEEDS_SC_HELP = 0b0010000000000, - const DISABLE_VERSION = 0b0100000000000, - const HIDDEN = 0b1000000000000, + const SC_NEGATE_REQS = 0b00000000000001, + const SC_REQUIRED = 0b00000000000010, + const A_REQUIRED_ELSE_HELP = 0b00000000000100, + const GLOBAL_VERSION = 0b00000000001000, + const VERSIONLESS_SC = 0b00000000010000, + const UNIFIED_HELP = 0b00000000100000, + const WAIT_ON_ERROR = 0b00000001000000, + const SC_REQUIRED_ELSE_HELP= 0b00000010000000, + const NEEDS_LONG_HELP = 0b00000100000000, + const NEEDS_LONG_VERSION = 0b00001000000000, + const NEEDS_SC_HELP = 0b00010000000000, + const DISABLE_VERSION = 0b00100000000000, + const HIDDEN = 0b01000000000000, + const TRAILING_VARARG = 0b10000000000000, } } @@ -41,6 +42,7 @@ impl AppFlags { AppSettings::NeedsSubcommandHelp => self.0.insert(NEEDS_SC_HELP), AppSettings::DisableVersion => self.0.insert(DISABLE_VERSION), AppSettings::Hidden => self.0.insert(HIDDEN), + AppSettings::TrailingVarArg => self.0.insert(TRAILING_VARARG), } } @@ -59,6 +61,7 @@ impl AppFlags { AppSettings::NeedsSubcommandHelp => self.0.remove(NEEDS_SC_HELP), AppSettings::DisableVersion => self.0.remove(DISABLE_VERSION), AppSettings::Hidden => self.0.remove(HIDDEN), + AppSettings::TrailingVarArg => self.0.remove(TRAILING_VARARG), } } @@ -77,6 +80,7 @@ impl AppFlags { AppSettings::NeedsSubcommandHelp => self.0.contains(NEEDS_SC_HELP), AppSettings::DisableVersion => self.0.contains(DISABLE_VERSION), AppSettings::Hidden => self.0.contains(HIDDEN), + AppSettings::TrailingVarArg => self.0.contains(TRAILING_VARARG), } } } @@ -232,6 +236,26 @@ pub enum AppSettings { /// # ; /// ``` Hidden, + /// Specifies that the final positional argument is a vararg and that `clap` should not attempt + /// to parse any further args. + /// + /// The values of the trailing positional argument will contain all args from itself on. + /// + /// **NOTE:** The final positional argument **must** have `.multiple(true)` or usage token + /// equivilant. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg, AppSettings}; + /// let m = App::new("myprog") + /// .setting(AppSettings::TrailingVarArg) + /// .arg(Arg::from_usage("... 'commands to run'")) + /// .get_matches_from(vec!["myprog", "some_command", "-r", "set"]); + /// + /// assert_eq!(m.values_of("cmd").unwrap(), &["some_command", "-r", "set"]); + /// ``` + TrailingVarArg, #[doc(hidden)] NeedsLongVersion, #[doc(hidden)]