From 09d1d4a5681961e4d8dfe909f196ee1277c58a98 Mon Sep 17 00:00:00 2001 From: Alex Helfet Date: Sat, 16 Dec 2017 10:09:09 +0000 Subject: [PATCH 01/15] tests(auto-completion): Normalise names in tests/completions.rs From -> To =========================================== with_underscore -> (with_)?special_commands WUS -> SPECIAL_CMDS special -> special_help SPECIAL -> SPECIAL_HELP --- tests/completions.rs | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/completions.rs b/tests/completions.rs index 1afc6bcb..eaedefcb 100644 --- a/tests/completions.rs +++ b/tests/completions.rs @@ -270,7 +270,7 @@ static POWERSHELL: &'static str = r#" "#; #[cfg(not(target_os="windows"))] -static POWERSHELL_WUS: &'static str = r#" +static POWERSHELL_SPECIAL_CMDS: &'static str = r#" @('my_app', './my_app') | %{ Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) @@ -330,7 +330,7 @@ static POWERSHELL_WUS: &'static str = r#" "#; #[cfg(target_os="windows")] -static POWERSHELL_WUS: &'static str = r#" +static POWERSHELL_SPECIAL_CMDS: &'static str = r#" @('my_app', './my_app', 'my_app.exe', '.\my_app', '.\my_app.exe', './my_app.exe') | %{ Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) @@ -376,7 +376,7 @@ static POWERSHELL_WUS: &'static str = r#" } "#; -static ZSH_WUS: &'static str = r#"#compdef my_app +static ZSH_SPECIAL_CMDS: &'static str = r#"#compdef my_app _my_app() { typeset -A opt_args @@ -460,7 +460,7 @@ _my_app__test_commands() { _my_app "$@""#; -static FISH_WUS: &'static str = r#"function __fish_using_command +static FISH_SPECIAL_CMDS: &'static str = r#"function __fish_using_command set cmd (commandline -opc) if [ (count $cmd) -eq (count $argv) ] for i in (seq (count $argv)) @@ -488,7 +488,7 @@ complete -c my_app -n "__fish_using_command my_app help" -s h -l help -d 'Prints complete -c my_app -n "__fish_using_command my_app help" -s V -l version -d 'Prints version information' "#; -static BASH_WUS: &'static str = r#"_my_app() { +static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() { local i cur prev opts cmds COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" @@ -593,7 +593,7 @@ static BASH_WUS: &'static str = r#"_my_app() { complete -F _my_app -o bashdefault -o default my_app "#; -static FISH_SPECIAL: &'static str = r#"function __fish_using_command +static FISH_SPECIAL_HELP: &'static str = r#"function __fish_using_command set cmd (commandline -opc) if [ (count $cmd) -eq (count $argv) ] for i in (seq (count $argv)) @@ -616,7 +616,7 @@ complete -c my_app -n "__fish_using_command my_app" -s h -l help -d 'Prints help complete -c my_app -n "__fish_using_command my_app" -s V -l version -d 'Prints version information' "#; -static ZSH_SPECIAL: &'static str = r#"#compdef my_app +static ZSH_SPECIAL_HELP: &'static str = r#"#compdef my_app _my_app() { typeset -A opt_args @@ -685,7 +685,7 @@ fn build_app_with_name(s: &'static str) -> App<'static, 'static> { .help("the case to test"))) } -fn build_app_with_underscore() -> App<'static, 'static> { +fn build_app_special_commands() -> App<'static, 'static> { build_app_with_name("my_app").subcommand(SubCommand::with_name("some_cmd") .about("tests other things") .arg(Arg::with_name("config") @@ -694,7 +694,7 @@ fn build_app_with_underscore() -> App<'static, 'static> { .help("the other case to test"))) } -fn build_app_special() -> App<'static, 'static> { +fn build_app_special_help() -> App<'static, 'static> { App::new("my_app") .arg(Arg::with_name("single-quotes") .long("single-quotes") @@ -759,61 +759,61 @@ fn fish() { // Disabled until I figure out this windows line ending and AppVeyor issues //#[test] -// fn powershell_with_underscore() { -// let mut app = build_app_with_underscore(); +// fn powershell_with_special_commands() { +// let mut app = build_app_special_commands(); // let mut buf = vec![]; // app.gen_completions_to("my_app", Shell::PowerShell, &mut buf); // let string = String::from_utf8(buf).unwrap(); // -// assert!(compare(&*string, POWERSHELL_WUS)); +// assert!(compare(&*string, POWERSHELL_SPECIAL_CMDS)); // } #[test] -fn bash_with_underscore() { - let mut app = build_app_with_underscore(); +fn bash_with_special_commands() { + let mut app = build_app_special_commands(); let mut buf = vec![]; app.gen_completions_to("my_app", Shell::Bash, &mut buf); let string = String::from_utf8(buf).unwrap(); - assert!(compare(&*string, BASH_WUS)); + assert!(compare(&*string, BASH_SPECIAL_CMDS)); } #[test] -fn fish_with_underscore() { - let mut app = build_app_with_underscore(); +fn fish_with_special_commands() { + let mut app = build_app_special_commands(); let mut buf = vec![]; app.gen_completions_to("my_app", Shell::Fish, &mut buf); let string = String::from_utf8(buf).unwrap(); - assert!(compare(&*string, FISH_WUS)); + assert!(compare(&*string, FISH_SPECIAL_CMDS)); } #[test] -fn zsh_with_underscore() { - let mut app = build_app_with_underscore(); +fn zsh_with_special_commands() { + let mut app = build_app_special_commands(); let mut buf = vec![]; app.gen_completions_to("my_app", Shell::Zsh, &mut buf); let string = String::from_utf8(buf).unwrap(); - assert!(compare(&*string, ZSH_WUS)); + assert!(compare(&*string, ZSH_SPECIAL_CMDS)); } #[test] -fn fish_special() { - let mut app = build_app_special(); +fn fish_with_special_help() { + let mut app = build_app_special_help(); let mut buf = vec![]; app.gen_completions_to("my_app", Shell::Fish, &mut buf); let string = String::from_utf8(buf).unwrap(); - assert!(compare(&*string, FISH_SPECIAL)); + assert!(compare(&*string, FISH_SPECIAL_HELP)); } #[test] -fn zsh_special() { - let mut app = build_app_special(); +fn zsh_with_special_help() { + let mut app = build_app_special_help(); let mut buf = vec![]; app.gen_completions_to("my_app", Shell::Zsh, &mut buf); let string = String::from_utf8(buf).unwrap(); - assert!(compare(&*string, ZSH_SPECIAL)); + assert!(compare(&*string, ZSH_SPECIAL_HELP)); } From b446a561cef1b8054bb5f4c3451b888771680cf7 Mon Sep 17 00:00:00 2001 From: Alex Helfet Date: Sat, 16 Dec 2017 10:48:33 +0000 Subject: [PATCH 02/15] tests(auto-completion): Update build_app_special_commands to include a sub-command with a hypen. The tests pass but the bash completion script won't work yet. --- tests/completions.rs | 53 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/tests/completions.rs b/tests/completions.rs index eaedefcb..456944d3 100644 --- a/tests/completions.rs +++ b/tests/completions.rs @@ -413,6 +413,14 @@ _arguments -s -S -C \ '--version[Prints version information]' \ && ret=0 ;; +(some-cmd-with-hypens) +_arguments -s -S -C \ +'-h[Prints help information]' \ +'--help[Prints help information]' \ +'-V[Prints version information]' \ +'--version[Prints version information]' \ +&& ret=0 +;; (help) _arguments -s -S -C \ '-h[Prints help information]' \ @@ -431,6 +439,7 @@ _my_app_commands() { local commands; commands=( "test:tests things" \ "some_cmd:tests other things" \ +"some-cmd-with-hypens:" \ "help:Prints this message or the help of the given subcommand(s)" \ "FILE:some input file" \ ) @@ -443,6 +452,13 @@ _my_app__help_commands() { ) _describe -t commands 'my_app help commands' commands "$@" } +(( $+functions[_my_app__some-cmd-with-hypens_commands] )) || +_my_app__some-cmd-with-hypens_commands() { + local commands; commands=( + + ) + _describe -t commands 'my_app some-cmd-with-hypens commands' commands "$@" +} (( $+functions[_my_app__some_cmd_commands] )) || _my_app__some_cmd_commands() { local commands; commands=( @@ -477,6 +493,7 @@ complete -c my_app -n "__fish_using_command my_app" -s h -l help -d 'Prints help complete -c my_app -n "__fish_using_command my_app" -s V -l version -d 'Prints version information' complete -c my_app -n "__fish_using_command my_app" -f -a "test" -d 'tests things' complete -c my_app -n "__fish_using_command my_app" -f -a "some_cmd" -d 'tests other things' +complete -c my_app -n "__fish_using_command my_app" -f -a "some-cmd-with-hypens" complete -c my_app -n "__fish_using_command my_app" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)' complete -c my_app -n "__fish_using_command my_app test" -l case -d 'the case to test' complete -c my_app -n "__fish_using_command my_app test" -s h -l help -d 'Prints help information' @@ -484,6 +501,8 @@ complete -c my_app -n "__fish_using_command my_app test" -s V -l version -d 'Pri complete -c my_app -n "__fish_using_command my_app some_cmd" -l config -d 'the other case to test' complete -c my_app -n "__fish_using_command my_app some_cmd" -s h -l help -d 'Prints help information' complete -c my_app -n "__fish_using_command my_app some_cmd" -s V -l version -d 'Prints version information' +complete -c my_app -n "__fish_using_command my_app some-cmd-with-hypens" -s h -l help -d 'Prints help information' +complete -c my_app -n "__fish_using_command my_app some-cmd-with-hypens" -s V -l version -d 'Prints version information' complete -c my_app -n "__fish_using_command my_app help" -s h -l help -d 'Prints help information' complete -c my_app -n "__fish_using_command my_app help" -s V -l version -d 'Prints version information' "#; @@ -506,6 +525,9 @@ static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() { help) cmd+="__help" ;; + some__cmd__with__hypens) + cmd+="__some__cmd__with__hypens" + ;; some_cmd) cmd+="__some_cmd" ;; @@ -519,7 +541,7 @@ static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() { case "${cmd}" in my_app) - opts=" -h -V --help --version test some_cmd help" + opts=" -h -V --help --version test some_cmd some-cmd-with-hypens help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 @@ -549,6 +571,21 @@ static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() { COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + my_app__some__cmd__with__hypens) + opts=" -h -V --help --version " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + case "${prev}" in + + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; my_app__some_cmd) opts=" -h -V --help --version --config " if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then @@ -686,12 +723,14 @@ fn build_app_with_name(s: &'static str) -> App<'static, 'static> { } fn build_app_special_commands() -> App<'static, 'static> { - build_app_with_name("my_app").subcommand(SubCommand::with_name("some_cmd") - .about("tests other things") - .arg(Arg::with_name("config") - .long("--config") - .takes_value(true) - .help("the other case to test"))) + build_app_with_name("my_app") + .subcommand(SubCommand::with_name("some_cmd") + .about("tests other things") + .arg(Arg::with_name("config") + .long("--config") + .takes_value(true) + .help("the other case to test"))) + .subcommand(SubCommand::with_name("some-cmd-with-hypens")) } fn build_app_special_help() -> App<'static, 'static> { From ba7f1d18eba7a07ce7f57e0981986f66c994b639 Mon Sep 17 00:00:00 2001 From: Alex Helfet Date: Sat, 16 Dec 2017 10:50:19 +0000 Subject: [PATCH 03/15] fix(bash completion): Change the bash completion script code generation to support hyphens. --- src/completions/bash.rs | 5 +++-- tests/completions.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/completions/bash.rs b/src/completions/bash.rs index 983b250b..f563922d 100644 --- a/src/completions/bash.rs +++ b/src/completions/bash.rs @@ -81,10 +81,11 @@ complete -F _{name} -o bashdefault -o default {name} subcmds = format!( "{} {name}) - cmd+=\"__{name}\" + cmd+=\"__{fn_name}\" ;;", subcmds, - name = sc.replace("-", "__") + name = sc, + fn_name = sc.replace("-", "__") ); } diff --git a/tests/completions.rs b/tests/completions.rs index 456944d3..31e0532f 100644 --- a/tests/completions.rs +++ b/tests/completions.rs @@ -525,7 +525,7 @@ static BASH_SPECIAL_CMDS: &'static str = r#"_my_app() { help) cmd+="__help" ;; - some__cmd__with__hypens) + some-cmd-with-hypens) cmd+="__some__cmd__with__hypens" ;; some_cmd) From 56e734b839303d733d2e5baf7dac39bd7b97b8e4 Mon Sep 17 00:00:00 2001 From: Cameron Dershem Date: Tue, 26 Dec 2017 22:56:50 -0500 Subject: [PATCH 04/15] docs: fixes broken links. Small correction to add a link to the source in the documentation, previously it was a placeholder. `rustdoc` does not appear to package assets with the docs, therefore relative links looking for static files do not work. The links are consistent enough on github that the static files can be directly linked to on the master branch. --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a55ed80e..de23769c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ //! //! The following examples show a quick example of some of the very basic functionality of `clap`. //! For more advanced usage, such as requirements, conflicts, groups, multiple values and -//! occurrences see the [documentation](https://docs.rs/clap/), [examples/](examples) directory of +//! occurrences see the [documentation](https://docs.rs/clap/), [examples/](https://github.com/kbknapp/clap-rs/tree/master/examples) directory of //! this repository or the [video tutorials](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U). //! //! **NOTE:** All of these examples are functionally the same, but show different styles in which to @@ -366,13 +366,13 @@ //! * **Red** Color: **NOT** included by default (must use cargo `features` to enable) //! * **Blue** Color: Dev dependency, only used while developing. //! -//! ![clap dependencies](clap_dep_graph.png) +//! ![clap dependencies](https://raw.githubusercontent.com/kbknapp/clap-rs/master/clap_dep_graph.png) //! //! ### More Information //! //! You can find complete documentation on the [docs.rs](https://docs.rs/clap/) for this project. //! -//! You can also find usage examples in the [examples/](examples) directory of this repo. +//! You can also find usage examples in the [examples/](https://github.com/kbknapp/clap-rs/tree/master/examples) directory of this repo. //! //! #### Video Tutorials //! @@ -391,7 +391,7 @@ //! `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell //! me. I'm all about giving credit where credit is due :) //! -//! Please read [CONTRIBUTING.md](.github/CONTRIBUTING.md) before you start contributing. +//! Please read [CONTRIBUTING.md](https://raw.githubusercontent.com/kbknapp/clap-rs/master/.github/CONTRIBUTING.md) before you start contributing. //! //! //! ### Testing Code From 46365cf8be5331ba04c895eb183e2f230b5aad51 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Sat, 6 Jan 2018 12:57:31 +0200 Subject: [PATCH 05/15] fix(completions/zsh.rs): Fix completion of long option values Long options take their argument either in the next word or after an equals sign, but the Zsh completion specified that they take it either in the same word with no separator or in the next word. See the documentation of the Zsh _arguments function for more information. --- src/completions/zsh.rs | 2 +- tests/completions.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/completions/zsh.rs b/src/completions/zsh.rs index 8fbc1ff6..c73a9878 100644 --- a/src/completions/zsh.rs +++ b/src/completions/zsh.rs @@ -371,7 +371,7 @@ fn write_opts_of(p: &Parser) -> String { } if let Some(long) = o.long() { let l = format!( - "'{conflicts}{multiple}--{arg}+[{help}]{possible_values}' \\", + "'{conflicts}{multiple}--{arg}=[{help}]{possible_values}' \\", conflicts = conflicts, multiple = multiple, arg = long, diff --git a/tests/completions.rs b/tests/completions.rs index 1afc6bcb..953b68c2 100644 --- a/tests/completions.rs +++ b/tests/completions.rs @@ -108,7 +108,7 @@ _myapp() { case $line[1] in (test) _arguments -s -S -C \ -'--case+[the case to test]' \ +'--case=[the case to test]' \ '-h[Prints help information]' \ '--help[Prints help information]' \ '-V[Prints version information]' \ @@ -397,7 +397,7 @@ _my_app() { case $line[1] in (test) _arguments -s -S -C \ -'--case+[the case to test]' \ +'--case=[the case to test]' \ '-h[Prints help information]' \ '--help[Prints help information]' \ '-V[Prints version information]' \ @@ -406,7 +406,7 @@ _arguments -s -S -C \ ;; (some_cmd) _arguments -s -S -C \ -'--config+[the other case to test]' \ +'--config=[the other case to test]' \ '-h[Prints help information]' \ '--help[Prints help information]' \ '-V[Prints version information]' \ From 6f25b2830a5e5d6b42760c8113d60f973fb2a138 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 10:16:23 -0500 Subject: [PATCH 06/15] chore: adds .idea project folder to ignore list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 11101efc..34253e90 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ Cargo.lock # Project files .vscode/* +.idea/* From 79c36396dbdaa0a800063d15cc44326207948d49 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 10:16:56 -0500 Subject: [PATCH 07/15] tests: adds a check_output2 function to compare output to one of two possible solutions --- clap-test.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clap-test.rs b/clap-test.rs index 2f91b9e8..3e01edee 100644 --- a/clap-test.rs +++ b/clap-test.rs @@ -39,6 +39,16 @@ mod test { assert_eq!(stderr, err.use_stderr()); compare(left, right) } + pub fn compare_output2(l: App, args: &str, right1: &str, right2: &str, stderr: bool) -> bool { + let mut buf = Cursor::new(Vec::with_capacity(50)); + let res = l.get_matches_from_safe(args.split(' ').collect::>()); + let err = res.unwrap_err(); + err.write_to(&mut buf).unwrap(); + let content = buf.into_inner(); + let left = String::from_utf8(content).unwrap(); + assert_eq!(stderr, err.use_stderr()); + compare(&*left, right1) || compare(&*left, right2) + } // Legacy tests from the pyhton script days From d26ab2b97cf9c0ea675b440b7b0eaf6ac3ad01f4 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 10:17:18 -0500 Subject: [PATCH 08/15] fix: fixes the ripgrep benchmark by adding a value to a flag that expects it --- benches/05_ripgrep.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/05_ripgrep.rs b/benches/05_ripgrep.rs index 48c0819b..7db15525 100644 --- a/benches/05_ripgrep.rs +++ b/benches/05_ripgrep.rs @@ -43,7 +43,7 @@ fn parse_complex(b: &mut Bencher) { app_short().get_matches_from(vec!["rg", "pat", "-cFlN", - "-pqr", + "-pqr=some", "--null", "--no-filename", "--no-messages", From 03e413d7175d35827cd7d8908d47dbae15a849a3 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 10:18:36 -0500 Subject: [PATCH 09/15] perf: debloats clap by deduplicating logic and refactors This commit removes heavy use of macros in certain functions which drastically increased code size. Some of the macros could be turned into functions, while others could be removed entirely. Examples were removing arg_post_processing! which did things like checked overrides, requirements, groups, etc. This would happen after every argument was parsed. This macro also had several other macros inside it, and would expand to several tens or hundreds of lines of code. Then add that due to borrowck and branch issues, this macro may be included in multiple parts of a function. Unlike traditional functions each of these uses expanded into TONS of code (just like agressive inlining). This commit primarily removes those arg_post_processing! calls and breaks up the functionality into two types. The first must happen at ever new argument (not new value, but new argument). This is pretty cheap. The next type was moved to the end of parsing validation section which is more expensive, but only happens once. i.e. clap was validating each argument/value as it saw them, now it's lazy and validates them all at once at the end. This MUCH more efficient! --- src/app/macros.rs | 165 -------------------------------- src/app/parser.rs | 202 +++++++++++++++++++++++----------------- src/app/validator.rs | 164 ++++++++++++++++++-------------- src/args/any_arg.rs | 31 ++++++ src/args/arg_matcher.rs | 33 ++++++- src/macros.rs | 89 ++++++++++-------- 6 files changed, 325 insertions(+), 359 deletions(-) delete mode 100644 src/app/macros.rs diff --git a/src/app/macros.rs b/src/app/macros.rs deleted file mode 100644 index e3f15e96..00000000 --- a/src/app/macros.rs +++ /dev/null @@ -1,165 +0,0 @@ -macro_rules! remove_overriden { - (@remove_requires $rem_from:expr, $a:ident.$ov:ident) => { - if let Some(ora) = $a.$ov() { - for i in (0 .. $rem_from.len()).rev() { - let should_remove = ora.iter().any(|&(_, ref name)| name == &$rem_from[i]); - if should_remove { $rem_from.swap_remove(i); } - } - } - }; - (@remove $rem_from:expr, $a:ident.$ov:ident) => { - if let Some(ora) = $a.$ov() { - vec_remove_all!($rem_from, ora.iter()); - } - }; - (@arg $_self:ident, $arg:ident) => { - remove_overriden!(@remove_requires $_self.required, $arg.requires); - remove_overriden!(@remove $_self.blacklist, $arg.blacklist); - remove_overriden!(@remove $_self.overrides, $arg.overrides); - }; - ($_self:ident, $name:expr) => { - debugln!("remove_overriden!;"); - if let Some(o) = $_self.opts.iter() .find(|o| o.b.name == *$name) { - remove_overriden!(@arg $_self, o); - } else if let Some(f) = $_self.flags.iter() .find(|f| f.b.name == *$name) { - remove_overriden!(@arg $_self, f); - } else { - let p = $_self.positionals.values() - .find(|p| p.b.name == *$name) - .expect(INTERNAL_ERROR_MSG); - remove_overriden!(@arg $_self, p); - } - }; -} - -macro_rules! arg_post_processing { - ($me:ident, $arg:ident, $matcher:ident) => { - debugln!("arg_post_processing!;"); - // Handle POSIX overrides - debug!("arg_post_processing!: Is '{}' in overrides...", $arg.to_string()); - if $me.overrides.contains(&$arg.name()) { - if let Some(ref name) = find_name_from!($me, &$arg.name(), overrides, $matcher) { - sdebugln!("Yes by {}", name); - $matcher.remove(name); - remove_overriden!($me, name); - } - } else { sdebugln!("No"); } - - // Add overrides - debug!("arg_post_processing!: Does '{}' have overrides...", $arg.to_string()); - if let Some(or) = $arg.overrides() { - sdebugln!("Yes"); - $matcher.remove_all(or); - for pa in or { remove_overriden!($me, pa); } - $me.overrides.extend(or); - vec_remove_all!($me.required, or.iter()); - } else { sdebugln!("No"); } - - // Handle conflicts - debug!("arg_post_processing!: Does '{}' have conflicts...", $arg.to_string()); - if let Some(bl) = $arg.blacklist() { - sdebugln!("Yes"); - - for c in bl { - // Inject two-way conflicts - debug!("arg_post_processing!: Has '{}' already been matched...", c); - if $matcher.contains(c) { - sdebugln!("Yes"); - // find who blacklisted us... - $me.blacklist.push(&$arg.b.name); - } else { - sdebugln!("No"); - } - } - - $me.blacklist.extend_from_slice(bl); - vec_remove_all!($me.overrides, bl.iter()); - // vec_remove_all!($me.required, bl.iter()); - } else { sdebugln!("No"); } - - // Add all required args which aren't already found in matcher to the master - // list - debug!("arg_post_processing!: Does '{}' have requirements...", $arg.to_string()); - if let Some(reqs) = $arg.requires() { - for n in reqs.iter() - .filter(|&&(val, _)| val.is_none()) - .filter(|&&(_, req)| !$matcher.contains(&req)) - .map(|&(_, name)| name) { - - $me.required.push(n); - } - } else { sdebugln!("No"); } - - _handle_group_reqs!($me, $arg); - }; -} - -macro_rules! _handle_group_reqs{ - ($me:ident, $arg:ident) => ({ - use args::AnyArg; - debugln!("_handle_group_reqs!;"); - for grp in &$me.groups { - let found = if grp.args.contains(&$arg.name()) { - if let Some(ref reqs) = grp.requires { - debugln!("_handle_group_reqs!: Adding {:?} to the required list", reqs); - $me.required.extend(reqs); - } - if let Some(ref bl) = grp.conflicts { - $me.blacklist.extend(bl); - } - true // What if arg is in more than one group with different reqs? - } else { - false - }; - debugln!("_handle_group_reqs!:iter: grp={}, found={:?}", grp.name, found); - if found { - for i in (0 .. $me.required.len()).rev() { - let should_remove = grp.args.contains(&$me.required[i]); - if should_remove { $me.required.swap_remove(i); } - } - debugln!("_handle_group_reqs!:iter: Adding args from group to blacklist...{:?}", grp.args); - if !grp.multiple { - $me.blacklist.extend(&grp.args); - debugln!("_handle_group_reqs!: removing {:?} from blacklist", $arg.name()); - for i in (0 .. $me.blacklist.len()).rev() { - let should_remove = $me.blacklist[i] == $arg.name(); - if should_remove { $me.blacklist.swap_remove(i); } - } - } - } - } - }) -} - -macro_rules! parse_positional { - ( - $_self:ident, - $p:ident, - $arg_os:ident, - $pos_counter:ident, - $matcher:ident - ) => { - debugln!("parse_positional!;"); - - if !$_self.is_set(AS::TrailingValues) && - ($_self.is_set(AS::TrailingVarArg) && - $pos_counter == $_self.positionals.len()) { - $_self.settings.set(AS::TrailingValues); - } - let _ = $_self.add_val_to_arg($p, &$arg_os, $matcher)?; - - $matcher.inc_occurrence_of($p.b.name); - let _ = $_self.groups_for_arg($p.b.name) - .and_then(|vec| Some($matcher.inc_occurrences_of(&*vec))); - if $_self.cache.map_or(true, |name| name != $p.b.name) { - arg_post_processing!($_self, $p, $matcher); - $_self.cache = Some($p.b.name); - } - - $_self.settings.set(AS::ValidArgFound); - // Only increment the positional counter if it doesn't allow multiples - if !$p.b.settings.is_set(ArgSettings::Multiple) { - $pos_counter += 1; - } - }; -} diff --git a/src/app/parser.rs b/src/app/parser.rs index 22c45c9a..01e0e1f3 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -61,8 +61,7 @@ where pub global_args: Vec>, pub required: Vec<&'a str>, pub r_ifs: Vec<(&'a str, &'b str, &'a str)>, - pub blacklist: Vec<&'b str>, - pub overrides: Vec<&'b str>, + pub overrides: Vec<(&'b str, &'a str)>, help_short: Option, version_short: Option, cache: Option<&'a str>, @@ -346,9 +345,9 @@ where if let Some(ref reqs) = group.requires { self.required.extend_from_slice(reqs); } - if let Some(ref bl) = group.conflicts { - self.blacklist.extend_from_slice(bl); - } +// if let Some(ref bl) = group.conflicts { +// self.blacklist.extend_from_slice(bl); +// } } if self.groups.iter().any(|g| g.name == group.name) { let grp = self.groups @@ -773,12 +772,8 @@ where // allow wrong self convention due to self.valid_neg_num = true and it's a private method #[cfg_attr(feature = "lints", allow(wrong_self_convention))] - fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult<'a>) -> bool { - debugln!( - "Parser::is_new_arg: arg={:?}, Needs Val of={:?}", - arg_os, - needs_val_of - ); + fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult) -> bool { + debugln!( "Parser::is_new_arg:{:?}:{:?}", arg_os, needs_val_of); let app_wide_settings = if self.is_set(AS::AllowLeadingHyphen) { true } else if self.is_set(AS::AllowNegativeNumbers) { @@ -807,12 +802,10 @@ where .expect(INTERNAL_ERROR_MSG); (p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) } + ParseResult::ValuesDone => return true, _ => false, }; - debugln!( - "Parser::is_new_arg: Arg::allow_leading_hyphen({:?})", - arg_allows_tac - ); + debugln!( "Parser::is_new_arg: arg_allows_tac={:?}", arg_allows_tac ); // Is this a new argument, or values from a previous option? let mut ret = if arg_os.starts_with(b"--") { @@ -913,7 +906,52 @@ where } } - if !starts_new_arg { + if starts_new_arg { + { + let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); + matcher.process_arg_overrides(any_arg, &mut self.overrides, &mut self.required); + } + + if arg_os.starts_with(b"--") { + needs_val_of = self.parse_long_arg(matcher, &arg_os)?; + debugln!( "Parser:get_matches_with: After parse_long_arg {:?}", needs_val_of ); + match needs_val_of { + ParseResult::Flag | ParseResult::Opt(..) | ParseResult::ValuesDone => { + continue + } + _ => (), + } + } else if arg_os.starts_with(b"-") && arg_os.len_() != 1 { + // Try to parse short args like normal, if AllowLeadingHyphen or + // AllowNegativeNumbers is set, parse_short_arg will *not* throw + // an error, and instead return Ok(None) + needs_val_of = self.parse_short_arg(matcher, &arg_os)?; + // If it's None, we then check if one of those two AppSettings was set + debugln!( + "Parser:get_matches_with: After parse_short_arg {:?}", + needs_val_of + ); + match needs_val_of { + ParseResult::MaybeNegNum => { + if !(arg_os.to_string_lossy().parse::().is_ok() + || arg_os.to_string_lossy().parse::().is_ok()) + { + return Err(Error::unknown_argument( + &*arg_os.to_string_lossy(), + "", + &*usage::create_error_usage(self, matcher, None), + self.color(), + )); + } + }, + ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => { + continue + } + _ => (), + } + } + + } else { if let ParseResult::Opt(name) = needs_val_of { // Check to see if parsing a value from a previous arg let arg = self.opts @@ -925,62 +963,20 @@ where // get the next value from the iterator continue; } - } else if arg_os.starts_with(b"--") { - needs_val_of = self.parse_long_arg(matcher, &arg_os)?; - debugln!( - "Parser:get_matches_with: After parse_long_arg {:?}", - needs_val_of - ); - match needs_val_of { - ParseResult::Flag | ParseResult::Opt(..) | ParseResult::ValuesDone => { - continue - } - _ => (), - } - } else if arg_os.starts_with(b"-") && arg_os.len_() != 1 { - // Try to parse short args like normal, if AllowLeadingHyphen or - // AllowNegativeNumbers is set, parse_short_arg will *not* throw - // an error, and instead return Ok(None) - needs_val_of = self.parse_short_arg(matcher, &arg_os)?; - // If it's None, we then check if one of those two AppSettings was set - debugln!( - "Parser:get_matches_with: After parse_short_arg {:?}", - needs_val_of - ); - match needs_val_of { - ParseResult::MaybeNegNum => { - if !(arg_os.to_string_lossy().parse::().is_ok() - || arg_os.to_string_lossy().parse::().is_ok()) - { - return Err(Error::unknown_argument( - &*arg_os.to_string_lossy(), - "", - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } - } - ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => { - continue - } - _ => (), - } } + } - if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) - && !self.is_set(AS::InferSubcommands) - { - if let Some(cdate) = - suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self)) - { - return Err(Error::invalid_subcommand( - arg_os.to_string_lossy().into_owned(), - cdate, - self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } + if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) + && !self.is_set(AS::InferSubcommands) + { + if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self)) { + return Err(Error::invalid_subcommand( + arg_os.to_string_lossy().into_owned(), + cdate, + self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), + &*usage::create_error_usage(self, matcher, None), + self.color(), + )); } } @@ -1035,7 +1031,29 @@ where self.color(), )); } - parse_positional!(self, p, arg_os, pos_counter, matcher); + if !self.is_set(AS::TrailingValues) && + (self.is_set(AS::TrailingVarArg) && + pos_counter == self.positionals.len()) { + self.settings.set(AS::TrailingValues); + } + if self.cache.map_or(true, |name| name != p.b.name) { + { + let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); + matcher.process_arg_overrides(any_arg, &mut self.overrides, &mut self.required); + } + self.cache = Some(p.b.name); + } + let _ = self.add_val_to_arg(p, &arg_os, matcher)?; + + matcher.inc_occurrence_of(p.b.name); + let _ = self.groups_for_arg(p.b.name) + .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); + + self.settings.set(AS::ValidArgFound); + // Only increment the positional counter if it doesn't allow multiples + if !p.b.settings.is_set(ArgSettings::Multiple) { + pos_counter += 1; + } self.settings.set(AS::ValidArgFound); } else if self.is_set(AS::AllowExternalSubcommands) { // Get external subcommand name @@ -1136,9 +1154,34 @@ where }); } + // In case the last arg was new, we need to process it's overrides + { + let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); + matcher.process_arg_overrides(any_arg, &mut self.overrides, &mut self.required); + } + + self.remove_overrides(matcher); + Validator::new(self).validate(needs_val_of, subcmd_name, matcher) } + fn remove_overrides(&mut self, matcher: &mut ArgMatcher) { + debugln!("Parser::remove_overrides:{:?};", self.overrides); + for &(overr, name) in &self.overrides { + debugln!("Parser::remove_overrides:iter:({},{});", overr, name); + if matcher.is_present(overr) { + debugln!("Parser::remove_overrides:iter:({},{}): removing {};", overr, name, name); + matcher.remove(name); + for i in (0 .. self.required.len()).rev() { + debugln!("Parser::remove_overrides:iter:({},{}): removing required {};", overr, name, name); + if self.required[i] == name { + self.required.swap_remove(i); + break; + } + } + } + } + } fn propagate_help_version(&mut self) { debugln!("Parser::propagate_help_version;"); @@ -1483,7 +1526,6 @@ where self.settings.set(AS::ValidArgFound); let ret = self.parse_opt(val, opt, val.is_some(), matcher)?; if self.cache.map_or(true, |name| name != opt.b.name) { - arg_post_processing!(self, opt, matcher); self.cache = Some(opt.b.name); } @@ -1501,10 +1543,9 @@ where self.parse_flag(flag, matcher)?; // Handle conflicts, requirements, etc. - // if self.cache.map_or(true, |name| name != flag.b.name) { - arg_post_processing!(self, flag, matcher); - // self.cache = Some(flag.b.name); - // } + if self.cache.map_or(true, |name| name != flag.b.name) { + self.cache = Some(flag.b.name); + } return Ok(ParseResult::Flag); } else if self.is_set(AS::AllowLeadingHyphen) { @@ -1580,7 +1621,6 @@ where let ret = self.parse_opt(val, opt, false, matcher)?; if self.cache.map_or(true, |name| name != opt.b.name) { - arg_post_processing!(self, opt, matcher); self.cache = Some(opt.b.name); } @@ -1595,7 +1635,6 @@ where // Handle conflicts, requirements, overrides, etc. // Must be called here due to mutablilty if self.cache.map_or(true, |name| name != flag.b.name) { - arg_post_processing!(self, flag, matcher); self.cache = Some(flag.b.name); } } else { @@ -1844,7 +1883,6 @@ where $_self.add_val_to_arg($a, OsStr::new(val), $m)?; if $_self.cache.map_or(true, |name| name != $a.name()) { - arg_post_processing!($_self, $a, $m); $_self.cache = Some($a.name()); } } else if $m.get($a.b.name).is_some() { @@ -1855,7 +1893,6 @@ where $_self.add_val_to_arg($a, OsStr::new(val), $m)?; if $_self.cache.map_or(true, |name| name != $a.name()) { - arg_post_processing!($_self, $a, $m); $_self.cache = Some($a.name()); } } @@ -1881,7 +1918,6 @@ where if add { $_self.add_val_to_arg($a, OsStr::new(default), $m)?; if $_self.cache.map_or(true, |name| name != $a.name()) { - arg_post_processing!($_self, $a, $m); $_self.cache = Some($a.name()); } done = true; @@ -1920,7 +1956,6 @@ where $_self.add_val_to_arg($a, OsStr::new(val), $m)?; if $_self.cache.map_or(true, |name| name != $a.name()) { - arg_post_processing!($_self, $a, $m); $_self.cache = Some($a.name()); } } @@ -1929,7 +1964,6 @@ where $_self.add_val_to_arg($a, OsStr::new(val), $m)?; if $_self.cache.map_or(true, |name| name != $a.name()) { - arg_post_processing!($_self, $a, $m); $_self.cache = Some($a.name()); } } @@ -1972,7 +2006,7 @@ where } } - pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg> { + pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg<'a, 'b>> { if let Some(f) = find_by_name!(self, name, flags, iter) { return Some(f); } diff --git a/src/app/validator.rs b/src/app/validator.rs index 01373dd5..9751321a 100644 --- a/src/app/validator.rs +++ b/src/app/validator.rs @@ -78,7 +78,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { Ok(()) } - fn validate_values( + fn validate_arg_values( &self, arg: &A, ma: &MatchedArg, @@ -87,11 +87,11 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { where A: AnyArg<'a, 'b> + Display, { - debugln!("Validator::validate_values: arg={:?}", arg.name()); + debugln!("Validator::validate_arg_values: arg={:?}", arg.name()); for val in &ma.vals { if self.0.is_set(AS::StrictUtf8) && val.to_str().is_none() { debugln!( - "Validator::validate_values: invalid UTF-8 found in val {:?}", + "Validator::validate_arg_values: invalid UTF-8 found in val {:?}", val ); return Err(Error::invalid_utf8( @@ -100,7 +100,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { )); } if let Some(p_vals) = arg.possible_vals() { - debugln!("Validator::validate_values: possible_vals={:?}", p_vals); + debugln!("Validator::validate_arg_values: possible_vals={:?}", p_vals); let val_str = val.to_string_lossy(); let ok = if arg.is_set(ArgSettings::CaseInsensitive) { p_vals.iter().any(|pv| pv.eq_ignore_ascii_case(&*val_str)) @@ -120,7 +120,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() && matcher.contains(&*arg.name()) { - debugln!("Validator::validate_values: illegal empty val found"); + debugln!("Validator::validate_arg_values: illegal empty val found"); return Err(Error::empty_value( arg, &*usage::create_error_usage(self.0, matcher, None), @@ -128,7 +128,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { )); } if let Some(vtor) = arg.validator() { - debug!("Validator::validate_values: checking validator..."); + debug!("Validator::validate_arg_values: checking validator..."); if let Err(e) = vtor(val.to_string_lossy().into_owned()) { sdebugln!("error"); return Err(Error::value_validation(Some(arg), e, self.0.color())); @@ -137,7 +137,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { } } if let Some(vtor) = arg.validator_os() { - debug!("Validator::validate_values: checking validator_os..."); + debug!("Validator::validate_arg_values: checking validator_os..."); if let Err(e) = vtor(val) { sdebugln!("error"); return Err(Error::value_validation( @@ -153,44 +153,83 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { Ok(()) } - fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { - debugln!( - "Validator::validate_blacklist: blacklist={:?}", - self.0.blacklist + fn build_err(&self, name: &str, matcher: &ArgMatcher) -> ClapResult<()> { + debugln!("build_err!: name={}", name); + let mut c_with = find_from!(self.0, &name, blacklist, &matcher); + c_with = c_with.or( + self.0.find_any_arg(&name).map_or(None, |aa| aa.blacklist()) + .map_or(None, + |bl| bl.iter().find(|arg| matcher.contains(arg))) + .map_or(None, |an| self.0.find_any_arg(an)) + .map_or(None, |aa| Some(format!("{}", aa))) ); - macro_rules! build_err { - ($p:expr, $name:expr, $matcher:ident) => ({ - debugln!("build_err!: name={}", $name); - let mut c_with = find_from!($p, &$name, blacklist, &$matcher); - c_with = c_with.or( - $p.find_any_arg(&$name).map_or(None, |aa| aa.blacklist()) - .map_or(None, - |bl| bl.iter().find(|arg| $matcher.contains(arg))) - .map_or(None, |an| $p.find_any_arg(an)) - .map_or(None, |aa| Some(format!("{}", aa))) - ); - debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, &$name); - $matcher.remove(&$name); - let usg = usage::create_error_usage($p, $matcher, None); - if let Some(f) = find_by_name!($p, $name, flags, iter) { - debugln!("build_err!: It was a flag..."); - Error::argument_conflict(f, c_with, &*usg, self.0.color()) - } else if let Some(o) = find_by_name!($p, $name, opts, iter) { - debugln!("build_err!: It was an option..."); - Error::argument_conflict(o, c_with, &*usg, self.0.color()) - } else { - match find_by_name!($p, $name, positionals, values) { - Some(p) => { - debugln!("build_err!: It was a positional..."); - Error::argument_conflict(p, c_with, &*usg, self.0.color()) - }, - None => panic!(INTERNAL_ERROR_MSG) + debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, &name); +// matcher.remove(&name); + let usg = usage::create_error_usage(self.0, matcher, None); + if let Some(f) = find_by_name!(self.0, name, flags, iter) { + debugln!("build_err!: It was a flag..."); + Err(Error::argument_conflict(f, c_with, &*usg, self.0.color())) + } else if let Some(o) = find_by_name!(self.0, name, opts, iter) { + debugln!("build_err!: It was an option..."); + Err(Error::argument_conflict(o, c_with, &*usg, self.0.color())) + } else { + match find_by_name!(self.0, name, positionals, values) { + Some(p) => { + debugln!("build_err!: It was a positional..."); + Err(Error::argument_conflict(p, c_with, &*usg, self.0.color())) + }, + None => panic!(INTERNAL_ERROR_MSG) + } + } + } + + fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { + debugln!("Validator::validate_blacklist;"); + let mut conflicts: Vec<&str> = vec![]; + for (&name, _) in matcher.iter() { + debugln!("Validator::validate_blacklist:iter:{};", name); + if let Some(grps) = self.0.groups_for_arg(name) { + for grp in &grps { + if let Some(g) = self.0.groups.iter().find(|g| &g.name == grp) { + if !g.multiple { + for arg in &g.args { + if arg == &name { + continue; + } + conflicts.push(arg); + } + } + if let Some(ref gc) = g.conflicts { + conflicts.extend(&*gc); + } } } - }); + } + if let Some(arg) = find_any_by_name!(self.0, name) { + if let Some(bl) = arg.blacklist() { + for conf in bl { + if matcher.get(conf).is_some() { + conflicts.push(conf); + } + } + } + } else { + debugln!("Validator::validate_blacklist:iter:{}:group;", name); + let args = self.0.arg_names_in_group(name); + for arg in &args { + debugln!("Validator::validate_blacklist:iter:{}:group:iter:{};", name, arg); + if let Some(bl) = find_any_by_name!(self.0, *arg).unwrap().blacklist() { + for conf in bl { + if matcher.get(conf).is_some() { + conflicts.push(conf); + } + } + } + } + } } - for name in &self.0.blacklist { + for name in &conflicts { debugln!( "Validator::validate_blacklist:iter:{}: Checking blacklisted arg", name @@ -213,7 +252,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { name, n ); - return Err(build_err!(self.0, n, matcher)); + return self.build_err(n, matcher); } } } else if let Some(ma) = matcher.get(name) { @@ -224,7 +263,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { should_err = ma.occurs > 0; } if should_err { - return Err(build_err!(self.0, *name, matcher)); + return self.build_err(*name, matcher); } } Ok(()) @@ -240,7 +279,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { ); if let Some(opt) = find_by_name!(self.0, *name, opts, iter) { self.validate_arg_num_vals(opt, ma, matcher)?; - self.validate_values(opt, ma, matcher)?; + self.validate_arg_values(opt, ma, matcher)?; self.validate_arg_requires(opt, ma, matcher)?; self.validate_arg_num_occurs(opt, ma, matcher)?; } else if let Some(flag) = find_by_name!(self.0, *name, flags, iter) { @@ -249,7 +288,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { } else if let Some(pos) = find_by_name!(self.0, *name, positionals, values) { self.validate_arg_num_vals(pos, ma, matcher)?; self.validate_arg_num_occurs(pos, ma, matcher)?; - self.validate_values(pos, ma, matcher)?; + self.validate_arg_values(pos, ma, matcher)?; self.validate_arg_requires(pos, ma, matcher)?; } else { let grp = self.0 @@ -381,7 +420,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { where A: AnyArg<'a, 'b> + Display, { - debugln!("Validator::validate_arg_requires;"); + debugln!("Validator::validate_arg_requires:{};", a.name()); if let Some(a_reqs) = a.requires() { for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) { let missing_req = @@ -390,6 +429,11 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { return self.missing_required_error(matcher, None); } } + for &(_, name) in a_reqs.iter().filter(|&&(val, _)| val.is_none()) { + if !matcher.contains(name) { + return self.missing_required_error(matcher, Some(name)); + } + } } Ok(()) } @@ -399,20 +443,13 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { "Validator::validate_required: required={:?};", self.0.required ); + 'outer: for name in &self.0.required { debugln!("Validator::validate_required:iter:{}:", name); if matcher.contains(name) { continue 'outer; } - if let Some(a) = find_by_name!(self.0, *name, flags, iter) { - if self.is_missing_required_ok(a, matcher) { - continue 'outer; - } - } else if let Some(a) = find_by_name!(self.0, *name, opts, iter) { - if self.is_missing_required_ok(a, matcher) { - continue 'outer; - } - } else if let Some(a) = find_by_name!(self.0, *name, positionals, values) { + if let Some(a) = find_any_by_name!(self.0, *name) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } @@ -431,11 +468,8 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { Ok(()) } - fn validate_conflicts(&self, a: &A, matcher: &ArgMatcher) -> Option - where - A: AnyArg<'a, 'b>, - { - debugln!("Validator::validate_conflicts: a={:?};", a.name()); + fn validate_arg_conflicts(&self, a: &AnyArg, matcher: &ArgMatcher) -> Option { + debugln!("Validator::validate_arg_conflicts: a={:?};", a.name()); a.blacklist().map(|bl| { bl.iter().any(|conf| { matcher.contains(conf) @@ -448,10 +482,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { }) } - fn validate_required_unless(&self, a: &A, matcher: &ArgMatcher) -> Option - where - A: AnyArg<'a, 'b>, - { + fn validate_required_unless(&self, a: &AnyArg, matcher: &ArgMatcher) -> Option { debugln!("Validator::validate_required_unless: a={:?};", a.name()); macro_rules! check { ($how:ident, $_self:expr, $a:ident, $m:ident) => {{ @@ -506,12 +537,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { } #[inline] - fn is_missing_required_ok(&self, a: &A, matcher: &ArgMatcher) -> bool - where - A: AnyArg<'a, 'b>, - { + fn is_missing_required_ok(&self, a: &AnyArg, matcher: &ArgMatcher) -> bool { debugln!("Validator::is_missing_required_ok: a={}", a.name()); - self.validate_conflicts(a, matcher).unwrap_or(false) + self.validate_arg_conflicts(a, matcher).unwrap_or(false) || self.validate_required_unless(a, matcher).unwrap_or(false) } } diff --git a/src/args/any_arg.rs b/src/args/any_arg.rs index 9fa34c7f..eee52283 100644 --- a/src/args/any_arg.rs +++ b/src/args/any_arg.rs @@ -6,6 +6,7 @@ use std::ffi::{OsStr, OsString}; // Internal use args::settings::ArgSettings; use map::{self, VecMap}; +use INTERNAL_ERROR_MSG; #[doc(hidden)] pub trait AnyArg<'n, 'e>: std_fmt::Display { @@ -41,3 +42,33 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display { pub trait DispOrder { fn disp_ord(&self) -> usize; } + +impl<'n, 'e, 'z, T: ?Sized> AnyArg<'n, 'e> for &'z T where T: AnyArg<'n, 'e> + 'z { + fn name(&self) -> &'n str { (*self).name() } + fn overrides(&self) -> Option<&[&'e str]> { (*self).overrides() } + fn aliases(&self) -> Option> { (*self).aliases() } + fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> { (*self).requires() } + fn blacklist(&self) -> Option<&[&'e str]> { (*self).blacklist() } + fn required_unless(&self) -> Option<&[&'e str]> { (*self).required_unless() } + fn is_set(&self, a: ArgSettings) -> bool { (*self).is_set(a) } + fn set(&mut self, _: ArgSettings) { panic!(INTERNAL_ERROR_MSG) } + fn has_switch(&self) -> bool { (*self).has_switch() } + fn max_vals(&self) -> Option { (*self).max_vals() } + fn min_vals(&self) -> Option { (*self).min_vals() } + fn num_vals(&self) -> Option { (*self).num_vals() } + fn possible_vals(&self) -> Option<&[&'e str]> { (*self).possible_vals() } + fn validator(&self) -> Option<&Rc Result<(), String>>> { (*self).validator() } + fn validator_os(&self) -> Option<&Rc Result<(), OsString>>> { (*self).validator_os() } + fn short(&self) -> Option { (*self).short() } + fn long(&self) -> Option<&'e str> { (*self).long() } + fn val_delim(&self) -> Option { (*self).val_delim() } + fn takes_value(&self) -> bool { (*self).takes_value() } + fn val_names(&self) -> Option<&VecMap<&'e str>> { (*self).val_names() } + fn help(&self) -> Option<&'e str> { (*self).help() } + fn long_help(&self) -> Option<&'e str> { (*self).long_help() } + fn default_val(&self) -> Option<&'e OsStr> { (*self).default_val() } + fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { (*self).default_vals_ifs() } + fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> { (*self).env() } + fn longest_filter(&self) -> bool { (*self).longest_filter() } + fn val_terminator(&self) -> Option<&'e str> { (*self).val_terminator() } +} diff --git a/src/args/arg_matcher.rs b/src/args/arg_matcher.rs index 4e76802e..25f3fc5d 100644 --- a/src/args/arg_matcher.rs +++ b/src/args/arg_matcher.rs @@ -21,11 +21,36 @@ impl<'a> Default for ArgMatcher<'a> { impl<'a> ArgMatcher<'a> { pub fn new() -> Self { ArgMatcher::default() } + pub fn process_arg_overrides<'b>(&mut self, a: Option<&AnyArg<'a, 'b>>, overrides: &mut Vec<(&'b str, &'a str)>, required: &mut Vec<&'a str>) { + debugln!("ArgMatcher::process_arg_overrides:{:?};", a.map_or(None, |a| Some(a.name()))); + if let Some(aa) = a { + if let Some(a_overrides) = aa.overrides() { + for overr in a_overrides { + debugln!("ArgMatcher::process_arg_overrides:iter:{};", overr); + if self.is_present(overr) { + debugln!("ArgMatcher::process_arg_overrides:iter:{}: removing from matches;", overr); + self.remove(overr); + for i in (0 .. required.len()).rev() { + if &required[i] == overr { + debugln!("ArgMatcher::process_arg_overrides:iter:{}: removing required;", overr); + required.swap_remove(i); + break; + } + } + } else { + overrides.push((overr, aa.name())); + } + } + } + } + } + + pub fn is_present(&self, name: &str) -> bool { + self.0.is_present(name) + } + pub fn propagate_globals(&mut self, global_arg_vec: &[&'a str]) { - debugln!( - "ArgMatcher::get_global_values: global_arg_vec={:?}", - global_arg_vec - ); + debugln!( "ArgMatcher::get_global_values: global_arg_vec={:?}", global_arg_vec ); let mut vals_map = HashMap::new(); self.fill_in_global_values(global_arg_vec, &mut vals_map); } diff --git a/src/macros.rs b/src/macros.rs index 51bb95a7..52fb35a4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -853,15 +853,15 @@ macro_rules! write_nspaces { } // convenience macro for remove an item from a vec -macro_rules! vec_remove_all { - ($vec:expr, $to_rem:expr) => { - debugln!("vec_remove_all! to_rem={:?}", $to_rem); - for i in (0 .. $vec.len()).rev() { - let should_remove = $to_rem.any(|name| name == &$vec[i]); - if should_remove { $vec.swap_remove(i); } - } - }; -} +//macro_rules! vec_remove_all { +// ($vec:expr, $to_rem:expr) => { +// debugln!("vec_remove_all! to_rem={:?}", $to_rem); +// for i in (0 .. $vec.len()).rev() { +// let should_remove = $to_rem.any(|name| name == &$vec[i]); +// if should_remove { $vec.swap_remove(i); } +// } +// }; +//} macro_rules! find_from { ($_self:expr, $arg_name:expr, $from:ident, $matcher:expr) => {{ let mut ret = None; @@ -892,36 +892,49 @@ macro_rules! find_from { }}; } -macro_rules! find_name_from { - ($_self:expr, $arg_name:expr, $from:ident, $matcher:expr) => {{ - let mut ret = None; - for k in $matcher.arg_names() { - if let Some(f) = find_by_name!($_self, k, flags, iter) { - if let Some(ref v) = f.$from() { - if v.contains($arg_name) { - ret = Some(f.b.name); - } - } - } - if let Some(o) = find_by_name!($_self, k, opts, iter) { - if let Some(ref v) = o.$from() { - if v.contains(&$arg_name) { - ret = Some(o.b.name); - } - } - } - if let Some(pos) = find_by_name!($_self, k, positionals, values) { - if let Some(ref v) = pos.$from() { - if v.contains($arg_name) { - ret = Some(pos.b.name); - } - } - } - } - ret - }}; -} +//macro_rules! find_name_from { +// ($_self:expr, $arg_name:expr, $from:ident, $matcher:expr) => {{ +// let mut ret = None; +// for k in $matcher.arg_names() { +// if let Some(f) = find_by_name!($_self, k, flags, iter) { +// if let Some(ref v) = f.$from() { +// if v.contains($arg_name) { +// ret = Some(f.b.name); +// } +// } +// } +// if let Some(o) = find_by_name!($_self, k, opts, iter) { +// if let Some(ref v) = o.$from() { +// if v.contains(&$arg_name) { +// ret = Some(o.b.name); +// } +// } +// } +// if let Some(pos) = find_by_name!($_self, k, positionals, values) { +// if let Some(ref v) = pos.$from() { +// if v.contains($arg_name) { +// ret = Some(pos.b.name); +// } +// } +// } +// } +// ret +// }}; +//} + +macro_rules! find_any_by_name { + ($p:expr, $name:expr) => { + { + fn as_trait_obj<'a, 'b, T: AnyArg<'a, 'b>>(x: &T) -> &AnyArg<'a, 'b> { x } + find_by_name!($p, $name, flags, iter).map(as_trait_obj).or( + find_by_name!($p, $name, opts, iter).map(as_trait_obj).or( + find_by_name!($p, $name, positionals, values).map(as_trait_obj) + ) + ) + } + } +} // Finds an arg by name macro_rules! find_by_name { ($p:expr, $name:expr, $what:ident, $how:ident) => { From 7ac5a5af9f5e743aeb6421a980b739d6069b0967 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 10:24:24 -0500 Subject: [PATCH 10/15] tests: cleans up certain test cases litle by little --- src/app/mod.rs | 2 -- tests/global_args.rs | 2 +- tests/groups.rs | 42 +++++++++++++++++++-------------------- tests/multiple_values.rs | 7 +++++-- tests/posix_compatible.rs | 9 ++++----- tests/possible_values.rs | 2 +- tests/subcommands.rs | 26 ++++++++++++++++-------- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 9c2d852b..908e1717 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,6 +1,4 @@ mod settings; -#[macro_use] -mod macros; pub mod parser; mod meta; mod help; diff --git a/tests/global_args.rs b/tests/global_args.rs index 6bcf11c5..28eaac11 100644 --- a/tests/global_args.rs +++ b/tests/global_args.rs @@ -4,7 +4,7 @@ extern crate regex; #[cfg(test)] mod tests { include!("../clap-test.rs"); - use clap::{App, Arg, SubCommand, ArgMatches}; + use clap::{App, Arg, SubCommand}; fn get_app() -> App<'static, 'static> { App::new("myprog") diff --git a/tests/groups.rs b/tests/groups.rs index fa497bfb..fde3f32c 100644 --- a/tests/groups.rs +++ b/tests/groups.rs @@ -55,36 +55,45 @@ fn non_existing_arg() { #[test] fn group_single_value() { - let m = App::new("group") + let res = App::new("group") .args_from_usage("-f, --flag 'some flag' -c, --color [color] 'some option'") .group(ArgGroup::with_name("grp") .args(&["flag", "color"])) - .get_matches_from(vec!["", "-c", "blue"]); + .get_matches_from_safe(vec!["", "-c", "blue"]); + assert!(res.is_ok()); + + let m = res.unwrap(); assert!(m.is_present("grp")); assert_eq!(m.value_of("grp").unwrap(), "blue"); } #[test] fn group_single_flag() { - let m = App::new("group") + let res = App::new("group") .args_from_usage("-f, --flag 'some flag' -c, --color [color] 'some option'") .group(ArgGroup::with_name("grp") .args(&["flag", "color"])) - .get_matches_from(vec!["", "-f"]); + .get_matches_from_safe(vec!["", "-f"]); + assert!(res.is_ok()); + + let m = res.unwrap(); assert!(m.is_present("grp")); assert!(m.value_of("grp").is_none()); } #[test] fn group_empty() { - let m = App::new("group") + let res = App::new("group") .args_from_usage("-f, --flag 'some flag' -c, --color [color] 'some option'") .group(ArgGroup::with_name("grp") .args(&["flag", "color"])) - .get_matches_from(vec![""]); + .get_matches_from_safe(vec![""]); + assert!(res.is_ok()); + + let m = res.unwrap(); assert!(!m.is_present("grp")); assert!(m.value_of("grp").is_none()); } @@ -105,12 +114,15 @@ fn group_reqired_flags_empty() { #[test] fn group_multi_value_single_arg() { - let m = App::new("group") + let res = App::new("group") .args_from_usage("-f, --flag 'some flag' -c, --color [color]... 'some option'") .group(ArgGroup::with_name("grp") .args(&["flag", "color"])) - .get_matches_from(vec!["", "-c", "blue", "red", "green"]); + .get_matches_from_safe(vec!["", "-c", "blue", "red", "green"]); + assert!(res.is_ok(), "{:?}", res.unwrap_err().kind); + + let m = res.unwrap(); assert!(m.is_present("grp")); assert_eq!(&*m.values_of("grp").unwrap().collect::>(), &["blue", "red", "green"]); } @@ -148,19 +160,7 @@ fn req_group_with_conflict_usage_string() { .args(&["base", "delete"]) .required(true)); - assert!(test::compare_output(app, "clap-test --delete base", REQ_GROUP_CONFLICT_REV, true)); -} - -#[test] -fn req_group_with_conflict_rev_usage_string() { - let app = App::new("req_group") - .arg(Arg::from_usage("[base] 'Base commit'").conflicts_with("delete")) - .arg(Arg::from_usage("-d, --delete 'Remove the base commit information'")) - .group(ArgGroup::with_name("base_or_delete") - .args(&["base", "delete"]) - .required(true)); - - assert!(test::compare_output(app, "clap-test base --delete", REQ_GROUP_CONFLICT_USAGE, true)); + assert!(test::compare_output2(app, "clap-test --delete base", REQ_GROUP_CONFLICT_REV, REQ_GROUP_CONFLICT_USAGE, true)); } #[test] diff --git a/tests/multiple_values.rs b/tests/multiple_values.rs index 32f8a08e..7f35e06a 100644 --- a/tests/multiple_values.rs +++ b/tests/multiple_values.rs @@ -1106,13 +1106,16 @@ fn multiple_value_terminator_option_other_arg() { #[test] fn multiple_vals_with_hyphen() { - let m = App::new("do") + let res = App::new("do") .arg(Arg::with_name("cmds") .multiple(true) .allow_hyphen_values(true) .value_terminator(";")) .arg(Arg::with_name("location")) - .get_matches_from(vec!["do", "find", "-type", "f", "-name", "special", ";", "/home/clap"]); + .get_matches_from_safe(vec!["do", "find", "-type", "f", "-name", "special", ";", "/home/clap"]); + assert!(res.is_ok(), "{:?}", res.unwrap_err().kind); + + let m = res.unwrap(); let cmds: Vec<_> = m.values_of("cmds").unwrap().collect(); assert_eq!(&cmds, &["find", "-type", "f", "-name", "special"]); assert_eq!(m.value_of("location"), Some("/home/clap")); diff --git a/tests/posix_compatible.rs b/tests/posix_compatible.rs index 060bc5f7..83f14481 100644 --- a/tests/posix_compatible.rs +++ b/tests/posix_compatible.rs @@ -166,14 +166,13 @@ fn pos_required_overridden_by_flag() { #[test] fn require_overriden_2() { let m = App::new("require_overriden") - .arg(Arg::with_name("flag") - .index(1) + .arg(Arg::with_name("req_pos") .required(true)) .arg(Arg::from_usage("-c, --color 'other flag'") - .overrides_with("flag")) - .get_matches_from(vec!["", "-c", "flag"]); + .overrides_with("req_pos")) + .get_matches_from(vec!["", "-c", "req_pos"]); assert!(!m.is_present("color")); - assert!(m.is_present("flag")); + assert!(m.is_present("req_pos")); } #[test] diff --git a/tests/possible_values.rs b/tests/possible_values.rs index 0085c9e9..4939a7d7 100644 --- a/tests/possible_values.rs +++ b/tests/possible_values.rs @@ -3,7 +3,7 @@ extern crate regex; include!("../clap-test.rs"); -#[allow(unsused_imports)] +#[allow(unused_imports)] use std::ascii::AsciiExt; use clap::{App, Arg, ErrorKind}; diff --git a/tests/subcommands.rs b/tests/subcommands.rs index e15de696..de2f8b56 100644 --- a/tests/subcommands.rs +++ b/tests/subcommands.rs @@ -32,22 +32,22 @@ SUBCOMMANDS: test Some help"; #[cfg(feature = "suggestions")] -static DYM: &'static str = "error: The subcommand 'subcm' wasn't recognized -\tDid you mean 'subcmd'? +static DYM_SUBCMD: &'static str = "error: The subcommand 'subcm' wasn't recognized + Did you mean 'subcmd'? -If you believe you received this message in error, try re-running with 'clap-test -- subcm' +If you believe you received this message in error, try re-running with 'dym -- subcm' USAGE: - clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] + dym [SUBCOMMAND] For more information try --help"; #[cfg(feature = "suggestions")] -static DYM2: &'static str = "error: Found argument '--subcm' which wasn't expected, or isn't valid in this context +static DYM_ARG: &'static str = "error: Found argument '--subcm' which wasn't expected, or isn't valid in this context \tDid you mean to put '--subcmdarg' after the subcommand 'subcmd'? USAGE: - clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] + dym [SUBCOMMAND] For more information try --help"; @@ -129,8 +129,18 @@ fn multiple_aliases() { #[test] #[cfg(feature="suggestions")] fn subcmd_did_you_mean_output() { - assert!(test::compare_output(test::complex_app(), "clap-test subcm", DYM, true)); - assert!(test::compare_output(test::complex_app(), "clap-test --subcm foo", DYM2, true)); + let app = App::new("dym") + .subcommand(SubCommand::with_name("subcmd")); + assert!(test::compare_output(app, "dym subcm", DYM_SUBCMD, true)); +} + +#[test] +#[cfg(feature="suggestions")] +fn subcmd_did_you_mean_output_arg() { + let app = App::new("dym") + .subcommand(SubCommand::with_name("subcmd") + .arg_from_usage("-s --subcmdarg [subcmdarg] 'tests'") ); + assert!(test::compare_output(app, "dym --subcm foo", DYM_ARG, true)); } #[test] From eb8d919e6f3443db279ba0c902f15d76676c02dc Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 10:40:29 -0500 Subject: [PATCH 11/15] perf: further debloating by removing generics from error cases --- src/errors.rs | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index c4108a64..664819d6 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -8,7 +8,7 @@ use std::process; use std::result::Result as StdResult; // Internal -use args::{AnyArg, FlagBuilder}; +use args::AnyArg; use fmt::{ColorWhen, Colorizer, ColorizerOption}; use suggestions; @@ -404,14 +404,13 @@ impl Error { pub fn write_to(&self, w: &mut W) -> io::Result<()> { write!(w, "{}", self.message) } #[doc(hidden)] - pub fn argument_conflict<'a, 'b, A, O, U>( - arg: &A, + pub fn argument_conflict<'a, 'b, O, U>( + arg: &AnyArg, other: Option, usage: U, color: ColorWhen, ) -> Self where - A: AnyArg<'a, 'b> + Display, O: Into, U: Display, { @@ -444,9 +443,8 @@ impl Error { } #[doc(hidden)] - pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U, color: ColorWhen) -> Self + pub fn empty_value<'a, 'b, U>(arg: &AnyArg, usage: U, color: ColorWhen) -> Self where - A: AnyArg<'a, 'b> + Display, U: Display, { let c = Colorizer::new(ColorizerOption { @@ -470,17 +468,16 @@ impl Error { } #[doc(hidden)] - pub fn invalid_value<'a, 'b, B, G, A, U>( + pub fn invalid_value<'a, 'b, B, G, U>( bad_val: B, good_vals: &[G], - arg: &A, + arg: &AnyArg, usage: U, color: ColorWhen, ) -> Self where B: AsRef, G: AsRef + Display, - A: AnyArg<'a, 'b> + Display, U: Display, { let c = Colorizer::new(ColorizerOption { @@ -660,10 +657,9 @@ impl Error { } #[doc(hidden)] - pub fn too_many_values<'a, 'b, V, A, U>(val: V, arg: &A, usage: U, color: ColorWhen) -> Self + pub fn too_many_values<'a, 'b, V, U>(val: V, arg: &AnyArg, usage: U, color: ColorWhen) -> Self where V: AsRef + Display + ToOwned, - A: AnyArg<'a, 'b> + Display, U: Display, { let v = val.as_ref(); @@ -689,15 +685,14 @@ impl Error { } #[doc(hidden)] - pub fn too_few_values<'a, 'b, A, U>( - arg: &A, + pub fn too_few_values<'a, 'b, U>( + arg: &AnyArg, min_vals: u64, curr_vals: usize, usage: U, color: ColorWhen, ) -> Self where - A: AnyArg<'a, 'b> + Display, U: Display, { let c = Colorizer::new(ColorizerOption { @@ -724,9 +719,7 @@ impl Error { } #[doc(hidden)] - pub fn value_validation<'a, 'b, A>(arg: Option<&A>, err: String, color: ColorWhen) -> Self - where - A: AnyArg<'a, 'b> + Display, + pub fn value_validation<'a, 'b>(arg: Option<&AnyArg>, err: String, color: ColorWhen) -> Self { let c = Colorizer::new(ColorizerOption { use_stderr: true, @@ -750,13 +743,13 @@ impl Error { #[doc(hidden)] pub fn value_validation_auto(err: String) -> Self { - let n: Option<&FlagBuilder> = None; + let n: Option<&AnyArg> = None; Error::value_validation(n, err, ColorWhen::Auto) } #[doc(hidden)] - pub fn wrong_number_of_values<'a, 'b, A, S, U>( - arg: &A, + pub fn wrong_number_of_values<'a, 'b, S, U>( + arg: &AnyArg, num_vals: u64, curr_vals: usize, suffix: S, @@ -764,7 +757,6 @@ impl Error { color: ColorWhen, ) -> Self where - A: AnyArg<'a, 'b> + Display, S: Display, U: Display, { @@ -792,9 +784,8 @@ impl Error { } #[doc(hidden)] - pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U, color: ColorWhen) -> Self + pub fn unexpected_multiple_usage<'a, 'b, U>(arg: &AnyArg, usage: U, color: ColorWhen) -> Self where - A: AnyArg<'a, 'b> + Display, U: Display, { let c = Colorizer::new(ColorizerOption { From e1313a5a0f69d8f4016f73b860a63af8318a6676 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 11:04:56 -0500 Subject: [PATCH 12/15] docs: updates contributors list --- CONTRIBUTORS.md | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d6b83808..c4f23859 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -17,69 +17,69 @@ the following is a list of contributors: :---: |:---: |:---: |:---: |:---: |:---: | [Arnavion](https://github.com/Arnavion) |[japaric](https://github.com/japaric) |[untitaker](https://github.com/untitaker) |[afiune](https://github.com/afiune) |[crazymerlyn](https://github.com/crazymerlyn) |[SuperFluffy](https://github.com/SuperFluffy) | -[malbarbo](https://github.com/malbarbo) |[matthiasbeyer](https://github.com/matthiasbeyer) |[gohyda](https://github.com/gohyda) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) |[jimmycuadra](https://github.com/jimmycuadra) | +[matthiasbeyer](https://github.com/matthiasbeyer) |[malbarbo](https://github.com/malbarbo) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) |[jimmycuadra](https://github.com/jimmycuadra) |[Nemo157](https://github.com/Nemo157) | :---: |:---: |:---: |:---: |:---: |:---: | -[malbarbo](https://github.com/malbarbo) |[matthiasbeyer](https://github.com/matthiasbeyer) |[gohyda](https://github.com/gohyda) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) |[jimmycuadra](https://github.com/jimmycuadra) | +[matthiasbeyer](https://github.com/matthiasbeyer) |[malbarbo](https://github.com/malbarbo) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) |[jimmycuadra](https://github.com/jimmycuadra) |[Nemo157](https://github.com/Nemo157) | -[Nemo157](https://github.com/Nemo157) |[SShrike](https://github.com/SShrike) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) |[frewsxcv](https://github.com/frewsxcv) | +[severen](https://github.com/severen) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) |[frewsxcv](https://github.com/frewsxcv) |[hoodie](https://github.com/hoodie) | :---: |:---: |:---: |:---: |:---: |:---: | -[Nemo157](https://github.com/Nemo157) |[SShrike](https://github.com/SShrike) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) |[frewsxcv](https://github.com/frewsxcv) | +[severen](https://github.com/severen) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) |[frewsxcv](https://github.com/frewsxcv) |[hoodie](https://github.com/hoodie) | -[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[porglezomp](https://github.com/porglezomp) |[kieraneglin](https://github.com/kieraneglin) | +[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[porglezomp](https://github.com/porglezomp) |[kieraneglin](https://github.com/kieraneglin) |[musoke](https://github.com/musoke) | :---: |:---: |:---: |:---: |:---: |:---: | -[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[porglezomp](https://github.com/porglezomp) |[kieraneglin](https://github.com/kieraneglin) | +[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[porglezomp](https://github.com/porglezomp) |[kieraneglin](https://github.com/kieraneglin) |[musoke](https://github.com/musoke) | -[musoke](https://github.com/musoke) |[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) |[vmchale](https://github.com/vmchale) |[messense](https://github.com/messense) | +[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) |[vmchale](https://github.com/vmchale) |[messense](https://github.com/messense) |[Keats](https://github.com/Keats) | :---: |:---: |:---: |:---: |:---: |:---: | -[musoke](https://github.com/musoke) |[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) |[vmchale](https://github.com/vmchale) |[messense](https://github.com/messense) | +[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) |[vmchale](https://github.com/vmchale) |[messense](https://github.com/messense) |[Keats](https://github.com/Keats) | -[Keats](https://github.com/Keats) |[starkat99](https://github.com/starkat99) |[durka](https://github.com/durka) |[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) | +[starkat99](https://github.com/starkat99) |[durka](https://github.com/durka) |[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) | :---: |:---: |:---: |:---: |:---: |:---: | -[Keats](https://github.com/Keats) |[starkat99](https://github.com/starkat99) |[durka](https://github.com/durka) |[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) | +[starkat99](https://github.com/starkat99) |[durka](https://github.com/durka) |[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) | -[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[nox](https://github.com/nox) |[pixelistik](https://github.com/pixelistik) |[brennie](https://github.com/brennie) |[ogham](https://github.com/ogham) | +[BurntSushi](https://github.com/BurntSushi) |[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[brennie](https://github.com/brennie) |[ogham](https://github.com/ogham) |[pixelistik](https://github.com/pixelistik) | :---: |:---: |:---: |:---: |:---: |:---: | -[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[nox](https://github.com/nox) |[pixelistik](https://github.com/pixelistik) |[brennie](https://github.com/brennie) |[ogham](https://github.com/ogham) | +[BurntSushi](https://github.com/BurntSushi) |[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[brennie](https://github.com/brennie) |[ogham](https://github.com/ogham) |[pixelistik](https://github.com/pixelistik) | -[Bilalh](https://github.com/Bilalh) |[dotdash](https://github.com/dotdash) |[bradurani](https://github.com/bradurani) |[Seeker14491](https://github.com/Seeker14491) |[brianp](https://github.com/brianp) |[casey](https://github.com/casey) | +[dotdash](https://github.com/dotdash) |[bradurani](https://github.com/bradurani) |[Seeker14491](https://github.com/Seeker14491) |[brianp](https://github.com/brianp) |[cldershem](https://github.com/cldershem) |[casey](https://github.com/casey) | :---: |:---: |:---: |:---: |:---: |:---: | -[Bilalh](https://github.com/Bilalh) |[dotdash](https://github.com/dotdash) |[bradurani](https://github.com/bradurani) |[Seeker14491](https://github.com/Seeker14491) |[brianp](https://github.com/brianp) |[casey](https://github.com/casey) | +[dotdash](https://github.com/dotdash) |[bradurani](https://github.com/bradurani) |[Seeker14491](https://github.com/Seeker14491) |[brianp](https://github.com/brianp) |[cldershem](https://github.com/cldershem) |[casey](https://github.com/casey) | [volks73](https://github.com/volks73) |[daboross](https://github.com/daboross) |[mernen](https://github.com/mernen) |[dguo](https://github.com/dguo) |[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) | :---: |:---: |:---: |:---: |:---: |:---: | [volks73](https://github.com/volks73) |[daboross](https://github.com/daboross) |[mernen](https://github.com/mernen) |[dguo](https://github.com/dguo) |[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) | -[eddyb](https://github.com/eddyb) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) |[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) |[SirVer](https://github.com/SirVer) | +[eddyb](https://github.com/eddyb) |[Enet4](https://github.com/Enet4) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) |[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) | :---: |:---: |:---: |:---: |:---: |:---: | -[eddyb](https://github.com/eddyb) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) |[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) |[SirVer](https://github.com/SirVer) | +[eddyb](https://github.com/eddyb) |[Enet4](https://github.com/Enet4) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) |[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) | -[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) |[jtdowney](https://github.com/jtdowney) | +[SirVer](https://github.com/SirVer) |[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) | :---: |:---: |:---: |:---: |:---: |:---: | -[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) |[jtdowney](https://github.com/jtdowney) | +[SirVer](https://github.com/SirVer) |[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) | -[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) |[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) |[mdaffin](https://github.com/mdaffin) | +[jtdowney](https://github.com/jtdowney) |[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) |[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) | :---: |:---: |:---: |:---: |:---: |:---: | -[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) |[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) |[mdaffin](https://github.com/mdaffin) | +[jtdowney](https://github.com/jtdowney) |[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) |[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) | -[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) |[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) |[Geogi](https://github.com/Geogi) | +[mdaffin](https://github.com/mdaffin) |[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) |[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) | :---: |:---: |:---: |:---: |:---: |:---: | -[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) |[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) |[Geogi](https://github.com/Geogi) | +[mdaffin](https://github.com/mdaffin) |[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) |[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) | -[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) |[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) |[hexjelly](https://github.com/hexjelly) | +[Geogi](https://github.com/Geogi) |[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) |[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) | :---: |:---: |:---: |:---: |:---: |:---: | -[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) |[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) |[hexjelly](https://github.com/hexjelly) | +[Geogi](https://github.com/Geogi) |[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) |[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) | -[rnelson](https://github.com/rnelson) |[swatteau](https://github.com/swatteau) |[tspiteri](https://github.com/tspiteri) |[siiptuo](https://github.com/siiptuo) |[vks](https://github.com/vks) |[vsupalov](https://github.com/vsupalov) | +[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) |[segevfiner](https://github.com/segevfiner) |[swatteau](https://github.com/swatteau) |[tspiteri](https://github.com/tspiteri) | :---: |:---: |:---: |:---: |:---: |:---: | -[rnelson](https://github.com/rnelson) |[swatteau](https://github.com/swatteau) |[tspiteri](https://github.com/tspiteri) |[siiptuo](https://github.com/siiptuo) |[vks](https://github.com/vks) |[vsupalov](https://github.com/vsupalov) | +[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) |[segevfiner](https://github.com/segevfiner) |[swatteau](https://github.com/swatteau) |[tspiteri](https://github.com/tspiteri) | -[mineo](https://github.com/mineo) |[wabain](https://github.com/wabain) |[grossws](https://github.com/grossws) |[kennytm](https://github.com/kennytm) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) | +[siiptuo](https://github.com/siiptuo) |[vks](https://github.com/vks) |[vsupalov](https://github.com/vsupalov) |[mineo](https://github.com/mineo) |[wabain](https://github.com/wabain) |[grossws](https://github.com/grossws) | :---: |:---: |:---: |:---: |:---: |:---: | -[mineo](https://github.com/mineo) |[wabain](https://github.com/wabain) |[grossws](https://github.com/grossws) |[kennytm](https://github.com/kennytm) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) | +[siiptuo](https://github.com/siiptuo) |[vks](https://github.com/vks) |[vsupalov](https://github.com/vsupalov) |[mineo](https://github.com/mineo) |[wabain](https://github.com/wabain) |[grossws](https://github.com/grossws) | -[mitsuhiko](https://github.com/mitsuhiko) | -:---: | -[mitsuhiko](https://github.com/mitsuhiko) | +[kennytm](https://github.com/kennytm) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) |[Bilalh](https://github.com/Bilalh) | +:---: |:---: |:---: |:---: | +[kennytm](https://github.com/kennytm) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) |[Bilalh](https://github.com/Bilalh) | From 92caafaf516518f8b5758ee16326896f9577e38c Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 11:08:47 -0500 Subject: [PATCH 13/15] chore: increase version --- CHANGELOG.md | 21 +++++++++++++++++++++ Cargo.toml | 2 +- README.md | 9 +++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43a9a897..03431f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ + +### 2.29.1 (2018-01-09) + + +#### Documentation + +* fixes broken links. ([56e734b8](https://github.com/kbknapp/clap-rs/commit/56e734b839303d733d2e5baf7dac39bd7b97b8e4)) +* updates contributors list ([e1313a5a](https://github.com/kbknapp/clap-rs/commit/e1313a5a0f69d8f4016f73b860a63af8318a6676)) + +#### Performance + +* further debloating by removing generics from error cases ([eb8d919e](https://github.com/kbknapp/clap-rs/commit/eb8d919e6f3443db279ba0c902f15d76676c02dc)) +* debloats clap by deduplicating logic and refactors ([03e413d7](https://github.com/kbknapp/clap-rs/commit/03e413d7175d35827cd7d8908d47dbae15a849a3)) + +#### Bug Fixes + +* fixes the ripgrep benchmark by adding a value to a flag that expects it ([d26ab2b9](https://github.com/kbknapp/clap-rs/commit/d26ab2b97cf9c0ea675b440b7b0eaf6ac3ad01f4)) +* **bash completion:** Change the bash completion script code generation to support hyphens. ([ba7f1d18](https://github.com/kbknapp/clap-rs/commit/ba7f1d18eba7a07ce7f57e0981986f66c994b639)) +* **completions/zsh.rs:** Fix completion of long option values ([46365cf8](https://github.com/kbknapp/clap-rs/commit/46365cf8be5331ba04c895eb183e2f230b5aad51)) + + ## 2.29.0 (2017-12-02) diff --git a/Cargo.toml b/Cargo.toml index 63e9922b..4c6bb773 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.29.0" +version = "2.29.1" authors = ["Kevin K. "] exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] repository = "https://github.com/kbknapp/clap-rs" diff --git a/README.md b/README.md index eb488a59..3bbe0d20 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,15 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) ## What's New +Here's whats new in 2.29.1: + +* Debloats clap by deduplicating logic and refactors for a ~57% decrease in code size! This is with zero functinoality lost, and a slight perf increase! +* Change the bash completion script code generation to support hyphens. +* Fix completion of long option values in ZSH completions +* Fixes broken links in docs +* Updates contributors list +* Fixes the ripgrep benchmark by adding a value to a flag that expects it + Here's whats new in 2.29.0: * **Arg:** adds Arg::hide_env_values(bool) which allows one to hide any current env values and display only the key in help messages From 3e97085e5828f2eff9aaf1f436ecd2d1efeb1bcc Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 12:02:35 -0500 Subject: [PATCH 14/15] chore: udpates html doc root --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index de23769c..d60eddd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -513,7 +513,7 @@ //! this repository for more information. #![crate_type = "lib"] -#![doc(html_root_url = "https://docs.rs/clap/2.29.0")] +#![doc(html_root_url = "https://docs.rs/clap/2.29.1")] #![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts, unused_import_braces, unused_allocation)] // Lints we'd like to deny but are currently failing for upstream crates From 3fef176543daf9cc4109b23b7395d21dde16927a Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 9 Jan 2018 12:07:19 -0500 Subject: [PATCH 15/15] docs: fixes typo in docs version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bbe0d20..95969569 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ subcommands: Since this feature requires additional dependencies that not everyone may want, it is *not* compiled in by default and we need to enable a feature flag in Cargo.toml: -Simply change your `clap = "2.29"` to `clap = {version = "2.87", features = ["yaml"]}`. +Simply change your `clap = "2.29"` to `clap = {version = "2.29", features = ["yaml"]}`. Finally we create our `main.rs` file just like we would have with the previous two examples: