Skip to content

Commit

Permalink
Merge pull request #1834 from davidMcneil/arg_value_precedence_over_s…
Browse files Browse the repository at this point in the history
…ubcommand

Add ArgPrecedenceOverSubcommand app setting
  • Loading branch information
pksunkara authored Apr 21, 2020
2 parents 857b440 + 746d9a4 commit 4911c35
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 42 deletions.
148 changes: 107 additions & 41 deletions src/build/app/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,48 @@ use std::str::FromStr;

bitflags! {
struct Flags: u64 {
const SC_NEGATE_REQS = 1;
const SC_REQUIRED = 1 << 1;
const A_REQUIRED_ELSE_HELP = 1 << 2;
const GLOBAL_VERSION = 1 << 3;
const VERSIONLESS_SC = 1 << 4;
const UNIFIED_HELP = 1 << 5;
const WAIT_ON_ERROR = 1 << 6;
const SC_REQUIRED_ELSE_HELP= 1 << 7;
const NO_AUTO_HELP = 1 << 8;
const NO_AUTO_VERSION = 1 << 9;
const DISABLE_VERSION = 1 << 10;
const HIDDEN = 1 << 11;
const TRAILING_VARARG = 1 << 12;
const NO_BIN_NAME = 1 << 13;
const ALLOW_UNK_SC = 1 << 14;
const UTF8_STRICT = 1 << 15;
const UTF8_NONE = 1 << 16;
const LEADING_HYPHEN = 1 << 17;
const NO_POS_VALUES = 1 << 18;
const NEXT_LINE_HELP = 1 << 19;
const DERIVE_DISP_ORDER = 1 << 20;
const COLORED_HELP = 1 << 21;
const COLOR_ALWAYS = 1 << 22;
const COLOR_AUTO = 1 << 23;
const COLOR_NEVER = 1 << 24;
const DONT_DELIM_TRAIL = 1 << 25;
const ALLOW_NEG_NUMS = 1 << 26;
const LOW_INDEX_MUL_POS = 1 << 27;
const DISABLE_HELP_SC = 1 << 28;
const DONT_COLLAPSE_ARGS = 1 << 29;
const ARGS_NEGATE_SCS = 1 << 30;
const PROPAGATE_VALS_DOWN = 1 << 31;
const ALLOW_MISSING_POS = 1 << 32;
const TRAILING_VALUES = 1 << 33;
const VALID_NEG_NUM_FOUND = 1 << 34;
const BUILT = 1 << 35;
const VALID_ARG_FOUND = 1 << 36;
const INFER_SUBCOMMANDS = 1 << 37;
const CONTAINS_LAST = 1 << 38;
const ARGS_OVERRIDE_SELF = 1 << 39;
const HELP_REQUIRED = 1 << 40;
const SC_NEGATE_REQS = 1;
const SC_REQUIRED = 1 << 1;
const A_REQUIRED_ELSE_HELP = 1 << 2;
const GLOBAL_VERSION = 1 << 3;
const VERSIONLESS_SC = 1 << 4;
const UNIFIED_HELP = 1 << 5;
const WAIT_ON_ERROR = 1 << 6;
const SC_REQUIRED_ELSE_HELP = 1 << 7;
const NO_AUTO_HELP = 1 << 8;
const NO_AUTO_VERSION = 1 << 9;
const DISABLE_VERSION = 1 << 10;
const HIDDEN = 1 << 11;
const TRAILING_VARARG = 1 << 12;
const NO_BIN_NAME = 1 << 13;
const ALLOW_UNK_SC = 1 << 14;
const UTF8_STRICT = 1 << 15;
const UTF8_NONE = 1 << 16;
const LEADING_HYPHEN = 1 << 17;
const NO_POS_VALUES = 1 << 18;
const NEXT_LINE_HELP = 1 << 19;
const DERIVE_DISP_ORDER = 1 << 20;
const COLORED_HELP = 1 << 21;
const COLOR_ALWAYS = 1 << 22;
const COLOR_AUTO = 1 << 23;
const COLOR_NEVER = 1 << 24;
const DONT_DELIM_TRAIL = 1 << 25;
const ALLOW_NEG_NUMS = 1 << 26;
const LOW_INDEX_MUL_POS = 1 << 27;
const DISABLE_HELP_SC = 1 << 28;
const DONT_COLLAPSE_ARGS = 1 << 29;
const ARGS_NEGATE_SCS = 1 << 30;
const PROPAGATE_VALS_DOWN = 1 << 31;
const ALLOW_MISSING_POS = 1 << 32;
const TRAILING_VALUES = 1 << 33;
const VALID_NEG_NUM_FOUND = 1 << 34;
const BUILT = 1 << 35;
const VALID_ARG_FOUND = 1 << 36;
const INFER_SUBCOMMANDS = 1 << 37;
const CONTAINS_LAST = 1 << 38;
const ARGS_OVERRIDE_SELF = 1 << 39;
const HELP_REQUIRED = 1 << 40;
const SUBCOMMAND_PRECEDENCE_OVER_ARG = 1 << 41;
}
}

Expand All @@ -69,6 +70,8 @@ impl Default for AppFlags {
impl_settings! { AppSettings, AppFlags,
ArgRequiredElseHelp("argrequiredelsehelp")
=> Flags::A_REQUIRED_ELSE_HELP,
SubcommandPrecedenceOverArg("subcommandprecedenceoverarg")
=> Flags::SUBCOMMAND_PRECEDENCE_OVER_ARG,
ArgsNegateSubcommands("argsnegatesubcommands")
=> Flags::ARGS_NEGATE_SCS,
AllowExternalSubcommands("allowexternalsubcommands")
Expand Down Expand Up @@ -436,6 +439,63 @@ pub enum AppSettings {
/// [`Arg::default_value`]: ./struct.Arg.html#method.default_value
ArgRequiredElseHelp,

/// Instructs the parser to stop when encountering a subcommand instead of greedily consuming
/// args.
///
/// By default, if an option taking multiple values is followed by a subcommand, the
/// subcommand will be parsed as another value.
///
/// ```text
/// app --foo val1 val2 subcommand
/// --------- ----------
/// values another value
/// ```
///
/// This setting instructs the parser to stop when encountering a subcommand instead of
/// greedily consuming arguments.
///
/// ```text
/// app --foo val1 val2 subcommand
/// --------- ----------
/// values subcommand
/// ```
///
/// **Note:** Make sure you apply it as `global_setting` if you want it to be propagated to
/// sub-sub commands!
///
/// # Examples
///
/// ```rust
/// # use clap::{App, AppSettings, Arg};
/// let app = App::new("app").subcommand(App::new("sub")).arg(
/// Arg::with_name("arg")
/// .long("arg")
/// .multiple(true)
/// .takes_value(true),
/// );
///
/// let matches = app
/// .clone()
/// .try_get_matches_from(&["app", "--arg", "1", "2", "3", "sub"])
/// .unwrap();
/// assert_eq!(
/// matches.values_of("arg").unwrap().collect::<Vec<_>>(),
/// &["1", "2", "3", "sub"]
/// );
/// assert!(matches.subcommand_matches("sub").is_none());
///
/// let app = app.setting(AppSettings::SubcommandPrecedenceOverArg);
/// let matches = app
/// .try_get_matches_from(&["app", "--arg", "1", "2", "3", "sub"])
/// .unwrap();
/// assert_eq!(
/// matches.values_of("arg").unwrap().collect::<Vec<_>>(),
/// &["1", "2", "3"]
/// );
/// assert!(matches.subcommand_matches("sub").is_some());
/// ```
SubcommandPrecedenceOverArg,

/// Uses colorized help messages.
///
/// **NOTE:** Must be compiled with the `color` cargo feature
Expand Down Expand Up @@ -988,6 +1048,12 @@ mod test {
"argrequiredelsehelp".parse::<AppSettings>().unwrap(),
AppSettings::ArgRequiredElseHelp
);
assert_eq!(
"subcommandprecedenceoverarg"
.parse::<AppSettings>()
.unwrap(),
AppSettings::SubcommandPrecedenceOverArg
);
assert_eq!(
"allowexternalsubcommands".parse::<AppSettings>().unwrap(),
AppSettings::AllowExternalSubcommands
Expand Down
3 changes: 2 additions & 1 deletion src/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,8 @@ where
if !self.is_set(AS::TrailingValues) {
// Does the arg match a subcommand name, or any of it's aliases (if defined)
match needs_val_of {
ParseResult::Opt(_) | ParseResult::Pos(_) => (),
ParseResult::Opt(_) | ParseResult::Pos(_)
if !self.is_set(AS::SubcommandPrecedenceOverArg) => {}
_ => {
let sc_name = self.possible_subcommand(arg_os);
debugln!(
Expand Down

0 comments on commit 4911c35

Please sign in to comment.