mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
Merge pull request #2525 from tertsdiepraam/infer-long-args
Infer long arguments
This commit is contained in:
commit
dc8d0f1b08
3 changed files with 80 additions and 5 deletions
|
@ -50,6 +50,7 @@ bitflags! {
|
||||||
const DISABLE_HELP_FLAG = 1 << 42;
|
const DISABLE_HELP_FLAG = 1 << 42;
|
||||||
const USE_LONG_FORMAT_FOR_HELP_SC = 1 << 43;
|
const USE_LONG_FORMAT_FOR_HELP_SC = 1 << 43;
|
||||||
const DISABLE_ENV = 1 << 44;
|
const DISABLE_ENV = 1 << 44;
|
||||||
|
const INFER_LONG_ARGS = 1 << 45;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +154,9 @@ impl_settings! { AppSettings, AppFlags,
|
||||||
InferSubcommands("infersubcommands")
|
InferSubcommands("infersubcommands")
|
||||||
=> Flags::INFER_SUBCOMMANDS,
|
=> Flags::INFER_SUBCOMMANDS,
|
||||||
AllArgsOverrideSelf("allargsoverrideself")
|
AllArgsOverrideSelf("allargsoverrideself")
|
||||||
=> Flags::ARGS_OVERRIDE_SELF
|
=> Flags::ARGS_OVERRIDE_SELF,
|
||||||
|
InferLongArgs("inferlongargs")
|
||||||
|
=> Flags::INFER_LONG_ARGS
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Application level settings, which affect how [`App`] operates
|
/// Application level settings, which affect how [`App`] operates
|
||||||
|
@ -826,6 +829,14 @@ pub enum AppSettings {
|
||||||
/// [aliases]: App::alias()
|
/// [aliases]: App::alias()
|
||||||
InferSubcommands,
|
InferSubcommands,
|
||||||
|
|
||||||
|
/// Tries to match unknown args to partial long arguments or their [aliases]. For example, to
|
||||||
|
/// match an argument named `--test`, one could use `--t`, `--te`, `--tes`, and `--test`.
|
||||||
|
///
|
||||||
|
/// **NOTE:** The match *must not* be ambiguous at all in order to succeed. i.e. to match
|
||||||
|
/// `--te` to `--test` there could not also be another argument or alias `--temp` because both
|
||||||
|
/// start with `--te`
|
||||||
|
InferLongArgs,
|
||||||
|
|
||||||
/// Specifies that the parser should not assume the first argument passed is the binary name.
|
/// Specifies that the parser should not assume the first argument passed is the binary name.
|
||||||
/// This is normally the case when using a "daemon" style mode, or an interactive CLI where one
|
/// This is normally the case when using a "daemon" style mode, or an interactive CLI where one
|
||||||
/// one would not normally type the binary or program name for each command.
|
/// one would not normally type the binary or program name for each command.
|
||||||
|
|
|
@ -1041,6 +1041,7 @@ impl<'help, 'app> Parser<'help, 'app> {
|
||||||
debug!("No");
|
debug!("No");
|
||||||
(long_arg, None)
|
(long_arg, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(opt) = self.app.args.get(&arg.to_os_string()) {
|
if let Some(opt) = self.app.args.get(&arg.to_os_string()) {
|
||||||
debug!(
|
debug!(
|
||||||
"Parser::parse_long_arg: Found valid opt or flag '{}'",
|
"Parser::parse_long_arg: Found valid opt or flag '{}'",
|
||||||
|
@ -1049,12 +1050,45 @@ impl<'help, 'app> Parser<'help, 'app> {
|
||||||
self.app.settings.set(AS::ValidArgFound);
|
self.app.settings.set(AS::ValidArgFound);
|
||||||
self.seen.push(opt.id.clone());
|
self.seen.push(opt.id.clone());
|
||||||
if opt.is_set(ArgSettings::TakesValue) {
|
if opt.is_set(ArgSettings::TakesValue) {
|
||||||
Ok(self.parse_opt(&val, opt, matcher)?)
|
return self.parse_opt(&val, opt, matcher);
|
||||||
} else {
|
} else {
|
||||||
self.check_for_help_and_version_str(&arg)?;
|
self.check_for_help_and_version_str(&arg)?;
|
||||||
Ok(self.parse_flag(opt, matcher))
|
return Ok(self.parse_flag(opt, matcher));
|
||||||
}
|
}
|
||||||
} else if let Some(sc_name) = self.possible_long_flag_subcommand(&arg) {
|
}
|
||||||
|
|
||||||
|
if self.is_set(AS::InferLongArgs) {
|
||||||
|
let arg_str = arg.to_string_lossy();
|
||||||
|
let matches: Vec<_> = self
|
||||||
|
.app
|
||||||
|
.args
|
||||||
|
.args()
|
||||||
|
.filter(|a| {
|
||||||
|
a.long
|
||||||
|
.map_or(false, |long| long.starts_with(arg_str.as_ref()))
|
||||||
|
|| a.aliases
|
||||||
|
.iter()
|
||||||
|
.any(|(alias, _)| alias.starts_with(arg_str.as_ref()))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let [opt] = matches.as_slice() {
|
||||||
|
debug!(
|
||||||
|
"Parser::parse_long_arg: Found valid opt or flag '{}'",
|
||||||
|
opt.to_string()
|
||||||
|
);
|
||||||
|
self.app.settings.set(AS::ValidArgFound);
|
||||||
|
self.seen.push(opt.id.clone());
|
||||||
|
if opt.is_set(ArgSettings::TakesValue) {
|
||||||
|
return self.parse_opt(&val, opt, matcher);
|
||||||
|
} else {
|
||||||
|
self.check_for_help_and_version_str(&arg)?;
|
||||||
|
return Ok(self.parse_flag(opt, matcher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sc_name) = self.possible_long_flag_subcommand(&arg) {
|
||||||
Ok(ParseResult::FlagSubCommand(sc_name.to_string()))
|
Ok(ParseResult::FlagSubCommand(sc_name.to_string()))
|
||||||
} else if self.is_set(AS::AllowLeadingHyphen) {
|
} else if self.is_set(AS::AllowLeadingHyphen) {
|
||||||
Ok(ParseResult::MaybeHyphenValue)
|
Ok(ParseResult::MaybeHyphenValue)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches, ArgSettings, ErrorKind};
|
use clap::{App, AppSettings, Arg, ArgMatches, ArgSettings, ErrorKind};
|
||||||
|
|
||||||
#[cfg(feature = "suggestions")]
|
#[cfg(feature = "suggestions")]
|
||||||
static DYM: &str =
|
static DYM: &str =
|
||||||
|
@ -577,3 +577,33 @@ fn issue_2279() {
|
||||||
|
|
||||||
assert_eq!(after_help_heading.value_of("foo"), Some("bar"));
|
assert_eq!(after_help_heading.value_of("foo"), Some("bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_long_arg() {
|
||||||
|
let app = App::new("test")
|
||||||
|
.setting(AppSettings::InferLongArgs)
|
||||||
|
.arg(Arg::new("racetrack").long("racetrack").alias("autobahn"))
|
||||||
|
.arg(Arg::new("racecar").long("racecar").takes_value(true));
|
||||||
|
|
||||||
|
let matches = app.clone().get_matches_from(&["test", "--racec=hello"]);
|
||||||
|
assert!(!matches.is_present("racetrack"));
|
||||||
|
assert_eq!(matches.value_of("racecar"), Some("hello"));
|
||||||
|
|
||||||
|
let matches = app.clone().get_matches_from(&["test", "--racet"]);
|
||||||
|
assert!(matches.is_present("racetrack"));
|
||||||
|
assert_eq!(matches.value_of("racecar"), None);
|
||||||
|
|
||||||
|
let matches = app.clone().get_matches_from(&["test", "--auto"]);
|
||||||
|
assert!(matches.is_present("racetrack"));
|
||||||
|
assert_eq!(matches.value_of("racecar"), None);
|
||||||
|
|
||||||
|
let app = App::new("test")
|
||||||
|
.setting(AppSettings::InferLongArgs)
|
||||||
|
.arg(Arg::new("arg").long("arg"));
|
||||||
|
|
||||||
|
let matches = app.clone().get_matches_from(&["test", "--"]);
|
||||||
|
assert!(!matches.is_present("arg"));
|
||||||
|
|
||||||
|
let matches = app.clone().get_matches_from(&["test", "--a"]);
|
||||||
|
assert!(matches.is_present("arg"));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue