Skip to content

Commit

Permalink
Add ArgPrecedenceOverSubcommand app setting
Browse files Browse the repository at this point in the history
Signed-off-by: David McNeil <[email protected]>
  • Loading branch information
davidMcneil committed Apr 16, 2020
1 parent 90e5eb6 commit 247231d
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 43 deletions.
104 changes: 63 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 ARG_PRECEDENCE_OVER_SUBCOMMAND = 1 << 41;
}
}

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

/// Specifies argument values should be greedily consumed instead of stopping when encountering
/// a subcommand during parsing.
///
/// # Examples
///
/// ```rust
/// # use clap::{App, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ArgPrecedenceOverSubcommand)
/// # ;
/// ```
ArgPrecedenceOverSubcommand,

/// Uses colorized help messages.
///
/// **NOTE:** Must be compiled with the `color` cargo feature
Expand Down Expand Up @@ -988,6 +1004,12 @@ mod test {
"argrequiredelsehelp".parse::<AppSettings>().unwrap(),
AppSettings::ArgRequiredElseHelp
);
assert_eq!(
"argprecedenceoversubcommand"
.parse::<AppSettings>()
.unwrap(),
AppSettings::ArgPrecedenceOverSubcommand
);
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 @@ -468,7 +468,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::ArgPrecedenceOverSubcommand) => {}
_ => {
let sc_name = self.possible_subcommand(arg_os);
debugln!(
Expand Down
30 changes: 30 additions & 0 deletions tests/app_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,36 @@ fn arg_required_else_help_over_reqs() {
assert_eq!(err.kind, ErrorKind::MissingArgumentOrSubcommand);
}

#[test]
fn arg_precedence_over_subcommand() {
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"]
);
assert!(matches.subcommand_matches("sub").is_some());

let app = app.setting(AppSettings::ArgPrecedenceOverSubcommand);
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", "sub"]
);
assert!(matches.subcommand_matches("sub").is_none());
}

#[cfg(not(feature = "suggestions"))]
#[test]
fn infer_subcommands_fail_no_args() {
Expand Down
4 changes: 3 additions & 1 deletion tests/subcommands.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod utils;

use clap::{App, Arg, ErrorKind};
use clap::{App, AppSettings, Arg, ErrorKind};

static VISIBLE_ALIAS_HELP: &str = "clap-test 2.6
Expand Down Expand Up @@ -291,6 +291,7 @@ fn replace() {
#[test]
fn issue_1031_args_with_same_name() {
let res = App::new("prog")
.setting(AppSettings::ArgPrecedenceOverSubcommand)
.arg(Arg::from("--ui-path=<PATH>"))
.subcommand(App::new("signer"))
.try_get_matches_from(vec!["prog", "--ui-path", "signer"]);
Expand All @@ -303,6 +304,7 @@ fn issue_1031_args_with_same_name() {
#[test]
fn issue_1031_args_with_same_name_no_more_vals() {
let res = App::new("prog")
.setting(AppSettings::ArgPrecedenceOverSubcommand)
.arg(Arg::from("--ui-path=<PATH>"))
.subcommand(App::new("signer"))
.try_get_matches_from(vec!["prog", "--ui-path", "value", "signer"]);
Expand Down

0 comments on commit 247231d

Please sign in to comment.