feat: long flag subcommand infer

Added tests and feature to infer long flag subcommands similarly to
normal subcommands.
This commit is contained in:
NickHackman 2020-07-08 00:11:28 -04:00
parent 1ea7178629
commit ec35ab8813
2 changed files with 91 additions and 2 deletions

View file

@ -865,6 +865,41 @@ where
None
}
// Checks if the arg matches a long flag subcommand name, or any of it's aliases (if defined)
fn possible_long_flag_subcommand(&self, arg_os: &ArgStr<'_>) -> Option<&str> {
debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg_os);
if self.is_set(AS::InferSubcommands) {
let options = self
.app
.get_subcommands()
.iter()
.fold(Vec::new(), |mut options, sc| {
if let Some(long) = sc.long {
if arg_os.is_prefix_of(long) {
options.push(long);
}
options.extend(
sc.get_all_aliases()
.filter(|alias| arg_os.is_prefix_of(alias)),
)
}
options
});
if options.len() == 1 {
return Some(options[0]);
}
for sc in &options {
if sc == arg_os {
return Some(sc);
}
}
} else if let Some(sc_name) = self.app.find_long_subcmd(arg_os) {
return Some(sc_name);
}
None
}
fn parse_help_subcommand(&self, cmds: &[OsString]) -> ClapResult<ParseResult> {
debug!("Parser::parse_help_subcommand");
@ -1173,7 +1208,7 @@ where
self.parse_flag(opt, matcher)?;
return Ok(ParseResult::Flag(opt.id.clone()));
} else if let Some(sc_name) = self.app.find_long_subcmd(&arg) {
} else if let Some(sc_name) = self.possible_long_flag_subcommand(&arg) {
return Ok(ParseResult::FlagSubCommand(sc_name.to_string()));
} else if self.is_set(AS::AllowLeadingHyphen) {
return Ok(ParseResult::MaybeHyphenValue);

View file

@ -1,4 +1,4 @@
use clap::{App, Arg};
use clap::{App, AppSettings, Arg, ErrorKind};
#[test]
fn flag_subcommand_normal() {
@ -274,3 +274,57 @@ fn flag_subcommand_conflict_with_version() {
.subcommand(App::new("ver").short_flag('V').long_flag("version"))
.get_matches_from(vec!["myprog", "--version"]);
}
#[test]
fn flag_subcommand_long_infer_pass() {
let m = App::new("prog")
.setting(AppSettings::InferSubcommands)
.subcommand(App::new("test").long_flag("test"))
.get_matches_from(vec!["prog", "--te"]);
assert_eq!(m.subcommand_name(), Some("test"));
}
#[cfg(not(feature = "suggestions"))]
#[test]
fn flag_subcommand_long_infer_fail() {
let m = App::new("prog")
.setting(AppSettings::InferSubcommands)
.subcommand(App::new("test").long_flag("test"))
.subcommand(App::new("temp").long_flag("temp"))
.try_get_matches_from(vec!["prog", "--te"]);
assert!(m.is_err(), "{:#?}", m.unwrap());
assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument);
}
#[cfg(feature = "suggestions")]
#[test]
fn flag_subcommand_long_infer_fail() {
let m = App::new("prog")
.setting(AppSettings::InferSubcommands)
.subcommand(App::new("test").long_flag("test"))
.subcommand(App::new("temp").long_flag("temp"))
.try_get_matches_from(vec!["prog", "--te"]);
assert!(m.is_err(), "{:#?}", m.unwrap());
assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument);
}
#[test]
fn flag_subcommands_long_infer_pass_close() {
let m = App::new("prog")
.setting(AppSettings::InferSubcommands)
.subcommand(App::new("test").long_flag("test"))
.subcommand(App::new("temp").long_flag("temp"))
.get_matches_from(vec!["prog", "--tes"]);
assert_eq!(m.subcommand_name(), Some("test"));
}
#[test]
fn flag_subcommands_long_infer_exact_match() {
let m = App::new("prog")
.setting(AppSettings::InferSubcommands)
.subcommand(App::new("test").long_flag("test"))
.subcommand(App::new("testa").long_flag("testa"))
.subcommand(App::new("testb").long_flag("testb"))
.get_matches_from(vec!["prog", "--test"]);
assert_eq!(m.subcommand_name(), Some("test"));
}