mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
setting: adds a setting to disable args being allowed between subcommands (ArgsNegateSubcommands
)
Specifies that use of a valid [argument] negates [subcomands] being used after. By default `clap` allows arguments between subcommands such as `<cmd> [cmd_args] <cmd2> [cmd2_args] <cmd3> [cmd3_args]`. This setting disables that functionality and says that arguments can only follow the *final* subcommand. For instance using this setting makes only the following invocations possible: * `<cmd> <cmd2> <cmd3> [cmd3_args]` * `<cmd> <cmd2> [cmd2_args]` * `<cmd> [cmd_args]` Closes #793
This commit is contained in:
parent
25cbca4e41
commit
5e2af8c96a
2 changed files with 80 additions and 40 deletions
|
@ -59,9 +59,11 @@ pub struct Parser<'a, 'b>
|
|||
settings: AppFlags,
|
||||
pub g_settings: Vec<AppSettings>,
|
||||
pub meta: AppMeta<'b>,
|
||||
trailing_vals: bool,
|
||||
pub id: usize,
|
||||
trailing_vals: bool,
|
||||
valid_neg_num: bool,
|
||||
// have we found a valid arg yet
|
||||
valid_arg: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Default for Parser<'a, 'b> {
|
||||
|
@ -87,6 +89,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
|
|||
trailing_vals: false,
|
||||
id: 0,
|
||||
valid_neg_num: false,
|
||||
valid_arg: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -458,7 +461,8 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
.next()
|
||||
.expect(INTERNAL_ERROR_MSG);
|
||||
return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()));
|
||||
} else if self.is_set(AppSettings::DontCollapseArgsInUsage) && !self.positionals.is_empty() {
|
||||
} else if self.is_set(AppSettings::DontCollapseArgsInUsage) &&
|
||||
!self.positionals.is_empty() {
|
||||
return Some(self.positionals
|
||||
.values()
|
||||
.filter(|p| !p.is_set(ArgSettings::Required))
|
||||
|
@ -608,6 +612,9 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
#[inline]
|
||||
fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
|
||||
debugln!("Parser::possible_subcommand;");
|
||||
if self.is_set(AppSettings::ArgsNegateSubcommands) && self.valid_arg {
|
||||
return false;
|
||||
}
|
||||
self.subcommands
|
||||
.iter()
|
||||
.any(|s| {
|
||||
|
@ -835,6 +842,8 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
}
|
||||
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
|
||||
break;
|
||||
} else if self.is_set(AppSettings::ArgsNegateSubcommands) {
|
||||
();
|
||||
} else if let Some(cdate) =
|
||||
suggestions::did_you_mean(&*arg_os.to_string_lossy(),
|
||||
self.subcommands
|
||||
|
@ -877,6 +886,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
}
|
||||
if let Some(p) = self.positionals.get(pos_counter) {
|
||||
parse_positional!(self, p, arg_os, pos_counter, matcher);
|
||||
self.valid_arg = true;
|
||||
} else if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
|
||||
// Get external subcommand name
|
||||
let sc_name = match arg_os.to_str() {
|
||||
|
@ -922,17 +932,15 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
let sc_name = &*self.subcommands
|
||||
.iter()
|
||||
.filter(|sc| sc.p.meta.aliases.is_some())
|
||||
.filter_map(|sc| if sc.p
|
||||
.filter(|sc| sc.p
|
||||
.meta
|
||||
.aliases
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.expect(INTERNAL_ERROR_MSG)
|
||||
.iter()
|
||||
.any(|&(a, _)| &a == &&*pos_sc_name) {
|
||||
Some(sc.p.meta.name.clone())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.any(|&(a, _)| &a == &&*pos_sc_name)
|
||||
)
|
||||
.map(|sc| sc.p.meta.name.clone())
|
||||
.next()
|
||||
.expect(INTERNAL_ERROR_MSG);
|
||||
try!(self.parse_subcommand(sc_name, matcher, it));
|
||||
|
@ -1321,12 +1329,14 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
|
||||
if let Some(opt) = find_by_long!(self, &arg, opts) {
|
||||
debugln!("Parser::parse_long_arg: Found valid opt '{}'", opt.to_string());
|
||||
self.valid_arg = true;
|
||||
let ret = try!(self.parse_opt(val, opt, matcher));
|
||||
arg_post_processing!(self, opt, matcher);
|
||||
|
||||
return Ok(ret);
|
||||
} else if let Some(flag) = find_by_long!(self, &arg, flags) {
|
||||
debugln!("Parser::parse_long_arg: Found valid flag '{}'", flag.to_string());
|
||||
self.valid_arg = true;
|
||||
// Only flags could be help or version, and we need to check the raw long
|
||||
// so this is the first point to check
|
||||
try!(self.check_for_help_and_version_str(arg));
|
||||
|
@ -1383,6 +1393,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
.iter()
|
||||
.find(|&o| o.s.short.is_some() && o.s.short.unwrap() == c) {
|
||||
debugln!("Parser::parse_short_arg:iter: Found valid short opt -{} in '{}'", c, arg);
|
||||
self.valid_arg = true;
|
||||
// Check for trailing concatenated value
|
||||
let p: Vec<_> = arg.splitn(2, c).collect();
|
||||
debugln!("Parser::parse_short_arg:iter: arg: {:?}, arg_os: {:?}, full_arg: {:?}",
|
||||
|
@ -1410,6 +1421,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
.iter()
|
||||
.find(|&f| f.s.short.is_some() && f.s.short.unwrap() == c) {
|
||||
debugln!("Parser::parse_short_arg:iter: Found valid short flag -{}", c);
|
||||
self.valid_arg = true;
|
||||
// Only flags can be help or version
|
||||
try!(self.check_for_help_and_version_char(c));
|
||||
try!(self.parse_flag(flag, matcher));
|
||||
|
@ -2164,6 +2176,7 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
|
|||
id: self.id,
|
||||
valid_neg_num: self.valid_neg_num,
|
||||
r_ifs: self.r_ifs.clone(),
|
||||
valid_arg: self.valid_arg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,37 +4,38 @@ use std::str::FromStr;
|
|||
|
||||
bitflags! {
|
||||
flags Flags: u32 {
|
||||
const SC_NEGATE_REQS = 0b0000000000000000000000000000001,
|
||||
const SC_REQUIRED = 0b0000000000000000000000000000010,
|
||||
const A_REQUIRED_ELSE_HELP = 0b0000000000000000000000000000100,
|
||||
const GLOBAL_VERSION = 0b0000000000000000000000000001000,
|
||||
const VERSIONLESS_SC = 0b0000000000000000000000000010000,
|
||||
const UNIFIED_HELP = 0b0000000000000000000000000100000,
|
||||
const WAIT_ON_ERROR = 0b0000000000000000000000001000000,
|
||||
const SC_REQUIRED_ELSE_HELP= 0b0000000000000000000000010000000,
|
||||
const NEEDS_LONG_HELP = 0b0000000000000000000000100000000,
|
||||
const NEEDS_LONG_VERSION = 0b0000000000000000000001000000000,
|
||||
const NEEDS_SC_HELP = 0b0000000000000000000010000000000,
|
||||
const DISABLE_VERSION = 0b0000000000000000000100000000000,
|
||||
const HIDDEN = 0b0000000000000000001000000000000,
|
||||
const TRAILING_VARARG = 0b0000000000000000010000000000000,
|
||||
const NO_BIN_NAME = 0b0000000000000000100000000000000,
|
||||
const ALLOW_UNK_SC = 0b0000000000000001000000000000000,
|
||||
const UTF8_STRICT = 0b0000000000000010000000000000000,
|
||||
const UTF8_NONE = 0b0000000000000100000000000000000,
|
||||
const LEADING_HYPHEN = 0b0000000000001000000000000000000,
|
||||
const NO_POS_VALUES = 0b0000000000010000000000000000000,
|
||||
const NEXT_LINE_HELP = 0b0000000000100000000000000000000,
|
||||
const DERIVE_DISP_ORDER = 0b0000000001000000000000000000000,
|
||||
const COLORED_HELP = 0b0000000010000000000000000000000,
|
||||
const COLOR_ALWAYS = 0b0000000100000000000000000000000,
|
||||
const COLOR_AUTO = 0b0000001000000000000000000000000,
|
||||
const COLOR_NEVER = 0b0000010000000000000000000000000,
|
||||
const DONT_DELIM_TRAIL = 0b0000100000000000000000000000000,
|
||||
const ALLOW_NEG_NUMS = 0b0001000000000000000000000000000,
|
||||
const LOW_INDEX_MUL_POS = 0b0010000000000000000000000000000,
|
||||
const DISABLE_HELP_SC = 0b0100000000000000000000000000000,
|
||||
const DONT_COLLAPSE_ARGS = 0b1000000000000000000000000000000,
|
||||
const SC_NEGATE_REQS = 0b00000000000000000000000000000001,
|
||||
const SC_REQUIRED = 0b00000000000000000000000000000010,
|
||||
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000000000100,
|
||||
const GLOBAL_VERSION = 0b00000000000000000000000000001000,
|
||||
const VERSIONLESS_SC = 0b00000000000000000000000000010000,
|
||||
const UNIFIED_HELP = 0b00000000000000000000000000100000,
|
||||
const WAIT_ON_ERROR = 0b00000000000000000000000001000000,
|
||||
const SC_REQUIRED_ELSE_HELP= 0b00000000000000000000000010000000,
|
||||
const NEEDS_LONG_HELP = 0b00000000000000000000000100000000,
|
||||
const NEEDS_LONG_VERSION = 0b00000000000000000000001000000000,
|
||||
const NEEDS_SC_HELP = 0b00000000000000000000010000000000,
|
||||
const DISABLE_VERSION = 0b00000000000000000000100000000000,
|
||||
const HIDDEN = 0b00000000000000000001000000000000,
|
||||
const TRAILING_VARARG = 0b00000000000000000010000000000000,
|
||||
const NO_BIN_NAME = 0b00000000000000000100000000000000,
|
||||
const ALLOW_UNK_SC = 0b00000000000000001000000000000000,
|
||||
const UTF8_STRICT = 0b00000000000000010000000000000000,
|
||||
const UTF8_NONE = 0b00000000000000100000000000000000,
|
||||
const LEADING_HYPHEN = 0b00000000000001000000000000000000,
|
||||
const NO_POS_VALUES = 0b00000000000010000000000000000000,
|
||||
const NEXT_LINE_HELP = 0b00000000000100000000000000000000,
|
||||
const DERIVE_DISP_ORDER = 0b00000000001000000000000000000000,
|
||||
const COLORED_HELP = 0b00000000010000000000000000000000,
|
||||
const COLOR_ALWAYS = 0b00000000100000000000000000000000,
|
||||
const COLOR_AUTO = 0b00000001000000000000000000000000,
|
||||
const COLOR_NEVER = 0b00000010000000000000000000000000,
|
||||
const DONT_DELIM_TRAIL = 0b00000100000000000000000000000000,
|
||||
const ALLOW_NEG_NUMS = 0b00001000000000000000000000000000,
|
||||
const LOW_INDEX_MUL_POS = 0b00010000000000000000000000000000,
|
||||
const DISABLE_HELP_SC = 0b00100000000000000000000000000000,
|
||||
const DONT_COLLAPSE_ARGS = 0b01000000000000000000000000000000,
|
||||
const ARGS_NEGATE_SCS = 0b10000000000000000000000000000000,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +58,7 @@ impl AppFlags {
|
|||
|
||||
impl_settings! { AppSettings,
|
||||
ArgRequiredElseHelp => A_REQUIRED_ELSE_HELP,
|
||||
ArgsNegateSubcommands => ARGS_NEGATE_SCS,
|
||||
AllowExternalSubcommands => ALLOW_UNK_SC,
|
||||
AllowInvalidUtf8 => UTF8_NONE,
|
||||
AllowLeadingHyphen => LEADING_HYPHEN,
|
||||
|
@ -223,6 +225,28 @@ pub enum AppSettings {
|
|||
/// [`ArgMatches`]: ./struct.ArgMatches.html
|
||||
AllowExternalSubcommands,
|
||||
|
||||
/// Specifies that use of a valid [argument] negates [subcomands] being used after. By default
|
||||
/// `clap` allows arguments between subcommands such as
|
||||
/// `<cmd> [cmd_args] <cmd2> [cmd2_args] <cmd3> [cmd3_args]`. This setting disables that
|
||||
/// functionality and says that arguments can only follow the *final* subcommand. For instance
|
||||
/// using this setting makes only the following invocations possible:
|
||||
///
|
||||
/// * `<cmd> <cmd2> <cmd3> [cmd3_args]`
|
||||
/// * `<cmd> <cmd2> [cmd2_args]`
|
||||
/// * `<cmd> [cmd_args]`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, AppSettings};
|
||||
/// App::new("myprog")
|
||||
/// .setting(AppSettings::ArgsNegateSubcommands)
|
||||
/// # ;
|
||||
/// ```
|
||||
/// [subcommands]: ./struct.SubCommand.html
|
||||
/// [argument]: ./struct.Arg.html
|
||||
ArgsNegateSubcommands,
|
||||
|
||||
/// Specifies that the help text should be displayed (and then exit gracefully),
|
||||
/// if no arguments are present at runtime (i.e. an empty run such as, `$ myprog`.
|
||||
///
|
||||
|
@ -707,6 +731,7 @@ impl FromStr for AppSettings {
|
|||
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
|
||||
match &*s.to_ascii_lowercase() {
|
||||
"argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp),
|
||||
"argsnegatesubcommands" => Ok(AppSettings::ArgsNegateSubcommands),
|
||||
"allowinvalidutf8" => Ok(AppSettings::AllowInvalidUtf8),
|
||||
"allowleadinghyphen" => Ok(AppSettings::AllowLeadingHyphen),
|
||||
"allowexternalsubcommands" => Ok(AppSettings::AllowExternalSubcommands),
|
||||
|
@ -745,6 +770,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn app_settings_fromstr() {
|
||||
assert_eq!("argsnegatesubcommands".parse::<AppSettings>().unwrap(),
|
||||
AppSettings::ArgsNegateSubcommands);
|
||||
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(),
|
||||
AppSettings::ArgRequiredElseHelp);
|
||||
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(),
|
||||
|
|
Loading…
Reference in a new issue