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,
|
settings: AppFlags,
|
||||||
pub g_settings: Vec<AppSettings>,
|
pub g_settings: Vec<AppSettings>,
|
||||||
pub meta: AppMeta<'b>,
|
pub meta: AppMeta<'b>,
|
||||||
trailing_vals: bool,
|
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
|
trailing_vals: bool,
|
||||||
valid_neg_num: bool,
|
valid_neg_num: bool,
|
||||||
|
// have we found a valid arg yet
|
||||||
|
valid_arg: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Default for Parser<'a, 'b> {
|
impl<'a, 'b> Default for Parser<'a, 'b> {
|
||||||
|
@ -87,6 +89,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
|
||||||
trailing_vals: false,
|
trailing_vals: false,
|
||||||
id: 0,
|
id: 0,
|
||||||
valid_neg_num: false,
|
valid_neg_num: false,
|
||||||
|
valid_arg: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,7 +461,8 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
.next()
|
.next()
|
||||||
.expect(INTERNAL_ERROR_MSG);
|
.expect(INTERNAL_ERROR_MSG);
|
||||||
return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()));
|
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
|
return Some(self.positionals
|
||||||
.values()
|
.values()
|
||||||
.filter(|p| !p.is_set(ArgSettings::Required))
|
.filter(|p| !p.is_set(ArgSettings::Required))
|
||||||
|
@ -608,6 +612,9 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
#[inline]
|
#[inline]
|
||||||
fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
|
fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
|
||||||
debugln!("Parser::possible_subcommand;");
|
debugln!("Parser::possible_subcommand;");
|
||||||
|
if self.is_set(AppSettings::ArgsNegateSubcommands) && self.valid_arg {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
self.subcommands
|
self.subcommands
|
||||||
.iter()
|
.iter()
|
||||||
.any(|s| {
|
.any(|s| {
|
||||||
|
@ -835,6 +842,8 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
}
|
}
|
||||||
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
|
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
|
||||||
break;
|
break;
|
||||||
|
} else if self.is_set(AppSettings::ArgsNegateSubcommands) {
|
||||||
|
();
|
||||||
} else if let Some(cdate) =
|
} else if let Some(cdate) =
|
||||||
suggestions::did_you_mean(&*arg_os.to_string_lossy(),
|
suggestions::did_you_mean(&*arg_os.to_string_lossy(),
|
||||||
self.subcommands
|
self.subcommands
|
||||||
|
@ -877,6 +886,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
}
|
}
|
||||||
if let Some(p) = self.positionals.get(pos_counter) {
|
if let Some(p) = self.positionals.get(pos_counter) {
|
||||||
parse_positional!(self, p, arg_os, pos_counter, matcher);
|
parse_positional!(self, p, arg_os, pos_counter, matcher);
|
||||||
|
self.valid_arg = true;
|
||||||
} else if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
|
} else if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
|
||||||
// Get external subcommand name
|
// Get external subcommand name
|
||||||
let sc_name = match arg_os.to_str() {
|
let sc_name = match arg_os.to_str() {
|
||||||
|
@ -922,17 +932,15 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
let sc_name = &*self.subcommands
|
let sc_name = &*self.subcommands
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|sc| sc.p.meta.aliases.is_some())
|
.filter(|sc| sc.p.meta.aliases.is_some())
|
||||||
.filter_map(|sc| if sc.p
|
.filter(|sc| sc.p
|
||||||
.meta
|
.meta
|
||||||
.aliases
|
.aliases
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.expect(INTERNAL_ERROR_MSG)
|
||||||
.iter()
|
.iter()
|
||||||
.any(|&(a, _)| &a == &&*pos_sc_name) {
|
.any(|&(a, _)| &a == &&*pos_sc_name)
|
||||||
Some(sc.p.meta.name.clone())
|
)
|
||||||
} else {
|
.map(|sc| sc.p.meta.name.clone())
|
||||||
None
|
|
||||||
})
|
|
||||||
.next()
|
.next()
|
||||||
.expect(INTERNAL_ERROR_MSG);
|
.expect(INTERNAL_ERROR_MSG);
|
||||||
try!(self.parse_subcommand(sc_name, matcher, it));
|
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) {
|
if let Some(opt) = find_by_long!(self, &arg, opts) {
|
||||||
debugln!("Parser::parse_long_arg: Found valid opt '{}'", opt.to_string());
|
debugln!("Parser::parse_long_arg: Found valid opt '{}'", opt.to_string());
|
||||||
|
self.valid_arg = true;
|
||||||
let ret = try!(self.parse_opt(val, opt, matcher));
|
let ret = try!(self.parse_opt(val, opt, matcher));
|
||||||
arg_post_processing!(self, opt, matcher);
|
arg_post_processing!(self, opt, matcher);
|
||||||
|
|
||||||
return Ok(ret);
|
return Ok(ret);
|
||||||
} else if let Some(flag) = find_by_long!(self, &arg, flags) {
|
} else if let Some(flag) = find_by_long!(self, &arg, flags) {
|
||||||
debugln!("Parser::parse_long_arg: Found valid flag '{}'", flag.to_string());
|
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
|
// Only flags could be help or version, and we need to check the raw long
|
||||||
// so this is the first point to check
|
// so this is the first point to check
|
||||||
try!(self.check_for_help_and_version_str(arg));
|
try!(self.check_for_help_and_version_str(arg));
|
||||||
|
@ -1383,6 +1393,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
.iter()
|
.iter()
|
||||||
.find(|&o| o.s.short.is_some() && o.s.short.unwrap() == c) {
|
.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);
|
debugln!("Parser::parse_short_arg:iter: Found valid short opt -{} in '{}'", c, arg);
|
||||||
|
self.valid_arg = true;
|
||||||
// Check for trailing concatenated value
|
// Check for trailing concatenated value
|
||||||
let p: Vec<_> = arg.splitn(2, c).collect();
|
let p: Vec<_> = arg.splitn(2, c).collect();
|
||||||
debugln!("Parser::parse_short_arg:iter: arg: {:?}, arg_os: {:?}, full_arg: {:?}",
|
debugln!("Parser::parse_short_arg:iter: arg: {:?}, arg_os: {:?}, full_arg: {:?}",
|
||||||
|
@ -1410,6 +1421,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
.iter()
|
.iter()
|
||||||
.find(|&f| f.s.short.is_some() && f.s.short.unwrap() == c) {
|
.find(|&f| f.s.short.is_some() && f.s.short.unwrap() == c) {
|
||||||
debugln!("Parser::parse_short_arg:iter: Found valid short flag -{}", c);
|
debugln!("Parser::parse_short_arg:iter: Found valid short flag -{}", c);
|
||||||
|
self.valid_arg = true;
|
||||||
// Only flags can be help or version
|
// Only flags can be help or version
|
||||||
try!(self.check_for_help_and_version_char(c));
|
try!(self.check_for_help_and_version_char(c));
|
||||||
try!(self.parse_flag(flag, matcher));
|
try!(self.parse_flag(flag, matcher));
|
||||||
|
@ -2164,6 +2176,7 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
|
||||||
id: self.id,
|
id: self.id,
|
||||||
valid_neg_num: self.valid_neg_num,
|
valid_neg_num: self.valid_neg_num,
|
||||||
r_ifs: self.r_ifs.clone(),
|
r_ifs: self.r_ifs.clone(),
|
||||||
|
valid_arg: self.valid_arg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,37 +4,38 @@ use std::str::FromStr;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
flags Flags: u32 {
|
flags Flags: u32 {
|
||||||
const SC_NEGATE_REQS = 0b0000000000000000000000000000001,
|
const SC_NEGATE_REQS = 0b00000000000000000000000000000001,
|
||||||
const SC_REQUIRED = 0b0000000000000000000000000000010,
|
const SC_REQUIRED = 0b00000000000000000000000000000010,
|
||||||
const A_REQUIRED_ELSE_HELP = 0b0000000000000000000000000000100,
|
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000000000100,
|
||||||
const GLOBAL_VERSION = 0b0000000000000000000000000001000,
|
const GLOBAL_VERSION = 0b00000000000000000000000000001000,
|
||||||
const VERSIONLESS_SC = 0b0000000000000000000000000010000,
|
const VERSIONLESS_SC = 0b00000000000000000000000000010000,
|
||||||
const UNIFIED_HELP = 0b0000000000000000000000000100000,
|
const UNIFIED_HELP = 0b00000000000000000000000000100000,
|
||||||
const WAIT_ON_ERROR = 0b0000000000000000000000001000000,
|
const WAIT_ON_ERROR = 0b00000000000000000000000001000000,
|
||||||
const SC_REQUIRED_ELSE_HELP= 0b0000000000000000000000010000000,
|
const SC_REQUIRED_ELSE_HELP= 0b00000000000000000000000010000000,
|
||||||
const NEEDS_LONG_HELP = 0b0000000000000000000000100000000,
|
const NEEDS_LONG_HELP = 0b00000000000000000000000100000000,
|
||||||
const NEEDS_LONG_VERSION = 0b0000000000000000000001000000000,
|
const NEEDS_LONG_VERSION = 0b00000000000000000000001000000000,
|
||||||
const NEEDS_SC_HELP = 0b0000000000000000000010000000000,
|
const NEEDS_SC_HELP = 0b00000000000000000000010000000000,
|
||||||
const DISABLE_VERSION = 0b0000000000000000000100000000000,
|
const DISABLE_VERSION = 0b00000000000000000000100000000000,
|
||||||
const HIDDEN = 0b0000000000000000001000000000000,
|
const HIDDEN = 0b00000000000000000001000000000000,
|
||||||
const TRAILING_VARARG = 0b0000000000000000010000000000000,
|
const TRAILING_VARARG = 0b00000000000000000010000000000000,
|
||||||
const NO_BIN_NAME = 0b0000000000000000100000000000000,
|
const NO_BIN_NAME = 0b00000000000000000100000000000000,
|
||||||
const ALLOW_UNK_SC = 0b0000000000000001000000000000000,
|
const ALLOW_UNK_SC = 0b00000000000000001000000000000000,
|
||||||
const UTF8_STRICT = 0b0000000000000010000000000000000,
|
const UTF8_STRICT = 0b00000000000000010000000000000000,
|
||||||
const UTF8_NONE = 0b0000000000000100000000000000000,
|
const UTF8_NONE = 0b00000000000000100000000000000000,
|
||||||
const LEADING_HYPHEN = 0b0000000000001000000000000000000,
|
const LEADING_HYPHEN = 0b00000000000001000000000000000000,
|
||||||
const NO_POS_VALUES = 0b0000000000010000000000000000000,
|
const NO_POS_VALUES = 0b00000000000010000000000000000000,
|
||||||
const NEXT_LINE_HELP = 0b0000000000100000000000000000000,
|
const NEXT_LINE_HELP = 0b00000000000100000000000000000000,
|
||||||
const DERIVE_DISP_ORDER = 0b0000000001000000000000000000000,
|
const DERIVE_DISP_ORDER = 0b00000000001000000000000000000000,
|
||||||
const COLORED_HELP = 0b0000000010000000000000000000000,
|
const COLORED_HELP = 0b00000000010000000000000000000000,
|
||||||
const COLOR_ALWAYS = 0b0000000100000000000000000000000,
|
const COLOR_ALWAYS = 0b00000000100000000000000000000000,
|
||||||
const COLOR_AUTO = 0b0000001000000000000000000000000,
|
const COLOR_AUTO = 0b00000001000000000000000000000000,
|
||||||
const COLOR_NEVER = 0b0000010000000000000000000000000,
|
const COLOR_NEVER = 0b00000010000000000000000000000000,
|
||||||
const DONT_DELIM_TRAIL = 0b0000100000000000000000000000000,
|
const DONT_DELIM_TRAIL = 0b00000100000000000000000000000000,
|
||||||
const ALLOW_NEG_NUMS = 0b0001000000000000000000000000000,
|
const ALLOW_NEG_NUMS = 0b00001000000000000000000000000000,
|
||||||
const LOW_INDEX_MUL_POS = 0b0010000000000000000000000000000,
|
const LOW_INDEX_MUL_POS = 0b00010000000000000000000000000000,
|
||||||
const DISABLE_HELP_SC = 0b0100000000000000000000000000000,
|
const DISABLE_HELP_SC = 0b00100000000000000000000000000000,
|
||||||
const DONT_COLLAPSE_ARGS = 0b1000000000000000000000000000000,
|
const DONT_COLLAPSE_ARGS = 0b01000000000000000000000000000000,
|
||||||
|
const ARGS_NEGATE_SCS = 0b10000000000000000000000000000000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ impl AppFlags {
|
||||||
|
|
||||||
impl_settings! { AppSettings,
|
impl_settings! { AppSettings,
|
||||||
ArgRequiredElseHelp => A_REQUIRED_ELSE_HELP,
|
ArgRequiredElseHelp => A_REQUIRED_ELSE_HELP,
|
||||||
|
ArgsNegateSubcommands => ARGS_NEGATE_SCS,
|
||||||
AllowExternalSubcommands => ALLOW_UNK_SC,
|
AllowExternalSubcommands => ALLOW_UNK_SC,
|
||||||
AllowInvalidUtf8 => UTF8_NONE,
|
AllowInvalidUtf8 => UTF8_NONE,
|
||||||
AllowLeadingHyphen => LEADING_HYPHEN,
|
AllowLeadingHyphen => LEADING_HYPHEN,
|
||||||
|
@ -223,6 +225,28 @@ pub enum AppSettings {
|
||||||
/// [`ArgMatches`]: ./struct.ArgMatches.html
|
/// [`ArgMatches`]: ./struct.ArgMatches.html
|
||||||
AllowExternalSubcommands,
|
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),
|
/// 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`.
|
/// 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> {
|
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
|
||||||
match &*s.to_ascii_lowercase() {
|
match &*s.to_ascii_lowercase() {
|
||||||
"argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp),
|
"argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp),
|
||||||
|
"argsnegatesubcommands" => Ok(AppSettings::ArgsNegateSubcommands),
|
||||||
"allowinvalidutf8" => Ok(AppSettings::AllowInvalidUtf8),
|
"allowinvalidutf8" => Ok(AppSettings::AllowInvalidUtf8),
|
||||||
"allowleadinghyphen" => Ok(AppSettings::AllowLeadingHyphen),
|
"allowleadinghyphen" => Ok(AppSettings::AllowLeadingHyphen),
|
||||||
"allowexternalsubcommands" => Ok(AppSettings::AllowExternalSubcommands),
|
"allowexternalsubcommands" => Ok(AppSettings::AllowExternalSubcommands),
|
||||||
|
@ -745,6 +770,8 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn app_settings_fromstr() {
|
fn app_settings_fromstr() {
|
||||||
|
assert_eq!("argsnegatesubcommands".parse::<AppSettings>().unwrap(),
|
||||||
|
AppSettings::ArgsNegateSubcommands);
|
||||||
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(),
|
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(),
|
||||||
AppSettings::ArgRequiredElseHelp);
|
AppSettings::ArgRequiredElseHelp);
|
||||||
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(),
|
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(),
|
||||||
|
|
Loading…
Reference in a new issue