diff --git a/CHANGELOG.md b/CHANGELOG.md
index 43844f33..01db9df7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,46 @@
+
+## v2.21.0 (2017-03-09)
+
+#### Performance
+
+* doesn't run `arg_post_processing` on multiple values anymore ([ec516182](https://github.com/kbknapp/clap-rs/commit/ec5161828729f6a53f0fccec8648f71697f01f78))
+* changes internal use of `VecMap` to `Vec` for matched values of `Arg`s ([22bf137a](https://github.com/kbknapp/clap-rs/commit/22bf137ac581684c6ed460d2c3c640c503d62621))
+* vastly reduces the amount of cloning when adding non-global args minus when they're added from `App::args` which is forced to clone ([8da0303b](https://github.com/kbknapp/clap-rs/commit/8da0303bc02db5fe047cfc0631a9da41d9dc60f7))
+* refactor to remove unneeded vectors and allocations and checks for significant performance increases ([0efa4119](https://github.com/kbknapp/clap-rs/commit/0efa4119632f134fc5b8b9695b007dd94b76735d))
+
+#### Documentation
+
+* Fix examples link in CONTRIBUTING.md ([60cf875d](https://github.com/kbknapp/clap-rs/commit/60cf875d67a252e19bb85054be57696fac2c57a1))
+
+#### Improvements
+
+* when `AppSettings::SubcommandsNegateReqs` and `ArgsNegateSubcommands` are used, a new more accurate double line usage string is shown ([50f02300](https://github.com/kbknapp/clap-rs/commit/50f02300d81788817acefef0697e157e01b6ca32), closes [#871](https://github.com/kbknapp/clap-rs/issues/871))
+
+#### API Additions
+
+* **Arg::last:** adds the ability to mark a positional argument as 'last' which means it should be used with `--` syntax and can be accessed early ([6a7aea90](https://github.com/kbknapp/clap-rs/commit/6a7aea9043b83badd9ab038b4ecc4c787716147e), closes [#888](https://github.com/kbknapp/clap-rs/issues/888))
+* provides `default_value_os` and `default_value_if[s]_os` ([0f2a3782](https://github.com/kbknapp/clap-rs/commit/0f2a378219a6930748d178ba350fe5925be5dad5), closes [#849](https://github.com/kbknapp/clap-rs/issues/849))
+* provides `App::help_message` and `App::version_message` which allows one to override the auto-generated help/version flag associated help ([389c413](https://github.com/kbknapp/clap-rs/commit/389c413b7023dccab8c76aa00577ea1d048e7a99), closes [#889](https://github.com/kbknapp/clap-rs/issues/889))
+
+#### New Settings
+
+* **InferSubcommands:** adds a setting to allow one to infer shortened subcommands or aliases (i.e. for subcommmand "test", "t", "te", or "tes" would be allowed assuming no other ambiguities) ([11602032](https://github.com/kbknapp/clap-rs/commit/11602032f6ff05881e3adf130356e37d5e66e8f9), closes [#863](https://github.com/kbknapp/clap-rs/issues/863))
+
+#### Bug Fixes
+
+* doesn't print the argument sections in the help message if all args in that section are hidden ([ce5ee5f5](https://github.com/kbknapp/clap-rs/commit/ce5ee5f5a76f838104aeddd01c8ec956dd347f50))
+* doesn't include the various [ARGS] [FLAGS] or [OPTIONS] if the only ones available are hidden ([7b4000af](https://github.com/kbknapp/clap-rs/commit/7b4000af97637703645c5fb2ac8bb65bd546b95b), closes [#882](https://github.com/kbknapp/clap-rs/issues/882))
+* now correctly shows subcommand as required in the usage string when AppSettings::SubcommandRequiredElseHelp is used ([8f0884c1](https://github.com/kbknapp/clap-rs/commit/8f0884c1764983a49b45de52a1eddf8d721564d8))
+* fixes some memory leaks when an error is detected and clap exits ([8c2dd287](https://github.com/kbknapp/clap-rs/commit/8c2dd28718262ace4ae0db98563809548e02a86b))
+* fixes a trait that's marked private accidentlly, but should be crate internal public ([1ae21108](https://github.com/kbknapp/clap-rs/commit/1ae21108015cea87e5360402e1747025116c7878))
+* **Completions:** fixes a bug that tried to propogate global args multiple times when generating multiple completion scripts ([5e9b9cf4](https://github.com/kbknapp/clap-rs/commit/5e9b9cf4dd80fa66a624374fd04e6545635c1f94), closes [#846](https://github.com/kbknapp/clap-rs/issues/846))
+
+#### Features
+
+* **Options:** adds the ability to require the equals syntax with options --opt=val ([f002693d](https://github.com/kbknapp/clap-rs/commit/f002693dec6a6959c4e9590cb7b7bfffd6d6e5bc), closes [#833](https://github.com/kbknapp/clap-rs/issues/833))
+
+
+
### v2.20.5 (2017-02-18)
diff --git a/Cargo.toml b/Cargo.toml
index da02d8a2..d196268b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,9 +22,9 @@ unicode-width = "0.1.4"
unicode-segmentation = "1.0.1"
strsim = { version = "0.6.0", optional = true }
ansi_term = { version = "0.9.0", optional = true }
-term_size = { version = "0.2.2", optional = true }
+term_size = { version = "0.2.3", optional = true }
yaml-rust = { version = "0.3.5", optional = true }
-clippy = { version = "~0.0.112", optional = true }
+clippy = { version = "~0.0.118", optional = true }
atty = { version = "0.2.2", optional = true }
[dev-dependencies]
@@ -65,7 +65,7 @@ debug = true
rpath = false
lto = false
debug-assertions = true
-codegen-units = 2
+codegen-units = 4
[profile.bench]
opt-level = 3
diff --git a/README.md b/README.md
index 7b232784..6d21ae61 100644
--- a/README.md
+++ b/README.md
@@ -45,131 +45,24 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New
-Here's the highlights for v2.20.5
+Here's the highlights for v2.21.0
+* adds the ability to mark a positional argument as 'last' which means it should be used with `--` syntax and can be accessed early to effectivly skip other positional args
+* Some performance improvements by reducing the ammount of duplicate work, cloning, and allocations in all cases.
+* Some massive perfomance gains when using many args (i.e. things like shell glob expansions)
+* adds a setting to allow one to infer shortened subcommands or aliases (i.e. for subcommmand "test", "t", "te", or "tes" would be allowed assuming no other ambiguities)
+* when `AppSettings::SubcommandsNegateReqs` and `ArgsNegateSubcommands` are used, a new more accurate double line usage string is shown
+* provides `default_value_os` and `default_value_if[s]_os`
+* provides `App::help_message` and `App::version_message` which allows one to override the auto-generated help/version flag associated help
+* adds the ability to require the equals syntax with options `--opt=val`
+* doesn't print the argument sections in the help message if all args in that section are hidden
+* doesn't include the various `[ARGS]` `[FLAGS]` or `[OPTIONS]` if the only ones available are hidden
+* now correctly shows subcommand as required in the usage string when AppSettings::SubcommandRequiredElseHelp is used
+* fixes some "memory leaks" when an error is detected and clap exits
+* fixes a trait that's marked private accidentlly, but should be crate internal public
+* fixes a bug that tried to propogate global args multiple times when generating multiple completion scripts
* Fixes a critical bug in the `clap_app!` macro of a missing fragment specifier when using `!property` style tags.
-
-
-Here's the highlights from v2.0.0 to v2.20.4
-
-* Fixes a bug that tried to propogate global args multiple times when generating multiple completion scripts
* Fix examples link in CONTRIBUTING.md
-* **Completions**: fixes bash completions for commands that have an underscore in the name
-* **Completions**: fixes a bug where ZSH completions would panic if the binary name had an underscore in it
-* allow final word to be wrapped in wrap_help
-* **Completions**: fixes a bug where global args weren't included in the generated completion scripts
-* **Macros Documentation:** adds a warning about changing values in Cargo.toml not triggering a rebuild automatically
-* Fixes a critical bug where subcommand settings were being propogated too far
-* Adds ArgGroup::multiple to the supported YAML fields for building ArgGroups from YAML
-* Fixes a bug where the final word wasn't wrapped in help messages
-* Fixes finding required arguments in group arguments
-* **ArgsNegateSubcommands:** disables args being allowed between subcommands
-* **DontCollapseArgsInUsage:** disables the collapsing of positional args into `[ARGS]` in the usage string
-* **DisableHelpSubcommand:** disables building the `help` subcommand
-* **AllowMissingPositional:** allows one to implement `$ prog [optional] ` style CLIs where the second postional argument is required, but the first is optional
-* **PropagateGlobalValuesDown:** automatically propagats global arg's values down through *used* subcommands
-* **Arg::value_terminator:** adds the ability to terminate multiple values with a given string or char
-* **Arg::default_value_if[s]:** adds new methods for *conditional* default values (such as a particular value from another argument was used)
-* **Arg::requires_if[s]:** adds the ability to *conditionally* require additional args (such as if a particular value was used)
-* **Arg::required_if[s]:** adds the ability for an arg to be *conditionally* required (i.e. "arg X is only required if arg Y was used with value Z")
-* **Arg::validator_os:** adds ability to validate values which may contain invalid UTF-8
-* **crate_description!:** Uses the `Cargo.toml` description field to fill in the `App::about` method at compile time
-* **crate_name!:** Uses the `Cargo.toml` name field to fill in the `App::new` method at compile time
-* **app_from_crate!:** Combines `crate_version!`, `crate_name!`, `crate_description!`, and `crate_authors!` into a single macro call to build a default `App` instance from the `Cargo.toml` fields
-* **no_cargo:** adds a `no_cargo` feature to disable Cargo-env-var-dependent macros for those *not* using `cargo` to build their crates
-* **Options:** fixes a critical bug where options weren't forced to have a value
-* fixes a bug where calling the help of a subcommand wasn't ignoring required args of parent commands
-* **Help Subcommand:** fixes a bug where the help subcommand couldn't be overriden
-* **Low Index Multiples:** fixes a bug which caused combinations of LowIndexMultiples and `Arg::allow_hyphen_values` to fail parsing
-* **Default Values:** improves the error message when default values are involved
-* **YAML:** adds conditional requirements and conditional default values to YAML
-* Support `--("some-arg-name")` syntax for defining long arg names when using `clap_app!` macro
-* Support `("some app name")` syntax for defining app names when using `clap_app!` macro
-* **Help Wrapping:** long app names (with spaces), authors, and descriptions are now wrapped appropriately
-* **Conditional Default Values:** fixes the failing doc tests of Arg::default_value_ifs
-* **Conditional Requirements:** adds docs for Arg::requires_ifs
-* Fixes a bug where calling the help of a subcommand wasn't ignoring required args of parent commands
-* Fixes a bug by escaping square brackets in ZSH completions which were causing conflicts and errors.
-* **Bash Completion:** allows bash completion to fall back to traidtional bash completion upon no matching completing function
-* **Arg Setting**: Allows specifying an `AllowLeadingHyphen` style setting for values only for specific args, vice command wide
-* **Validators:** improves the error messages for validators
-* **Required Unless:** fixes a bug where having required_unless set doesn't work when conflicts are also set
-* **ZSH Completions:** fixes an issue where zsh completions caused panics if there were no subcommands
-* **Completions:** Adds completion support for Microsoft PowerShell! (Thanks to @Arnavion)
-* Allows specifying the second to last positional argument as `multiple(true)` (i.e. things such as `mv ... `)
-* Adds an `App::get_name` and `App::get_bin_name`
-* Conflicting argument errors are now symetrical, meaning more consistent and better usage suggestions
-* **Completions:** adds automatic ZSH completion script generation support! :tada: :tada:
-* **AppSettings:** adds new setting `AppSettings::AllowNegativeNumbers` which functions like `AllowLeadingHyphen` except only allows undefined negative numbers to pass parsing.
-* Stabilize `clap_app!` macro (i.e. no longer need to use `unstable` feature)
-* Deprecate `App::with_defaults`
-* One can now alias arguments either visibly (which appears in the help text) or invisibly just like subcommands!
-* The `from_usage` parser now correctly handles non-ascii names / options and help!
-* **Value Delimiters:** fixes the confusion around implicitly setting value delimiters. (The default is to *not* use a delimiter unless explicitly set)
-* Changes the default value delimiter rules (i.e. the default is `use_delimiter(false)` *unless* a setting/method that implies multiple values was used) **[Bugfix that *may* "break" code]**
- * If code breaks, simply add `Arg::use_delimiter(true)` to the affected args
-* Adds ability to hide the possible values from the help text on a per argument basis, instead of command wide
-* Allows for limiting detected terminal width (i.e. wrap at `x` length, unless the terminal width is *smaller*)
-* `clap` now ignores hard newlines in help messages and properly re-aligns text, but still wraps if the term width is too small
-* Adds support for the setting `Arg::require_delimiter` from YAML
-* `clap` no longer requires one to use `{n}` inside help text to insert a newline that is properly aligned. One can now use the normal `\n`.
-* `clap` now ignores hard newlines in help messages and properly re-aligns text, but still wraps if the term width is too small
-* Errors can now have custom description
-* Uses `term_size` instead of home-grown solution on Windows
-* Adds the ability to wrap help text intelligently on Windows!
-* Moves docs to [docs.rs!](https://docs.rs/clap/)!
-* Automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long
-* Vastly improves *development* error messages when using YAML
-* Adds a shorthand way to ignore help text wrapping and use source formatting (i.e. `App::set_term_width(0)`)
-* **Help Subcommand:** fixes misleading usage string when using multi-level subcommmands such as `myprog help subcmd1 subcmd2`
-* **YAML:** allows using lists or single values with certain arg declarations for increased ergonomics
-* **Fish Shell Completions:** one can generate a basic fish completions script at compile time!
-* Adds the ability to generate completions to an `io::Write` object
-* Adds an `App::unset_setting` and `App::unset_settings`
-* **Completions:** one can now [generate a bash completions](https://docs.rs/clap/2.9.0/clap/struct.App.html#method.gen_completions) script at compile time! These completions work with options using [possible values](https://docs.rs/clap/2.9.0/clap/struct.Arg.html#method.possible_values), [subcommand aliases](https://docs.rs/clap/2.9.0/clap/struct.App.html#method.aliases), and even multiple levels of subcommands
-* **Arg:** adds new optional setting [`Arg::require_delimiter`](https://docs.rs/clap/2.8.0/clap/struct.Arg.html#method.require_delimiter) which requires val delimiter to parse multiple values
-* The terminal sizing portion has been factored out into a separate crate, [term_size](https://crates.io/crates/term_size)
-* Options using multiple values and delimiters no longer parse additional values after a trailing space (i.e. `prog -o 1,2 file.txt` parses as `1,2` for `-o` and `file.txt` for a positional arg)
-* Using options using multiple values and with an `=` no longer parse args after the trailing space as values (i.e. `prog -o=1 file.txt` parses as `1` for `-o` and `file.txt` for a positional arg)
-* **Usage Strings:** `[FLAGS]` and `[ARGS]` are no longer blindly added to usage strings, instead only when applicable
-* `arg_enum!`: allows using more than one meta item, or things like `#[repr(C)]` with `arg_enum!`s
-* `App::print_help`: now prints the same as would have been printed by `--help` or the like
-* Prevents invoking ` help help` and displaying incorrect help message
-* Subcommand help messages requested via ` help ` now correctly match ` --help`
-* One can now specify groups which require AT LEAST one of the args
-* Allows adding multiple ArgGroups per Arg
-* **Global Settings:** One can now set an `AppSetting` which is propogated down through child subcommands
-* **Terminal Wrapping:** Allows wrapping at specified term width (Even on Windows!) (can now set an absolute width to "smart" wrap at)
-* **SubCommands/Aliases:** adds support for visible aliases for subcommands (i.e. aliases that are dipslayed in the help message)
-* **Subcommands/Aliases:** when viewing the help of an alias, it now display help of the aliased subcommand
-* Adds new setting to stop delimiting values with `--` or `AppSettings::TrailingVarArg`
-* Subcommands now support aliases - think of them as hidden subcommands that dispatch to said subcommand automatically
-* Fixed times when `ArgGroup`s are duplicated in usage strings
-* **Before Help:** adds support for displaying info before help message
-* **Required Unless:** adds support for allowing args that are required unless certain other args are present
-* **New Help Template Engine!**: Now you have full control over the layout of your help message. Major thanks to @hgrecco
-* **Pull crate Authors from Cargo.toml**: One can now use the `crate_authors!` macro to automatically pull the crate authors from their Cargo.toml file
-* **Colored Help Messages**: Help messages can now be optionally colored (See the `AppSettings::ColoredHelp` setting). Screenshot below.
-* **Help text auto wraps and aligns at for subcommands too!** - Long help strings of subcommands will now properly wrap and align to term width on Linux and OS X. This can be turned off as well.
-* **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OS X (and presumably Unix too). This can be turned off as well.
-* **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arrange the order of your args and subcommands in help message. This helps to emphasize more popular or important options.
-* **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order!
-* **Help subcommand now accepts other subcommands as arguments!** - Similar to other CLI precedents, the `help` subcommand can now accept other subcommands as arguments to display their help message. i.e. `$ myprog help mysubcmd` (*Note* these can even be nested heavily such as `$ myprog help subcmd1 subcmd2 subcmd3` etc.)
-* **Default Values**: Args can now specify default values
-* **Next Line Help**: Args can have help strings on the line following the argument (useful for long arguments, or those with many values). This can be set command-wide or for individual args
-
-Here's a gif of them in action!
-
-
-
-An example of the help text wrapping at term width:
-
-
-
-An example of the optional colored help:
-
-
-
For full details, see [CHANGELOG.md](https://github.com/kbknapp/clap-rs/blob/master/CHANGELOG.md)
diff --git a/benches/02_simple.rs b/benches/02_simple.rs
index eab94b37..010fa866 100644
--- a/benches/02_simple.rs
+++ b/benches/02_simple.rs
@@ -3,7 +3,7 @@
extern crate clap;
extern crate test;
-use clap::App;
+use clap::{App, Arg};
use test::Bencher;
@@ -25,6 +25,69 @@ fn build_app(b: &mut Bencher) {
b.iter(|| create_app!());
}
+#[bench]
+fn add_flag(b: &mut Bencher) {
+ fn build_app() -> App<'static, 'static> {
+ App::new("claptests")
+ }
+
+ b.iter(|| build_app().arg(Arg::from_usage("-s, --some 'something'")));
+}
+
+#[bench]
+fn add_flag_ref(b: &mut Bencher) {
+ fn build_app() -> App<'static, 'static> {
+ App::new("claptests")
+ }
+
+ b.iter(|| {
+ let arg = Arg::from_usage("-s, --some 'something'");
+ build_app().arg(&arg)
+ });
+}
+
+#[bench]
+fn add_opt(b: &mut Bencher) {
+ fn build_app() -> App<'static, 'static> {
+ App::new("claptests")
+ }
+
+ b.iter(|| build_app().arg(Arg::from_usage("-s, --some 'something'")));
+}
+
+#[bench]
+fn add_opt_ref(b: &mut Bencher) {
+ fn build_app() -> App<'static, 'static> {
+ App::new("claptests")
+ }
+
+ b.iter(|| {
+ let arg = Arg::from_usage("-s, --some 'something'");
+ build_app().arg(&arg)
+ });
+}
+
+#[bench]
+fn add_pos(b: &mut Bencher) {
+ fn build_app() -> App<'static, 'static> {
+ App::new("claptests")
+ }
+
+ b.iter(|| build_app().arg(Arg::with_name("some")));
+}
+
+#[bench]
+fn add_pos_ref(b: &mut Bencher) {
+ fn build_app() -> App<'static, 'static> {
+ App::new("claptests")
+ }
+
+ b.iter(|| {
+ let arg = Arg::with_name("some");
+ build_app().arg(&arg)
+ });
+}
+
#[bench]
fn parse_clean(b: &mut Bencher) {
b.iter(|| create_app!().get_matches_from(vec![""]));
diff --git a/src/app/help.rs b/src/app/help.rs
index 848671a8..dab618e9 100644
--- a/src/app/help.rs
+++ b/src/app/help.rs
@@ -11,6 +11,7 @@ use app::parser::Parser;
use args::{AnyArg, ArgSettings, DispOrder};
use errors::{Error, Result as ClapResult};
use fmt::{Format, Colorizer};
+use app::usage;
// Third Party
use unicode_width::UnicodeWidthStr;
@@ -195,9 +196,7 @@ impl<'a> Help<'a> {
if arg.longest_filter() {
self.longest = cmp::max(self.longest, arg.to_string().len());
}
- if !arg.is_set(ArgSettings::Hidden) {
- arg_v.push(arg)
- }
+ arg_v.push(arg)
}
let mut first = true;
for arg in arg_v {
@@ -541,7 +540,7 @@ impl<'a> Help<'a> {
pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
debugln!("Help::write_all_args;");
let flags = parser.has_flags();
- let pos = parser.has_positionals();
+ let pos = parser.positionals().filter(|arg| !arg.is_set(ArgSettings::Hidden)).count() > 0;
let opts = parser.has_opts();
let subcmds = parser.has_subcommands();
@@ -684,7 +683,7 @@ impl<'a> Help<'a> {
try!(write!(self.writer,
"\n{}{}\n\n",
TAB,
- parser.create_usage_no_title(&[])));
+ usage::create_help_usage(parser, true)));
let flags = parser.has_flags();
let pos = parser.has_positionals();
@@ -881,7 +880,7 @@ impl<'a> Help<'a> {
parser.meta.about.unwrap_or("unknown about")));
}
b"usage" => {
- try!(write!(self.writer, "{}", parser.create_usage_no_title(&[])));
+ try!(write!(self.writer, "{}", usage::create_help_usage(parser, true)));
}
b"all-args" => {
try!(self.write_all_args(&parser));
diff --git a/src/app/macros.rs b/src/app/macros.rs
index 7224fa42..41af48c5 100644
--- a/src/app/macros.rs
+++ b/src/app/macros.rs
@@ -148,7 +148,12 @@ macro_rules! parse_positional {
$matcher.inc_occurrence_of($p.b.name);
let _ = $_self.groups_for_arg($p.b.name)
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
- arg_post_processing!($_self, $p, $matcher);
+ 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/mod.rs b/src/app/mod.rs
index 5c25dc89..9fa181ee 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -4,6 +4,8 @@ mod macros;
pub mod parser;
mod meta;
mod help;
+mod validator;
+mod usage;
// Std
use std::env;
@@ -405,7 +407,7 @@ impl<'a, 'b> App<'a, 'b> {
/// # ;
/// ```
pub fn help_message>(mut self, s: S) -> Self {
- self.p.help_message(s.into());
+ self.p.help_message = Some(s.into());
self
}
@@ -423,7 +425,7 @@ impl<'a, 'b> App<'a, 'b> {
/// # ;
/// ```
pub fn version_message>(mut self, s: S) -> Self {
- self.p.version_message(s.into());
+ self.p.version_message = Some(s.into());
self
}
diff --git a/src/app/parser.rs b/src/app/parser.rs
index 1294df47..751ec189 100644
--- a/src/app/parser.rs
+++ b/src/app/parser.rs
@@ -1,5 +1,4 @@
// Std
-use std::collections::{BTreeMap, VecDeque};
use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use std::fs::File;
@@ -21,17 +20,18 @@ use app::App;
use app::help::Help;
use app::meta::AppMeta;
use app::settings::AppFlags;
-use args::{AnyArg, ArgMatcher, Base, Switched, Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder,
- MatchedArg};
+use args::{AnyArg, ArgMatcher, Base, Switched, Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder};
use args::settings::ArgSettings;
use completions::ComplGen;
use errors::{Error, ErrorKind};
use errors::Result as ClapResult;
-use fmt::{Colorizer, ColorWhen};
+use fmt::ColorWhen;
use osstringext::OsStrExt2;
use completions::Shell;
use suggestions;
use app::settings::AppSettings as AS;
+use app::validator::Validator;
+use app::usage;
#[allow(missing_debug_implementations)]
#[doc(hidden)]
@@ -46,16 +46,17 @@ pub struct Parser<'a, 'b>
pub opts: Vec>,
pub positionals: VecMap>,
pub subcommands: Vec>,
- groups: Vec>,
+ pub groups: Vec>,
pub global_args: Vec>,
- required: Vec<&'a str>,
- r_ifs: Vec<(&'a str, &'b str, &'a str)>,
- blacklist: Vec<&'b str>,
- overrides: Vec<&'b str>,
+ 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>,
help_short: Option,
version_short: Option,
- help_message: Option<&'a str>,
- version_message: Option<&'a str>,
+ cache: Option<&'a str>,
+ pub help_message: Option<&'a str>,
+ pub version_message: Option<&'a str>,
}
impl<'a, 'b> Parser<'a, 'b>
@@ -81,14 +82,6 @@ impl<'a, 'b> Parser<'a, 'b>
self.version_short = Some(c);
}
- pub fn help_message(&mut self, s: &'a str) {
- self.help_message = Some(s);
- }
-
- pub fn version_message(&mut self, s: &'a str) {
- self.version_message = Some(s);
- }
-
pub fn gen_completions_to(&mut self, for_shell: Shell, buf: &mut W) {
if !self.is_set(AS::Propogated) {
self.propogate_help_version();
@@ -105,7 +98,11 @@ impl<'a, 'b> Parser<'a, 'b>
use std::error::Error;
let out_dir = PathBuf::from(od);
- let name = &*self.meta.bin_name.as_ref().unwrap().clone();
+ let name = &*self.meta
+ .bin_name
+ .as_ref()
+ .unwrap()
+ .clone();
let file_name = match for_shell {
Shell::Bash => format!("{}.bash-completion", name),
Shell::Fish => format!("{}.fish", name),
@@ -121,33 +118,44 @@ impl<'a, 'b> Parser<'a, 'b>
}
#[inline]
- fn debug_asserts(&self, a: &Arg) {
- debug_assert!(!arg_names!(self).any(|name| name == a.b.name),
- format!("Non-unique argument name: {} is already in use", a.b.name));
+ fn debug_asserts(&self, a: &Arg) -> bool {
+ assert!(!arg_names!(self).any(|name| name == a.b.name),
+ format!("Non-unique argument name: {} is already in use", a.b.name));
if let Some(l) = a.s.long {
- debug_assert!(!self.contains_long(l),
- format!("Argument long must be unique\n\n\t--{} is already in use",
- l));
+ assert!(!self.contains_long(l),
+ "Argument long must be unique\n\n\t--{} is already in use",
+ l);
}
if let Some(s) = a.s.short {
- debug_assert!(!self.contains_short(s),
- format!("Argument short must be unique\n\n\t-{} is already in use",
- s));
+ assert!(!self.contains_short(s),
+ "Argument short must be unique\n\n\t-{} is already in use",
+ s);
}
let i = if a.index.is_none() {
(self.positionals.len() + 1)
} else {
a.index.unwrap() as usize
};
- debug_assert!(!self.positionals.contains_key(i),
- format!("Argument \"{}\" has the same index as another positional \
+ assert!(!self.positionals.contains_key(i),
+ "Argument \"{}\" has the same index as another positional \
argument\n\n\tPerhaps try .multiple(true) to allow one positional argument \
to take multiple values",
- a.b.name));
- debug_assert!(!(a.is_set(ArgSettings::Required) && a.is_set(ArgSettings::Global)),
- format!("Global arguments cannot be required.\n\n\t'{}' is marked as \
+ a.b.name);
+ assert!(!(a.is_set(ArgSettings::Required) && a.is_set(ArgSettings::Global)),
+ "Global arguments cannot be required.\n\n\t'{}' is marked as \
global and required",
- a.b.name));
+ a.b.name);
+ if a.b.is_set(ArgSettings::Last) {
+ assert!(!self.positionals.values().any(|p| p.b.is_set(ArgSettings::Last)),
+ "Only one positional argument may have last(true) set. Found two.");
+ assert!(a.s.long.is_none(),
+ "Flags or Options may not have last(true) set. {} has both a long and last(true) set.",
+ a.b.name);
+ assert!(a.s.short.is_none(),
+ "Flags or Options may not have last(true) set. {} has both a short and last(true) set.",
+ a.b.name);
+ }
+ true
}
#[inline]
@@ -190,16 +198,27 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
+ #[inline]
+ fn implied_settings(&mut self, a: &Arg<'a, 'b>) {
+ if a.is_set(ArgSettings::Last) {
+ // if an arg has `Last` set, we need to imply DontCollapseArgsInUsage so that args
+ // in the usage string don't get confused or left out.
+ self.set(AS::DontCollapseArgsInUsage);
+ self.set(AS::ContainsLast);
+ }
+ }
+
// actually adds the arguments
pub fn add_arg(&mut self, a: Arg<'a, 'b>) {
// if it's global we have to clone anyways
if a.is_set(ArgSettings::Global) {
return self.add_arg_ref(&a);
}
- self.debug_asserts(&a);
+ debug_assert!(self.debug_asserts(&a));
self.add_conditional_reqs(&a);
self.add_arg_groups(&a);
self.add_reqs(&a);
+ self.implied_settings(&a);
if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) {
let i = if a.index.is_none() {
(self.positionals.len() + 1)
@@ -219,10 +238,11 @@ impl<'a, 'b> Parser<'a, 'b>
}
// actually adds the arguments but from a borrow (which means we have to do some clonine)
pub fn add_arg_ref(&mut self, a: &Arg<'a, 'b>) {
- self.debug_asserts(&a);
- self.add_conditional_reqs(&a);
- self.add_arg_groups(&a);
- self.add_reqs(&a);
+ debug_assert!(self.debug_asserts(&a));
+ self.add_conditional_reqs(a);
+ self.add_arg_groups(a);
+ self.add_reqs(a);
+ self.implied_settings(&a);
if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) {
let i = if a.index.is_none() {
(self.positionals.len() + 1)
@@ -256,8 +276,10 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
if self.groups.iter().any(|g| g.name == group.name) {
- let grp =
- self.groups.iter_mut().find(|g| g.name == group.name).expect(INTERNAL_ERROR_MSG);
+ let grp = self.groups
+ .iter_mut()
+ .find(|g| g.name == group.name)
+ .expect(INTERNAL_ERROR_MSG);
grp.args.extend_from_slice(&group.args);
grp.requires = group.requires.clone();
grp.conflicts = group.conflicts.clone();
@@ -297,7 +319,11 @@ impl<'a, 'b> Parser<'a, 'b>
if vsc {
sc.p.set(AS::DisableVersion);
}
- if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() {
+ if gv &&
+ sc.p
+ .meta
+ .version
+ .is_none() && self.meta.version.is_some() {
sc.p.set(AS::GlobalVersion);
sc.p.meta.version = Some(self.meta.version.unwrap());
}
@@ -313,21 +339,21 @@ impl<'a, 'b> Parser<'a, 'b>
if self.is_set(AS::DeriveDisplayOrder) {
let unified = self.is_set(AS::UnifiedHelpMessage);
for (i, o) in self.opts
- .iter_mut()
- .enumerate()
- .filter(|&(_, ref o)| o.s.disp_ord == 999) {
+ .iter_mut()
+ .enumerate()
+ .filter(|&(_, ref o)| o.s.disp_ord == 999) {
o.s.disp_ord = if unified { o.s.unified_ord } else { i };
}
for (i, f) in self.flags
- .iter_mut()
- .enumerate()
- .filter(|&(_, ref f)| f.s.disp_ord == 999) {
+ .iter_mut()
+ .enumerate()
+ .filter(|&(_, ref f)| f.s.disp_ord == 999) {
f.s.disp_ord = if unified { f.s.unified_ord } else { i };
}
for (i, sc) in &mut self.subcommands
- .iter_mut()
- .enumerate()
- .filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) {
+ .iter_mut()
+ .enumerate()
+ .filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) {
sc.p.meta.disp_ord = i;
}
}
@@ -339,199 +365,9 @@ impl<'a, 'b> Parser<'a, 'b>
pub fn required(&self) -> Iter<&str> { self.required.iter() }
#[cfg_attr(feature = "lints", allow(needless_borrow))]
- pub fn get_required_from(&self,
- reqs: &[&'a str],
- matcher: Option<&ArgMatcher<'a>>,
- extra: Option<&str>)
- -> VecDeque {
- debugln!("Parser::get_required_from: reqs={:?}, extra={:?}",
- reqs,
- extra);
- let mut desc_reqs: Vec<&str> = vec![];
- desc_reqs.extend(extra);
- let mut new_reqs: Vec<&str> = vec![];
- macro_rules! get_requires {
- (@group $a: ident, $v:ident, $p:ident) => {{
- if let Some(rl) = self.groups.iter().filter(|g| g.requires.is_some()).find(|g| &g.name == $a).map(|g| g.requires.as_ref().unwrap()) {
- for r in rl {
- if !$p.contains(&r) {
- debugln!("Parser::get_required_from:iter:{}: adding group req={:?}", $a, r);
- $v.push(r);
- }
- }
- }
- }};
- ($a:ident, $what:ident, $how:ident, $v:ident, $p:ident) => {{
- if let Some(rl) = self.$what.$how().filter(|a| a.b.requires.is_some()).find(|arg| &arg.b.name == $a).map(|a| a.b.requires.as_ref().unwrap()) {
- for &(_, r) in rl.iter() {
- if !$p.contains(&r) {
- debugln!("Parser::get_required_from:iter:{}: adding arg req={:?}",$a, r);
- $v.push(r);
- }
- }
- }
- }};
- }
- // initialize new_reqs
- for a in reqs {
- get_requires!(a, flags, iter, new_reqs, reqs);
- get_requires!(a, opts, iter, new_reqs, reqs);
- get_requires!(a, positionals, values, new_reqs, reqs);
- get_requires!(@group a, new_reqs, reqs);
- }
- desc_reqs.extend_from_slice(&*new_reqs);
- debugln!("Parser::get_required_from: after init desc_reqs={:?}",
- desc_reqs);
- loop {
- let mut tmp = vec![];
- for a in &new_reqs {
- get_requires!(a, flags, iter, tmp, desc_reqs);
- get_requires!(a, opts, iter, tmp, desc_reqs);
- get_requires!(a, positionals, values, tmp, desc_reqs);
- get_requires!(@group a, tmp, desc_reqs);
- }
- if tmp.is_empty() {
- debugln!("Parser::get_required_from: no more children");
- break;
- } else {
- debugln!("Parser::get_required_from: after iter tmp={:?}", tmp);
- debugln!("Parser::get_required_from: after iter new_reqs={:?}",
- new_reqs);
- desc_reqs.extend_from_slice(&*new_reqs);
- new_reqs.clear();
- new_reqs.extend_from_slice(&*tmp);
- debugln!("Parser::get_required_from: after iter desc_reqs={:?}",
- desc_reqs);
- }
- }
- desc_reqs.extend_from_slice(reqs);
- desc_reqs.sort();
- desc_reqs.dedup();
- debugln!("Parser::get_required_from: final desc_reqs={:?}", desc_reqs);
- let mut ret_val = VecDeque::new();
- let args_in_groups = self.groups
- .iter()
- .filter(|gn| desc_reqs.contains(&gn.name))
- .flat_map(|g| self.arg_names_in_group(&g.name))
- .collect::>();
-
- let pmap = if let Some(ref m) = matcher {
- desc_reqs.iter()
- .filter(|a| self.positionals.values().any(|p| &&p.b.name == a))
- .filter(|&p| !m.contains(p))
- .filter_map(|p| self.positionals.values().find(|x| &x.b.name == p))
- .filter(|p| !args_in_groups.contains(&p.b.name))
- .map(|p| (p.index, p))
- .collect::>() // sort by index
- } else {
- desc_reqs.iter()
- .filter(|a| self.positionals.values().any(|p| &&p.b.name == a))
- .filter_map(|p| self.positionals.values().find(|x| &x.b.name == p))
- .filter(|p| !args_in_groups.contains(&p.b.name))
- .map(|p| (p.index, p))
- .collect::>() // sort by index
- };
- debugln!("Parser::get_required_from: args_in_groups={:?}",
- args_in_groups);
- for &p in pmap.values() {
- let s = p.to_string();
- if args_in_groups.is_empty() || !args_in_groups.contains(&&*s) {
- ret_val.push_back(s);
- }
- }
- for a in desc_reqs.iter()
- .filter(|name| !self.positionals.values().any(|p| &&p.b.name == name))
- .filter(|name| !self.groups.iter().any(|g| &&g.name == name))
- .filter(|name| !args_in_groups.contains(name))
- .filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name))) {
- debugln!("Parser::get_required_from:iter:{}:", a);
- let arg = find_by_name!(self, a, flags, iter)
- .map(|f| f.to_string())
- .unwrap_or_else(|| {
- find_by_name!(self, a, opts, iter)
- .map(|o| o.to_string())
- .expect(INTERNAL_ERROR_MSG)
- });
- ret_val.push_back(arg);
- }
- let mut g_vec = vec![];
- for g in desc_reqs.iter().filter(|n| self.groups.iter().any(|g| &&g.name == n)) {
- let g_string = self.args_in_group(g)
- .join("|");
- g_vec.push(format!("<{}>", &g_string[..g_string.len()]));
- }
- g_vec.sort();
- g_vec.dedup();
- for g in g_vec {
- ret_val.push_back(g);
- }
-
- ret_val
- }
-
- // Gets the `[ARGS]` tag for the usage string
- pub fn get_args_tag(&self) -> Option {
- debugln!("Parser::get_args_tag;");
- let mut count = 0;
- 'outer: for p in self.positionals.values().filter(|p| !p.is_set(ArgSettings::Required)) {
- debugln!("Parser::get_args_tag:iter:{}:", p.b.name);
- if let Some(g_vec) = self.groups_for_arg(p.b.name) {
- for grp_s in &g_vec {
- debugln!("Parser::get_args_tag:iter:{}:iter:{};", p.b.name, grp_s);
- // if it's part of a required group we don't want to count it
- if self.groups.iter().any(|g| g.required && (&g.name == grp_s)) {
- continue 'outer;
- }
- }
- }
- count += 1;
- debugln!("Parser::get_args_tag:iter: {} Args not required", count);
- }
- if !self.is_set(AS::DontCollapseArgsInUsage) && (count > 1 || self.positionals.len() > 1) {
- return None; // [ARGS]
- } else if count == 1 {
- let p = self.positionals
- .values()
- .find(|p| !p.is_set(ArgSettings::Required))
- .expect(INTERNAL_ERROR_MSG);
- return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()));
- } else if self.is_set(AS::DontCollapseArgsInUsage) && !self.positionals.is_empty() {
- return Some(self.positionals
- .values()
- .filter(|p| !p.is_set(ArgSettings::Required))
- .map(|p| format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()))
- .collect::>()
- .join(""));
- }
- Some("".into())
- }
-
- // Determines if we need the `[FLAGS]` tag in the usage string
- pub fn needs_flags_tag(&self) -> bool {
- debugln!("Parser::needs_flags_tag;");
- 'outer: for f in &self.flags {
- debugln!("Parser::needs_flags_tag:iter: f={};", f.b.name);
- if let Some(l) = f.s.long {
- if l == "help" || l == "version" {
- // Don't print `[FLAGS]` just for help or version
- continue;
- }
- }
- if let Some(g_vec) = self.groups_for_arg(f.b.name) {
- for grp_s in &g_vec {
- debugln!("Parser::needs_flags_tag:iter:iter: grp_s={};", grp_s);
- if self.groups.iter().any(|g| &g.name == grp_s && g.required) {
- debug!("Parser::needs_flags_tag:iter:iter: Group is required");
- continue 'outer;
- }
- }
- }
- debugln!("Parser::needs_flags_tag:iter: [FLAGS] required");
- return true;
- }
-
- debugln!("Parser::needs_flags_tag: [FLAGS] not required");
- false
+ #[inline]
+ pub fn has_args(&self) -> bool {
+ !(self.flags.is_empty() && self.opts.is_empty() && self.positionals.is_empty())
}
#[inline]
@@ -546,6 +382,38 @@ impl<'a, 'b> Parser<'a, 'b>
#[inline]
pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() }
+ #[inline]
+ pub fn has_visible_opts(&self) -> bool {
+ if self.opts.is_empty() {
+ return false;
+ }
+ self.opts.iter().any(|o| !o.is_set(ArgSettings::Hidden))
+ }
+
+ #[inline]
+ pub fn has_visible_flags(&self) -> bool {
+ if self.flags.is_empty() {
+ return false;
+ }
+ self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden))
+ }
+
+ #[inline]
+ pub fn has_visible_positionals(&self) -> bool {
+ if self.positionals.is_empty() {
+ return false;
+ }
+ self.positionals.values().any(|p| !p.is_set(ArgSettings::Hidden))
+ }
+
+ #[inline]
+ pub fn has_visible_subcommands(&self) -> bool {
+ if self.subcommands.is_empty() {
+ return false;
+ }
+ self.subcommands.iter().any(|s| !s.p.is_set(AS::Hidden))
+ }
+
#[inline]
pub fn is_set(&self, s: AS) -> bool { self.settings.is_set(s) }
@@ -556,74 +424,83 @@ impl<'a, 'b> Parser<'a, 'b>
pub fn unset(&mut self, s: AS) { self.settings.unset(s) }
#[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))]
- pub fn verify_positionals(&mut self) {
+ pub fn verify_positionals(&mut self) -> bool {
// Because you must wait until all arguments have been supplied, this is the first chance
// to make assertions on positional argument indexes
//
// Firt we verify that the index highest supplied index, is equal to the number of
// positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
// but no 2)
- if let Some((idx, p)) = self.positionals.iter().rev().next() {
- debug_assert!(!(idx != self.positionals.len()),
- format!("Found positional argument \"{}\" who's index is {} but there are \
- only {} positional arguments defined",
- p.b.name,
- idx,
- self.positionals.len()));
+ if let Some((idx, p)) = self.positionals
+ .iter()
+ .rev()
+ .next() {
+ assert!(!(idx != self.positionals.len()),
+ "Found positional argument \"{}\" who's index is {} but there \
+ are only {} positional arguments defined",
+ p.b.name,
+ idx,
+ self.positionals.len());
}
// Next we verify that only the highest index has a .multiple(true) (if any)
- if
- self.positionals
- .values()
- .any(|a| {
- a.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len())
- }) {
+ if self.positionals.values().any(|a| {
+ a.b.is_set(ArgSettings::Multiple) &&
+ (a.index as usize != self.positionals.len())
+ }) {
+ let mut it = self.positionals.values().rev();
+ let last = it.next().unwrap();
+ let second_to_last = it.next().unwrap();
+ // Either the final positional is required
+ // Or the second to last has a terminator or .last(true) set
+ let ok = last.is_set(ArgSettings::Required) ||
+ (second_to_last.v.terminator.is_some() ||
+ second_to_last.b.is_set(ArgSettings::Last));
+ assert!(ok,
+ "When using a positional argument with .multiple(true) that is *not the \
+ last* positional argument, the last positional argument (i.e the one \
+ with the highest index) *must* have .required(true) or .last(true) set.");
+ let num = self.positionals.len() - 1;
+ let ok = self.positionals
+ .get(num)
+ .unwrap()
+ .is_set(ArgSettings::Multiple);
+ assert!(ok,
+ "Only the last positional argument, or second to last positional \
+ argument may be set to .multiple(true)");
- debug_assert!({
- let mut it = self.positionals.values().rev();
- // Either the final positional is required
- it.next().unwrap().is_set(ArgSettings::Required)
- // Or the second to last has a terminator set
- || it.next().unwrap().v.terminator.is_some()
- },
- "When using a positional argument with .multiple(true) that is *not the last* \
- positional argument, the last positional argument (i.e the one with the highest \
- index) *must* have .required(true) set.");
-
- debug_assert!({
- let num = self.positionals.len() - 1;
- self.positionals.get(num).unwrap().is_set(ArgSettings::Multiple)
- },
- "Only the last positional argument, or second to last positional argument may be set to .multiple(true)");
-
- self.set(AS::LowIndexMultiplePositional);
+ self.settings.set(AS::LowIndexMultiplePositional);
}
- debug_assert!(self.positionals
- .values()
- .filter(|p| {
- p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none()
- })
- .map(|_| 1)
- .sum::() <= 1,
- "Only one positional argument with .multiple(true) set is allowed per command");
+ let ok = self.positionals
+ .values()
+ .filter(|p| p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none())
+ .map(|_| 1)
+ .sum::() <= 1;
+ assert!(ok,
+ "Only one positional argument with .multiple(true) set is allowed per \
+ command");
- // If it's required we also need to ensure all previous positionals are
- // required too
if self.is_set(AS::AllowMissingPositional) {
+ // Check that if a required positional argument is found, all positions with a lower
+ // index are also required.
let mut found = false;
let mut foundx2 = false;
for p in self.positionals.values().rev() {
if foundx2 && !p.b.settings.is_set(ArgSettings::Required) {
- // [arg1] is Ok
- // [arg1] Is not
- debug_assert!(p.b.settings.is_set(ArgSettings::Required),
- "Found positional argument which is not required with a lower index \
- than a required positional argument by two or more: {:?} index {}",
- p.b.name,
- p.index);
- } else if p.b.settings.is_set(ArgSettings::Required) {
+ assert!(p.b.is_set(ArgSettings::Required),
+ "Found positional argument which is not required with a lower \
+ index than a required positional argument by two or more: {:?} \
+ index {}",
+ p.b.name,
+ p.index);
+ } else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) {
+ // Args that .last(true) don't count since they can be required and have
+ // positionals with a lower index that aren't required
+ // Imagine: prog [opt1] --
+ // Both of these are valid invocations:
+ // $ prog r1 -- r2
+ // $ prog r1 o1 -- r2
if found {
foundx2 = true;
continue;
@@ -635,20 +512,38 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
} else {
+ // Check that if a required positional argument is found, all positions with a lower
+ // index are also required
let mut found = false;
for p in self.positionals.values().rev() {
if found {
- debug_assert!(p.b.settings.is_set(ArgSettings::Required),
- "Found positional argument which is not required with a lower index \
- than a required positional argument: {:?} index {}",
- p.b.name,
- p.index);
- } else if p.b.settings.is_set(ArgSettings::Required) {
+ assert!(p.b.is_set(ArgSettings::Required),
+ "Found positional argument which is not required with a lower \
+ index than a required positional argument: {:?} index {}",
+ p.b.name,
+ p.index);
+ } else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) {
+ // Args that .last(true) don't count since they can be required and have
+ // positionals with a lower index that aren't required
+ // Imagine: prog [opt1] --
+ // Both of these are valid invocations:
+ // $ prog r1 -- r2
+ // $ prog r1 o1 -- r2
found = true;
continue;
}
}
}
+ if self.positionals.values().any(|p| {
+ p.b.is_set(ArgSettings::Last) &&
+ p.b.is_set(ArgSettings::Required)
+ }) && self.has_subcommands() &&
+ !self.is_set(AS::SubcommandsNegateReqs) {
+ panic!("Having a required positional argument with .last(true) set *and* child \
+ subcommands without setting SubcommandsNegateReqs isn't compatible.");
+ }
+
+ true
}
pub fn propogate_globals(&mut self) {
@@ -665,25 +560,53 @@ impl<'a, 'b> Parser<'a, 'b>
}
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
- #[inline]
- fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
+ fn possible_subcommand(&self, arg_os: &OsStr) -> (bool, Option<&str>) {
debugln!("Parser::possible_subcommand: arg={:?}", arg_os);
- if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) {
- return false;
+ fn starts(h: &str, n: &OsStr) -> bool {
+ #[cfg(not(target_os = "windows"))]
+ use std::os::unix::ffi::OsStrExt;
+ #[cfg(target_os = "windows")]
+ use osstringext::OsStrExt3;
+
+ let n_bytes = n.as_bytes();
+ let h_bytes = OsStr::new(h).as_bytes();
+
+ h_bytes.starts_with(n_bytes)
}
- self.subcommands
- .iter()
- .any(|s| {
- &s.p.meta.name[..] == &*arg_os ||
- (s.p.meta.aliases.is_some() &&
- s.p
- .meta
- .aliases
- .as_ref()
- .unwrap()
- .iter()
- .any(|&(a, _)| a == &*arg_os))
- })
+
+ if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) {
+ return (false, None);
+ }
+ if !self.is_set(AS::InferSubcommands) {
+ if let Some(sc) = find_subcmd!(self, arg_os) {
+ return (true, Some(&sc.p.meta.name));
+ }
+ } else {
+ let v = self.subcommands
+ .iter()
+ .filter(|s| {
+ starts(&s.p.meta.name[..], &*arg_os) ||
+ (s.p
+ .meta
+ .aliases
+ .is_some() &&
+ s.p
+ .meta
+ .aliases
+ .as_ref()
+ .unwrap()
+ .iter()
+ .filter(|&&(a, _)| starts(a, &*arg_os))
+ .count() == 1)
+ })
+ .map(|sc| &sc.p.meta.name)
+ .collect::>();
+
+ if v.len() == 1 {
+ return (true, Some(v[0]));
+ }
+ }
+ (false, None)
}
fn parse_help_subcommand(&self, it: &mut I) -> ClapResult<()>
@@ -706,24 +629,21 @@ impl<'a, 'b> Parser<'a, 'b>
help_help = true;
}
if let Some(c) = sc.subcommands
- .iter()
- .find(|s| &*s.p.meta.name == cmd)
- .map(|sc| &sc.p) {
+ .iter()
+ .find(|s| &*s.p.meta.name == cmd)
+ .map(|sc| &sc.p) {
sc = c;
if i == cmds.len() - 1 {
break;
}
} else if let Some(c) = sc.subcommands
- .iter()
- .find(|s| if let Some(ref als) = s.p
- .meta
- .aliases {
- als.iter()
- .any(|&(a, _)| &a == &&*cmd.to_string_lossy())
- } else {
- false
- })
- .map(|sc| &sc.p) {
+ .iter()
+ .find(|s| if let Some(ref als) = s.p.meta.aliases {
+ als.iter().any(|&(a, _)| &a == &&*cmd.to_string_lossy())
+ } else {
+ false
+ })
+ .map(|sc| &sc.p) {
sc = c;
if i == cmds.len() - 1 {
break;
@@ -786,29 +706,28 @@ impl<'a, 'b> Parser<'a, 'b>
} else {
false
};
- debugln!("Parser::is_new_arg: Does arg allow leading hyphen...{:?}",
+ debugln!("Parser::is_new_arg: Arg::allow_leading_hyphen({:?})",
arg_allows_tac);
// Is this a new argument, or values from a previous option?
- debug!("Parser::is_new_arg: Starts new arg...");
let mut ret = if arg_os.starts_with(b"--") {
- sdebugln!("--");
+ debugln!("Parser::is_new_arg: -- found");
if arg_os.len_() == 2 {
return true; // We have to return true so override everything else
}
true
} else if arg_os.starts_with(b"-") {
- sdebugln!("-");
+ debugln!("Parser::is_new_arg: - found");
// a singe '-' by itself is a value and typically means "stdin" on unix systems
!(arg_os.len_() == 1)
} else {
- sdebugln!("Probable value");
+ debugln!("Parser::is_new_arg: probably value");
false
};
ret = ret && !arg_allows_tac;
- debugln!("Parser::is_new_arg: Starts new arg...{:?}", ret);
+ debugln!("Parser::is_new_arg: starts_new_arg={:?}", ret);
ret
}
@@ -823,7 +742,8 @@ impl<'a, 'b> Parser<'a, 'b>
{
debugln!("Parser::get_matches_with;");
// Verify all positional assertions pass
- self.verify_positionals();
+ debug_assert!(self.verify_positionals());
+ let has_args = self.has_args();
// Next we create the `--help` and `--version` arguments and add them if
// necessary
@@ -845,12 +765,19 @@ impl<'a, 'b> Parser<'a, 'b>
// Has the user already passed '--'? Meaning only positional args follow
if !self.is_set(AS::TrailingValues) {
// Does the arg match a subcommand name, or any of it's aliases (if defined)
- if self.possible_subcommand(&arg_os) {
- if &*arg_os == "help" && self.is_set(AS::NeedsSubcommandHelp) {
- try!(self.parse_help_subcommand(it));
+ {
+ let (is_match, sc_name) = self.possible_subcommand(&arg_os);
+ debugln!("Parser::get_matches_with: possible_sc={:?}, sc={:?}",
+ is_match,
+ sc_name);
+ if is_match {
+ let sc_name = sc_name.expect(INTERNAL_ERROR_MSG);
+ if sc_name == "help" && self.is_set(AS::NeedsSubcommandHelp) {
+ try!(self.parse_help_subcommand(it));
+ }
+ subcmd_name = Some(sc_name.to_owned());
+ break;
}
- subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
- break;
}
if !starts_new_arg {
@@ -868,6 +795,8 @@ impl<'a, 'b> Parser<'a, 'b>
if arg_os.len_() == 2 {
// The user has passed '--' which means only positional args follow no
// matter what they start with
+ debugln!("Parser::get_matches_with: found '--'");
+ debugln!("Parser::get_matches_with: setting TrailingVals=true");
self.set(AS::TrailingValues);
continue;
}
@@ -887,9 +816,9 @@ impl<'a, 'b> Parser<'a, 'b>
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(),
- "",
- &*self.create_current_usage(matcher, None),
- self.color()));
+ "",
+ &*usage::create_error_usage(self, matcher, None),
+ self.color()));
}
} else if !self.is_set(AS::AllowLeadingHyphen) {
continue;
@@ -900,11 +829,10 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
- if !self.is_set(AS::ArgsNegateSubcommands) {
+ 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(),
- self.subcommands
- .iter()
- .map(|s| &s.p.meta.name)) {
+ sc_names!(self)) {
return Err(Error::invalid_subcommand(arg_os.to_string_lossy()
.into_owned(),
cdate,
@@ -912,7 +840,8 @@ impl<'a, 'b> Parser<'a, 'b>
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
- &*self.create_current_usage(matcher,
+ &*usage::create_error_usage(self,
+ matcher,
None),
self.color()));
}
@@ -920,15 +849,13 @@ impl<'a, 'b> Parser<'a, 'b>
}
let low_index_mults = self.is_set(AS::LowIndexMultiplePositional) &&
- !self.positionals.is_empty() &&
pos_counter == (self.positionals.len() - 1);
let missing_pos = self.is_set(AS::AllowMissingPositional) &&
- !self.positionals.is_empty() &&
pos_counter == (self.positionals.len() - 1);
- debugln!("Parser::get_matches_with: Low index multiples...{:?}",
- low_index_mults);
debugln!("Parser::get_matches_with: Positional counter...{}",
pos_counter);
+ debugln!("Parser::get_matches_with: Low index multiples...{:?}",
+ low_index_mults);
if low_index_mults || missing_pos {
if let Some(na) = it.peek() {
let n = (*na).clone().into();
@@ -941,12 +868,11 @@ impl<'a, 'b> Parser<'a, 'b>
} else {
None
};
- if self.is_new_arg(&n, needs_val_of) || self.possible_subcommand(&n) ||
- suggestions::did_you_mean(&n.to_string_lossy(),
- self.subcommands
- .iter()
- .map(|s| &s.p.meta.name))
- .is_some() {
+ let sc_match = {
+ self.possible_subcommand(&n).0
+ };
+ if self.is_new_arg(&n, needs_val_of) || sc_match ||
+ suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self)).is_some() {
debugln!("Parser::get_matches_with: Bumping the positional counter...");
pos_counter += 1;
}
@@ -954,8 +880,21 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::get_matches_with: Bumping the positional counter...");
pos_counter += 1;
}
+ } else if self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues) {
+ // Came to -- and one postional has .last(true) set, so we go immediately
+ // to the last (highest index) positional
+ debugln!("Parser::get_matches_with: .last(true) and --, setting last pos");
+ pos_counter = self.positionals.len();
}
if let Some(p) = self.positionals.get(pos_counter) {
+ if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) {
+ return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
+ "",
+ &*usage::create_error_usage(self,
+ matcher,
+ None),
+ self.color()));
+ }
parse_positional!(self, p, arg_os, pos_counter, matcher);
self.settings.set(AS::ValidArgFound);
} else if self.is_set(AS::AllowExternalSubcommands) {
@@ -964,7 +903,8 @@ impl<'a, 'b> Parser<'a, 'b>
Some(s) => s.to_string(),
None => {
if !self.is_set(AS::StrictUtf8) {
- return Err(Error::invalid_utf8(&*self.create_current_usage(matcher,
+ return Err(Error::invalid_utf8(&*usage::create_error_usage(self,
+ matcher,
None),
self.color()));
}
@@ -977,112 +917,85 @@ impl<'a, 'b> Parser<'a, 'b>
while let Some(v) = it.next() {
let a = v.into();
if a.to_str().is_none() && !self.is_set(AS::StrictUtf8) {
- return Err(Error::invalid_utf8(&*self.create_current_usage(matcher, None),
+ return Err(Error::invalid_utf8(&*usage::create_error_usage(self,
+ matcher,
+ None),
self.color()));
}
sc_m.add_val_to("", &a);
}
matcher.subcommand(SubCommand {
- name: sc_name,
- matches: sc_m.into(),
- });
+ name: sc_name,
+ matches: sc_m.into(),
+ });
} else if !(self.is_set(AS::AllowLeadingHyphen) ||
- self.is_set(AS::AllowNegativeNumbers)) {
+ self.is_set(AS::AllowNegativeNumbers)) &&
+ !self.is_set(AS::InferSubcommands) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
- &*self.create_current_usage(matcher, None),
+ &*usage::create_error_usage(self,
+ matcher,
+ None),
self.color()));
- }
- }
-
- if let Some(ref pos_sc_name) = subcmd_name {
- // is this is a real subcommand, or an alias
- if self.subcommands.iter().any(|sc| &sc.p.meta.name == pos_sc_name) {
- try!(self.parse_subcommand(&*pos_sc_name, matcher, it));
- } else {
- let sc_name = &*self.subcommands
- .iter()
- .filter(|sc| sc.p.meta.aliases.is_some())
- .filter(|sc| {
- sc.p
- .meta
- .aliases
- .as_ref()
- .expect(INTERNAL_ERROR_MSG)
- .iter()
- .any(|&(a, _)| &a == &&*pos_sc_name)
- })
- .map(|sc| sc.p.meta.name.clone())
- .next()
- .expect(INTERNAL_ERROR_MSG);
- try!(self.parse_subcommand(sc_name, matcher, it));
- }
- } else if self.is_set(AS::SubcommandRequired) {
- let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name);
- return Err(Error::missing_subcommand(bn,
- &self.create_current_usage(matcher, None),
- self.color()));
- } else if self.is_set(AS::SubcommandRequiredElseHelp) {
- debugln!("parser::get_matches_with: SubcommandRequiredElseHelp=true");
- let mut out = vec![];
- try!(self.write_help_err(&mut out));
- return Err(Error {
- message: String::from_utf8_lossy(&*out).into_owned(),
- kind: ErrorKind::MissingArgumentOrSubcommand,
- info: None,
- });
- }
-
- self.validate(needs_val_of, subcmd_name, matcher)
- }
-
- fn validate(&mut self,
- needs_val_of: Option<&'a str>,
- subcmd_name: Option,
- matcher: &mut ArgMatcher<'a>)
- -> ClapResult<()> {
- debugln!("Parser::validate;");
- let mut reqs_validated = false;
- try!(self.add_defaults(matcher));
- if let Some(a) = needs_val_of {
- debugln!("Parser::validate: needs_val_of={:?}", a);
- if let Some(o) = find_by_name!(self, &a, opts, iter) {
- try!(self.validate_required(matcher));
- reqs_validated = true;
- let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
- v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
+ } else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
+ 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()));
} else {
- true
- };
- if should_err {
- return Err(Error::empty_value(o,
- &*self.create_current_usage(matcher, None),
- self.color()));
+ return Err(Error::unrecognized_subcommand(arg_os.to_string_lossy()
+ .into_owned(),
+ self.meta
+ .bin_name
+ .as_ref()
+ .unwrap_or(&self.meta.name),
+ self.color()));
}
}
}
- try!(self.validate_blacklist(matcher));
- if !(self.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated {
- try!(self.validate_required(matcher));
- }
- try!(self.validate_matched_args(matcher));
- matcher.usage(self.create_usage(&[]));
-
- if matcher.is_empty() && matcher.subcommand_name().is_none() &&
- self.is_set(AS::ArgRequiredElseHelp) {
+ if let Some(ref pos_sc_name) = subcmd_name {
+ let sc_name = {
+ find_subcmd!(self, pos_sc_name)
+ .expect(INTERNAL_ERROR_MSG)
+ .p
+ .meta
+ .name
+ .clone()
+ };
+ try!(self.parse_subcommand(&*sc_name, matcher, it));
+ } else if self.is_set(AS::SubcommandRequired) {
+ let bn = self.meta
+ .bin_name
+ .as_ref()
+ .unwrap_or(&self.meta.name);
+ return Err(Error::missing_subcommand(bn,
+ &usage::create_error_usage(self, matcher, None),
+ self.color()));
+ } else if self.is_set(AS::SubcommandRequiredElseHelp) {
+ debugln!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
let mut out = vec![];
try!(self.write_help_err(&mut out));
return Err(Error {
- message: String::from_utf8_lossy(&*out).into_owned(),
- kind: ErrorKind::MissingArgumentOrSubcommand,
- info: None,
- });
+ message: String::from_utf8_lossy(&*out).into_owned(),
+ kind: ErrorKind::MissingArgumentOrSubcommand,
+ info: None,
+ });
}
- Ok(())
+
+ Validator::new(self).validate(needs_val_of, subcmd_name, matcher)
}
+
fn propogate_help_version(&mut self) {
debugln!("Parser::propogate_help_version;");
self.create_help_and_version();
@@ -1095,7 +1008,10 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::build_bin_names;");
for sc in &mut self.subcommands {
debug!("Parser::build_bin_names:iter: bin_name set...");
- if sc.p.meta.bin_name.is_none() {
+ if sc.p
+ .meta
+ .bin_name
+ .is_none() {
sdebugln!("No");
let bin_name = format!("{}{}{}",
self.meta
@@ -1133,25 +1049,29 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::parse_subcommand;");
let mut mid_string = String::new();
if !self.is_set(AS::SubcommandsNegateReqs) {
- let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect();
+ let mut hs: Vec<&str> = self.required
+ .iter()
+ .map(|n| &**n)
+ .collect();
for k in matcher.arg_names() {
hs.push(k);
}
- let reqs = self.get_required_from(&hs, Some(matcher), None);
+ let reqs = usage::get_required_usage_from(self, &hs, Some(matcher), None, false);
for s in &reqs {
write!(&mut mid_string, " {}", s).expect(INTERNAL_ERROR_MSG);
}
}
mid_string.push_str(" ");
- if let Some(ref mut sc) = self.subcommands
- .iter_mut()
- .find(|s| &s.p.meta.name == &sc_name) {
+ if let Some(ref mut sc) = self.subcommands.iter_mut().find(|s| &s.p.meta.name == &sc_name) {
let mut sc_matcher = ArgMatcher::new();
// bin_name should be parent's bin_name + [] + the sc's name separated by
// a space
sc.p.meta.usage = Some(format!("{}{}{}",
- self.meta.bin_name.as_ref().unwrap_or(&String::new()),
+ self.meta
+ .bin_name
+ .as_ref()
+ .unwrap_or(&String::new()),
if self.meta.bin_name.is_some() {
&*mid_string
} else {
@@ -1174,15 +1094,17 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings);
try!(sc.p.get_matches_with(&mut sc_matcher, it));
matcher.subcommand(SubCommand {
- name: sc.p.meta.name.clone(),
- matches: sc_matcher.into(),
- });
+ name: sc.p
+ .meta
+ .name
+ .clone(),
+ matches: sc_matcher.into(),
+ });
}
Ok(())
}
-
- fn groups_for_arg(&self, name: &str) -> Option> {
+ pub fn groups_for_arg(&self, name: &str) -> Option> {
debugln!("Parser::groups_for_arg: name={}", name);
if self.groups.is_empty() {
@@ -1206,18 +1128,20 @@ impl<'a, 'b> Parser<'a, 'b>
Some(res)
}
- fn args_in_group(&self, group: &str) -> Vec {
+ pub fn args_in_group(&self, group: &str) -> Vec {
let mut g_vec = vec![];
let mut args = vec![];
- for n in &self.groups.iter().find(|g| g.name == group).expect(INTERNAL_ERROR_MSG).args {
+ for n in &self.groups
+ .iter()
+ .find(|g| g.name == group)
+ .expect(INTERNAL_ERROR_MSG)
+ .args {
if let Some(f) = self.flags.iter().find(|f| &f.b.name == n) {
args.push(f.to_string());
} else if let Some(f) = self.opts.iter().find(|o| &o.b.name == n) {
args.push(f.to_string());
- } else if let Some(p) = self.positionals
- .values()
- .find(|p| &p.b.name == n) {
+ } else if let Some(p) = self.positionals.values().find(|p| &p.b.name == n) {
args.push(p.b.name.to_owned());
} else {
g_vec.push(*n);
@@ -1231,22 +1155,25 @@ impl<'a, 'b> Parser<'a, 'b>
args.iter().map(ToOwned::to_owned).collect()
}
- fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> {
+ pub fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> {
let mut g_vec = vec![];
let mut args = vec![];
- for n in &self.groups.iter().find(|g| g.name == group).expect(INTERNAL_ERROR_MSG).args {
+ for n in &self.groups
+ .iter()
+ .find(|g| g.name == group)
+ .expect(INTERNAL_ERROR_MSG)
+ .args {
if self.groups.iter().any(|g| &g.name == &*n) {
- args.extend(self.arg_names_in_group(&*n));
+ args.extend(self.arg_names_in_group(n));
g_vec.push(*n);
} else {
- args.push(*n);
+ if !args.contains(n) {
+ args.push(*n);
+ }
}
}
- // TODO: faster way to sort/dedup?
- args.sort();
- args.dedup();
args.iter().map(|s| *s).collect()
}
@@ -1261,7 +1188,7 @@ impl<'a, 'b> Parser<'a, 'b>
let arg = FlagBuilder {
b: Base {
name: "hclap_help",
- help: Some(self.help_message.unwrap_or("Prints help information")),
+ help: self.help_message.or(Some("Prints help information")),
..Default::default()
},
s: Switched {
@@ -1281,7 +1208,7 @@ impl<'a, 'b> Parser<'a, 'b>
let arg = FlagBuilder {
b: Base {
name: "vclap_version",
- help: Some(self.version_message.unwrap_or("Prints version information")),
+ help: self.version_message.or(Some("Prints version information")),
..Default::default()
},
s: Switched {
@@ -1303,26 +1230,6 @@ impl<'a, 'b> Parser<'a, 'b>
// Retrieves the names of all args the user has supplied thus far, except required ones
// because those will be listed in self.required
- pub fn create_current_usage(&self, matcher: &'b ArgMatcher<'a>, extra: Option<&str>) -> String {
- let mut args: Vec<_> = matcher.arg_names()
- .iter()
- .filter(|n| {
- if let Some(o) = find_by_name!(self, *n, opts, iter) {
- !o.b.settings.is_set(ArgSettings::Required)
- } else if let Some(p) = find_by_name!(self, *n, positionals, values) {
- !p.b.settings.is_set(ArgSettings::Required)
- } else {
- true // flags can't be required, so they're always true
- }
- })
- .map(|&n| n)
- .collect();
- if let Some(r) = extra {
- args.push(r);
- }
- self.create_usage(&*args)
- }
-
fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> {
debugln!("Parser::check_for_help_and_version_str;");
debug!("Parser::check_for_help_and_version_str: Checking if --{} is help or version...",
@@ -1364,10 +1271,10 @@ impl<'a, 'b> Parser<'a, 'b>
let mut buf = vec![];
try!(Help::write_parser_help(&mut buf, self));
Err(Error {
- message: unsafe { String::from_utf8_unchecked(buf) },
- kind: ErrorKind::HelpDisplayed,
- info: None,
- })
+ message: unsafe { String::from_utf8_unchecked(buf) },
+ kind: ErrorKind::HelpDisplayed,
+ info: None,
+ })
}
fn _version(&self) -> ClapResult<()> {
@@ -1375,10 +1282,10 @@ impl<'a, 'b> Parser<'a, 'b>
let mut buf_w = BufWriter::new(out.lock());
try!(self.print_version(&mut buf_w));
Err(Error {
- message: String::new(),
- kind: ErrorKind::VersionDisplayed,
- info: None,
- })
+ message: String::new(),
+ kind: ErrorKind::VersionDisplayed,
+ info: None,
+ })
}
fn parse_long_arg(&mut self,
@@ -1404,7 +1311,10 @@ impl<'a, 'b> Parser<'a, 'b>
opt.to_string());
self.settings.set(AS::ValidArgFound);
let ret = try!(self.parse_opt(val, opt, val.is_some(), matcher));
- arg_post_processing!(self, opt, matcher);
+ if self.cache.map_or(true, |name| name != opt.b.name) {
+ arg_post_processing!(self, opt, matcher);
+ self.cache = Some(opt.b.name);
+ }
return Ok(ret);
} else if let Some(flag) = find_flag_by_long!(@os self, &arg) {
@@ -1418,7 +1328,10 @@ impl<'a, 'b> Parser<'a, 'b>
try!(self.parse_flag(flag, matcher));
// Handle conflicts, requirements, etc.
- arg_post_processing!(self, flag, matcher);
+ if self.cache.map_or(true, |name| name != flag.b.name) {
+ arg_post_processing!(self, flag, matcher);
+ self.cache = Some(flag.b.name);
+ }
return Ok(None);
} else if self.is_set(AS::AllowLeadingHyphen) {
@@ -1431,11 +1344,12 @@ impl<'a, 'b> Parser<'a, 'b>
self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher).map(|_| None)
}
+ #[cfg_attr(feature = "lints", allow(len_zero))]
fn parse_short_arg(&mut self,
matcher: &mut ArgMatcher<'a>,
full_arg: &OsStr)
-> ClapResult