diff --git a/.clog.toml b/.clog.toml index 301dd920e78e..5a62fa4badfe 100644 --- a/.clog.toml +++ b/.clog.toml @@ -11,4 +11,3 @@ Deprecations = ["depr"] Examples = ["examples"] "New Settings" = ["setting", "settings"] "API Additions" = ["add", "api"] -"New Sponsor" = ["sponsor"] diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2c6f7528d173..ba84eab478d1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -35,7 +35,7 @@ $ just run-test ### Linting Code -During the CI process `clap` runs against many different lints using [`clippy`](https://github.com/rust-lang-nursery/rust-clippy). In order to check if these lints pass on your own computer prior to submitting a PR you'll need a nightly compiler. +During the CI process `clap` runs against many different lints using [`clippy`](https://github.com/Manishearth/rust-clippy). In order to check if these lints pass on your own computer prior to submitting a PR you'll need a nightly compiler. In order to check the code for lints run either: diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5f94a2c11e5c..8367b541cd2d 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -10,9 +10,6 @@ Please use the following template to assist with creating an issue and to ensure * Can be found in Cargo.lock of your project (i.e. `grep clap Cargo.lock`) -### Bug or Feature Request Summary - - ### Expected Behavior Summary @@ -33,6 +30,7 @@ Compile clap with cargo features `"debug"` such as: [dependencies] clap = { version = "2", features = ["debug"] } ``` +The output may be very long, so feel free to link to a gist or attach a text file
Debug Output diff --git a/.gitignore b/.gitignore index 34253e908332..6a097d4693ac 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ Cargo.lock # Project files .vscode/* .idea/* +clap-rs.iml + +# Auxiliary files +test-results.test diff --git a/.travis.yml b/.travis.yml index 9eb40444e7af..53e566359c2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,15 @@ language: rust cache: cargo rust: - nightly + - nightly-2019-06-18 - beta - stable - - 1.31.0 + - 1.33.0 matrix: allow_failures: - rust: nightly + nightly-2019-06-18: + - script: cargo clippy before_script: - | pip install git+git://github.com/kbknapp/travis-cargo.git --user && @@ -21,21 +24,19 @@ before_script: fi script: - | - travis-cargo --only stable test -- --verbose --no-default-features && + travis-cargo test -- --verbose --no-default-features && travis-cargo --skip nightly test -- --verbose --features "yaml unstable" && travis-cargo --only nightly test -- --verbose --features "yaml unstable nightly" && - travis-cargo --only nightly bench -- --no-run + travis-cargo --only nightly bench addons: apt: packages: - - binutils-dev - libcurl4-openssl-dev - libelf-dev - libdw-dev - - libiberty-dev - cmake - gcc - - zlib1g-dev + - binutils-dev after_success: - | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && @@ -55,5 +56,4 @@ after_success: echo "Uploaded code coverage" env: global: - - TRAVIS_CARGO_NIGHTLY_FEATURE=lints - secure: JLBlgHY6OEmhJ8woewNJHmuBokTNUv7/WvLkJGV8xk0t6bXBwSU0jNloXwlH7FiQTc4TccX0PumPDD4MrMgxIAVFPmmmlQOCmdpYP4tqZJ8xo189E5zk8lKF5OyaVYCs5SMmFC3cxCsKjfwGIexNu3ck5Uhwe9jI0tqgkgM3URA= diff --git a/CHANGELOG.md b/CHANGELOG.md index d1fdd401af1d..9eb19112ed97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,160 +1,8 @@ - -## v2.33.0 (2019-04-06) - -#### New Sponsor - -* Stephen Oats is now a sponsor \o/ ([823457c0](https://github.com/kbknapp/clap-rs/commit/823457c0ef5e994ed7080cf62addbfe1aa3b1833)) -* **SPONSORS.md:** fixes Josh Triplett's info in the sponsor document ([24cb5740](https://github.com/kbknapp/clap-rs/commit/24cb574090a11159b48bba105d5ec2dfb0a20e4e)) - -#### Features - -* **Completions:** adds completion support for Elvish. ([e9d0562a](https://github.com/kbknapp/clap-rs/commit/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9)) -* There is a new setting to disable automatic building of `--help` and `-h` flags (`AppSettings::DisableAutoHelp`) - -#### Improvements - -* **arg_matches.rs:** add Debug implementations ([47192b7a](https://github.com/kbknapp/clap-rs/commit/47192b7a2d84ec716b81ae4af621e008a8762dc9)) -* **macros:** Support shorthand syntax for ArgGroups ([df9095e7](https://github.com/kbknapp/clap-rs/commit/df9095e75bb1e7896415251d0d4ffd8a0ebcd559)) - -#### Documentation - -* Refer to macOS rather than OSX. ([ab0d767f](https://github.com/kbknapp/clap-rs/commit/ab0d767f3a5a57e2bbb97d0183c2ef63c8c77a6c)) -* **README.md:** use https for all links ([96a7639a](https://github.com/kbknapp/clap-rs/commit/96a7639a36bcb184c3f45348986883115ef1ab3a)) - -#### Bug Fixes - -* add debug assertion for missing args in subcommand ArgGroup ([2699d9e5](https://github.com/kbknapp/clap-rs/commit/2699d9e51e7eadc258ba64c4e347c5d1fef61343)) -* Restore compat with Rust 1.21 ([6b263de1](https://github.com/kbknapp/clap-rs/commit/6b263de1d42ede692ec5ee55019ad2fc6386f92e)) -* Dont mention unused subcommands ([ef92e2b6](https://github.com/kbknapp/clap-rs/commit/ef92e2b639ed305bdade4741f60fa85cb0101c5a)) -* **OsValues:** Add `ExactSizeIterator` implementation ([356c69e5](https://github.com/kbknapp/clap-rs/commit/356c69e508fd25a9f0ea2d27bf80ae1d9a8d88f4)) -* **arg_enum!:** - * Fix comma position for valid values. ([1f1f9ff3](https://github.com/kbknapp/clap-rs/commit/1f1f9ff3fa38a43231ef8be9cfea89a32e53f518)) - * Invalid expansions of some trailing-comma patterns ([7023184f](https://github.com/kbknapp/clap-rs/commit/7023184fca04e852c270341548d6a16207d13862)) -* **completions:** improve correctness of completions when whitespace is involved ([5a08ff29](https://github.com/kbknapp/clap-rs/commit/5a08ff295b2aa6ce29420df6252a0e3ff4441bdc)) -* **help message:** Unconditionally uses long description for subcommands ([6acc8b6a](https://github.com/kbknapp/clap-rs/commit/6acc8b6a621a765cbf513450188000d943676a30), closes [#897](https://github.com/kbknapp/clap-rs/issues/897)) -* **macros:** fixes broken pattern which prevented calling multi-argument Arg methods ([9e7a352e](https://github.com/kbknapp/clap-rs/commit/9e7a352e13aaf8025d80f2bac5c47fb32528672b)) -* **parser:** Better interaction between AllowExternalSubcommands and SubcommandRequired ([9601c95a](https://github.com/kbknapp/clap-rs/commit/9601c95a03d2b82bf265c328b4769238f1b79002)) +## Unreleased #### Minimum Required Rust -* As of this release, `clap` requires `rustc 1.31.0` or greater. - - -## v2.32.0 (2018-06-26) - -#### Minimum Required Rust - -* As of this release, `clap` requires `rustc 1.21.0` or greater. - - -#### Features - -* **Completions:** adds completion support for Elvish. ([e9d0562a](https://github.com/kbknapp/clap-rs/commit/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9)) - -#### Improvements - -* **macros:** Support shorthand syntax for ArgGroups ([df9095e7](https://github.com/kbknapp/clap-rs/commit/df9095e75bb1e7896415251d0d4ffd8a0ebcd559)) - -#### Bug Fixes - -* **OsValues:** Add `ExactSizeIterator` implementation ([356c69e5](https://github.com/kbknapp/clap-rs/commit/356c69e508fd25a9f0ea2d27bf80ae1d9a8d88f4)) -* **arg_enum!:** Invalid expansions of some trailing-comma patterns ([7023184f](https://github.com/kbknapp/clap-rs/commit/7023184fca04e852c270341548d6a16207d13862)) -* **help message:** Unconditionally uses long description for subcommands ([6acc8b6a](https://github.com/kbknapp/clap-rs/commit/6acc8b6a621a765cbf513450188000d943676a30), closes [#897](https://github.com/kbknapp/clap-rs/issues/897)) - -#### Documentation - -* Refer to macOS rather than OSX. ([ab0d767f](https://github.com/kbknapp/clap-rs/commit/ab0d767f3a5a57e2bbb97d0183c2ef63c8c77a6c)) - - - - -### v2.31.2 (2018-03-19) - -#### Bug Fixes - -* **Fish Completions:** fixes a bug that only allowed a single completion in in Fish Shell ([e8774a8](https://github.com/kbknapp/clap-rs/pull/1214/commits/e8774a84ee4a319c888036e7c595ab46451d8e48), closes [#1212](https://github.com/kbknapp/clap-rs/issues/1212)) -* **AllowExternalSubcommands**: fixes a bug where external subcommands would be blocked by a similarly named subcommand (suggestions were getting in the way). ([a410e85](https://github.com/kbknapp/clap-rs/pull/1215/commits/a410e855bcd82b05f9efa73fa8b9774dc8842c6b)) - -#### Documentation - -* Fixes some typos in the `README.md` ([c8e685d7](https://github.com/kbknapp/clap-rs/commit/c8e685d76adee2a3cc06cac6952ffcf6f9548089)) - - -### v2.31.1 (2018-03-06) - - -#### Improvements - -* **AllowMissingPositional:** improves the ability of AllowMissingPositional to allow 'skipping' to the last positional arg with '--' ([df20e6e2](https://github.com/kbknapp/clap-rs/commit/df20e6e24b4e782be0b423b484b9798e3e2efe2f)) - - - -## v2.31.0 (2018-03-04) - - -#### Features - -* **Arg Indices:** adds the ability to query argument value indices ([f58d0576](https://github.com/kbknapp/clap-rs/commit/f58d05767ec8133c8eb2de117cb642b9ae29ccbc)) -* **Indices:** implements an Indices iterator ([1e67be44](https://github.com/kbknapp/clap-rs/commit/1e67be44f0ccf161cc84c4e6082382072e89c302)) -* **Raw Args** adds a convenience function to `Arg` that allows implying all of `Arg::last` `Arg::allow_hyphen_values` and `Arg::multiple(true)` ([66a78f29](https://github.com/kbknapp/clap-rs/commit/66a78f2972786f5fe7c07937a1ac23da2542afd2)) - -#### Documentation - -* Fix some typos and markdown issues. ([935ba0dd](https://github.com/kbknapp/clap-rs/commit/935ba0dd547a69c3f636c5486795012019408794)) -* **Arg Indices:** adds the documentation for the arg index querying methods ([50bc0047](https://github.com/kbknapp/clap-rs/commit/50bc00477afa64dc6cdc5de161d3de3ba1d105a7)) -* **CONTRIBUTING.md:** fix url to clippy upstream repo to point to https://github.com/rust-lang-nursery/rust-clippy instead of https://github.com/Manishearth/rust-clippy ([42407d7f](https://github.com/kbknapp/clap-rs/commit/42407d7f21d794103cda61f49d2615aae0a4bcd9)) -* **Values:** improves the docs example of the Values iterator ([74075d65](https://github.com/kbknapp/clap-rs/commit/74075d65e8db1ddb5e2a4558009a5729d749d1b6)) -* Updates readme to hint that the `wrap_help` feature is a thing ([fc7ab227](https://github.com/kbknapp/clap-rs/commit/66a78f2972786f5fe7c07937a1ac23da2542afd2)) - -### Improvements - -* Cargo.toml: use codegen-units = 1 in release and bench profiles ([19f425ea](https://github.com/kbknapp/clap-rs/commit/66a78f2972786f5fe7c07937a1ac23da2542afd2)) -* Adds WASM support (clap now compiles on WASM!) ([689949e5](https://github.com/kbknapp/clap-rs/commit/689949e57d390bb61bc69f3ed91f60a2105738d0)) -* Uses the short help tool-tip for PowerShell completion scripts ([ecda22ce](https://github.com/kbknapp/clap-rs/commit/ecda22ce7210ce56d7b2d1a5445dd1b8a2959656)) - - - -## v2.30.0 (2018-02-13) - -#### Bug Fixes - -* **YAML:** Adds a missing conversion from `Arg::last` when instantiating from a YAML file ([aab77c81a5](https://github.com/kbknapp/clap-rs/pull/1175/commits/aab77c81a519b045f95946ae0dd3e850f9b93070), closes [#1160](https://github.com/kbknapp/clap-rs/issues/1173)) - -#### Improvements - -* **Bash Completions:** instead of completing a generic option name, all bash completions fall back to file completions UNLESS `Arg::possible_values` was used ([872f02ae](https://github.com/kbknapp/clap-rs/commit/872f02aea900ffa376850a279eb164645e1234fa)) -* **Deps:** No longer needlessly compiles `ansi_term` on Windows since its not used ([b57ee946](https://github.com/kbknapp/clap-rs/commit/b57ee94609da3ddc897286cfba968f26ff961491), closes [#1155](https://github.com/kbknapp/clap-rs/issues/1155)) -* **Help Message:** changes the `[values: foo bar baz]` array to `[possible values: foo bar baz]` for consistency with the API ([414707e4e97](https://github.com/kbknapp/clap-rs/pull/1176/commits/414707e4e979d07bfe555247e5d130c546673708), closes [#1160](https://github.com/kbknapp/clap-rs/issues/1160)) - - - -### v2.29.4 (2018-02-06) - - -#### Bug Fixes - -* **Overrides Self:** fixes a bug where options with multiple values couldnt ever have multiple values ([d95907cf](https://github.com/kbknapp/clap-rs/commit/d95907cff6d011a901fe35fa00b0f4e18547a1fb)) - - - - -### v2.29.3 (2018-02-05) - - -#### Improvements - -* **Overrides:** clap now supports arguments which override with themselves ([6c7a0010](https://github.com/kbknapp/clap-rs/commit/6c7a001023ca1eac1cc6ffe6c936b4c4a2aa3c45), closes [#976](https://github.com/kbknapp/clap-rs/issues/976)) - -#### Bug Fixes - -* **Requirements:** fixes an issue where conflicting args would still show up as required ([e06cefac](https://github.com/kbknapp/clap-rs/commit/e06cefac97083838c0a4e1444dcad02a5c3f911e), closes [#1158](https://github.com/kbknapp/clap-rs/issues/1158)) -* Fixes a bug which disallows proper nesting of `--` ([73993fe](https://github.com/kbknapp/clap-rs/commit/73993fe30d135f682e763ec93dcb0814ed518011), closes [#1161](https://github.com/kbknapp/clap-rs/issues/1161)) - -#### New Settings - -* **AllArgsOverrideSelf:** adds a new convenience setting to allow all args to override themselves ([4670325d](https://github.com/kbknapp/clap-rs/commit/4670325d1bf0369addec2ae2bcb56f1be054c924)) - - +* As of this release, `clap` requires `rustc 1.33.0` or greater. ### v2.29.2 (2018-01-16) @@ -959,7 +807,7 @@ Minimum version of Rust is now v1.13.0 (Stable) #### Documentation -* **Help Wrapping:** removes the verbiage about using `'{n}'` to insert newlines in help text ([c5a2b352](https://github.com/kbknapp/clap-rs/commit/c5a2b352ca600f5b802290ad945731066cd53611)) +* **Help Wrapping:** removes the verbage about using `'{n}'` to insert newlines in help text ([c5a2b352](https://github.com/kbknapp/clap-rs/commit/c5a2b352ca600f5b802290ad945731066cd53611)) * **Value Delimiters:** updates the docs for the Arg::multiple method WRT value delimiters and default settings ([f9d17a06](https://github.com/kbknapp/clap-rs/commit/f9d17a060aa53f10d0a6e1a7eed5d989d1a59533)) * **appsettings:** Document AppSetting::DisableVersion ([94501965](https://github.com/kbknapp/clap-rs/commit/945019654d2ca67eb2b1d6014fdf80b84d528d30), closes [#589](https://github.com/kbknapp/clap-rs/issues/589)) @@ -987,7 +835,7 @@ Minimum version of Rust is now v1.13.0 (Stable) #### Documentation -* **Help Wrapping:** removes the verbiage about using `'{n}'` to insert newlines in help text ([c5a2b352](https://github.com/kbknapp/clap-rs/commit/c5a2b352ca600f5b802290ad945731066cd53611)) +* **Help Wrapping:** removes the verbage about using `'{n}'` to insert newlines in help text ([c5a2b352](https://github.com/kbknapp/clap-rs/commit/c5a2b352ca600f5b802290ad945731066cd53611)) #### Improvements @@ -2619,7 +2467,7 @@ Minimum version of Rust is now v1.13.0 (Stable) #### Features * **macros** - * add ability to get multiple typed values or exit ([0b87251f](https://github.com/kbknapp/clap-rs/commit/0b87251fc088234bee51c323c2b652d7254f7a59)) + * add ability to get mutliple typed values or exit ([0b87251f](https://github.com/kbknapp/clap-rs/commit/0b87251fc088234bee51c323c2b652d7254f7a59)) * add ability to get a typed multiple values ([e243fe38](https://github.com/kbknapp/clap-rs/commit/e243fe38ddbbf845a46c0b9baebaac3778c80927)) * add convenience macro to get a typed value or exit ([4b7cd3ea](https://github.com/kbknapp/clap-rs/commit/4b7cd3ea4947780d9daa39f3e1ddab53ad4c7fef)) * add convenience macro to get a typed value ([8752700f](https://github.com/kbknapp/clap-rs/commit/8752700fbb30e89ee68adbce24489ae9a24d33a9)) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f0fd77724435..8899cd36e96f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,89 +1,85 @@ the following is a list of contributors: -[kbknapp](https://github.com/kbknapp) |[homu](https://github.com/homu) |[Vinatorul](https://github.com/Vinatorul) |[tormol](https://github.com/tormol) |[willmurphyscode](https://github.com/willmurphyscode) |[little-dude](https://github.com/little-dude) | +[kbknapp](https://github.com/kbknapp) |[homu](https://github.com/homu) |[Vinatorul](https://github.com/Vinatorul) |[tormol](https://github.com/tormol) |[little-dude](https://github.com/little-dude) |[sru](https://github.com/sru) | :---: |:---: |:---: |:---: |:---: |:---: | -[kbknapp](https://github.com/kbknapp) |[homu](https://github.com/homu) |[Vinatorul](https://github.com/Vinatorul) |[tormol](https://github.com/tormol) |[willmurphyscode](https://github.com/willmurphyscode) |[little-dude](https://github.com/little-dude) | +[kbknapp](https://github.com/kbknapp) |[homu](https://github.com/homu) |[Vinatorul](https://github.com/Vinatorul) |[tormol](https://github.com/tormol) |[little-dude](https://github.com/little-dude) |[sru](https://github.com/sru) | -[sru](https://github.com/sru) |[mgeisler](https://github.com/mgeisler) |[nabijaczleweli](https://github.com/nabijaczleweli) |[Byron](https://github.com/Byron) |[hgrecco](https://github.com/hgrecco) |[bluejekyll](https://github.com/bluejekyll) | +[willmurphyscode](https://github.com/willmurphyscode) |[mgeisler](https://github.com/mgeisler) |[nabijaczleweli](https://github.com/nabijaczleweli) |[Byron](https://github.com/Byron) |[hgrecco](https://github.com/hgrecco) |[bluejekyll](https://github.com/bluejekyll) | :---: |:---: |:---: |:---: |:---: |:---: | -[sru](https://github.com/sru) |[mgeisler](https://github.com/mgeisler) |[nabijaczleweli](https://github.com/nabijaczleweli) |[Byron](https://github.com/Byron) |[hgrecco](https://github.com/hgrecco) |[bluejekyll](https://github.com/bluejekyll) | +[willmurphyscode](https://github.com/willmurphyscode) |[mgeisler](https://github.com/mgeisler) |[nabijaczleweli](https://github.com/nabijaczleweli) |[Byron](https://github.com/Byron) |[hgrecco](https://github.com/hgrecco) |[bluejekyll](https://github.com/bluejekyll) | [segevfiner](https://github.com/segevfiner) |[ignatenkobrain](https://github.com/ignatenkobrain) |[james-darkfox](https://github.com/james-darkfox) |[H2CO3](https://github.com/H2CO3) |[nateozem](https://github.com/nateozem) |[glowing-chemist](https://github.com/glowing-chemist) | :---: |:---: |:---: |:---: |:---: |:---: | [segevfiner](https://github.com/segevfiner) |[ignatenkobrain](https://github.com/ignatenkobrain) |[james-darkfox](https://github.com/james-darkfox) |[H2CO3](https://github.com/H2CO3) |[nateozem](https://github.com/nateozem) |[glowing-chemist](https://github.com/glowing-chemist) | -[discosultan](https://github.com/discosultan) |[rtaycher](https://github.com/rtaycher) |[Arnavion](https://github.com/Arnavion) |[japaric](https://github.com/japaric) |[untitaker](https://github.com/untitaker) |[afiune](https://github.com/afiune) | +[rtaycher](https://github.com/rtaycher) |[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) | :---: |:---: |:---: |:---: |:---: |:---: | -[discosultan](https://github.com/discosultan) |[rtaycher](https://github.com/rtaycher) |[Arnavion](https://github.com/Arnavion) |[japaric](https://github.com/japaric) |[untitaker](https://github.com/untitaker) |[afiune](https://github.com/afiune) | +[rtaycher](https://github.com/rtaycher) |[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) | -[crazymerlyn](https://github.com/crazymerlyn) |[SuperFluffy](https://github.com/SuperFluffy) |[matthiasbeyer](https://github.com/matthiasbeyer) |[malbarbo](https://github.com/malbarbo) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) | +[SuperFluffy](https://github.com/SuperFluffy) |[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) | :---: |:---: |:---: |:---: |:---: |:---: | -[crazymerlyn](https://github.com/crazymerlyn) |[SuperFluffy](https://github.com/SuperFluffy) |[matthiasbeyer](https://github.com/matthiasbeyer) |[malbarbo](https://github.com/malbarbo) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) | +[SuperFluffy](https://github.com/SuperFluffy) |[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) | -[jimmycuadra](https://github.com/jimmycuadra) |[Nemo157](https://github.com/Nemo157) |[severen](https://github.com/severen) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) | +[Nemo157](https://github.com/Nemo157) |[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) | :---: |:---: |:---: |:---: |:---: |:---: | -[jimmycuadra](https://github.com/jimmycuadra) |[Nemo157](https://github.com/Nemo157) |[severen](https://github.com/severen) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) | +[Nemo157](https://github.com/Nemo157) |[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) | -[frewsxcv](https://github.com/frewsxcv) |[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[starkat99](https://github.com/starkat99) | +[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[starkat99](https://github.com/starkat99) |[porglezomp](https://github.com/porglezomp) | :---: |:---: |:---: |:---: |:---: |:---: | -[frewsxcv](https://github.com/frewsxcv) |[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[starkat99](https://github.com/starkat99) | +[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[starkat99](https://github.com/starkat99) |[porglezomp](https://github.com/porglezomp) | -[porglezomp](https://github.com/porglezomp) |[kraai](https://github.com/kraai) |[musoke](https://github.com/musoke) |[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) | +[kraai](https://github.com/kraai) |[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) | :---: |:---: |:---: |:---: |:---: |:---: | -[porglezomp](https://github.com/porglezomp) |[kraai](https://github.com/kraai) |[musoke](https://github.com/musoke) |[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) | +[kraai](https://github.com/kraai) |[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) | -[vmchale](https://github.com/vmchale) |[etopiei](https://github.com/etopiei) |[messense](https://github.com/messense) |[Keats](https://github.com/Keats) |[kieraneglin](https://github.com/kieraneglin) |[durka](https://github.com/durka) | +[messense](https://github.com/messense) |[Keats](https://github.com/Keats) |[kieraneglin](https://github.com/kieraneglin) |[durka](https://github.com/durka) |[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) | :---: |:---: |:---: |:---: |:---: |:---: | -[vmchale](https://github.com/vmchale) |[etopiei](https://github.com/etopiei) |[messense](https://github.com/messense) |[Keats](https://github.com/Keats) |[kieraneglin](https://github.com/kieraneglin) |[durka](https://github.com/durka) | +[messense](https://github.com/messense) |[Keats](https://github.com/Keats) |[kieraneglin](https://github.com/kieraneglin) |[durka](https://github.com/durka) |[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) | -[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[AndrewGaspar](https://github.com/AndrewGaspar) | +[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[brennie](https://github.com/brennie) | :---: |:---: |:---: |:---: |:---: |:---: | -[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[AndrewGaspar](https://github.com/AndrewGaspar) | +[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[brennie](https://github.com/brennie) | -[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[pixelistik](https://github.com/pixelistik) |[ogham](https://github.com/ogham) |[Bilalh](https://github.com/Bilalh) |[dotdash](https://github.com/dotdash) | +[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) | :---: |:---: |:---: |:---: |:---: |:---: | -[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[pixelistik](https://github.com/pixelistik) |[ogham](https://github.com/ogham) |[Bilalh](https://github.com/Bilalh) |[dotdash](https://github.com/dotdash) | +[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) | -[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) | +[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) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | +[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) | -[daboross](https://github.com/daboross) |[da-x](https://github.com/da-x) |[mernen](https://github.com/mernen) |[dguo](https://github.com/dguo) |[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) | +[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) |[eddyb](https://github.com/eddyb) |[Enet4](https://github.com/Enet4) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) | :---: |:---: |:---: |:---: |:---: |:---: | -[daboross](https://github.com/daboross) |[da-x](https://github.com/da-x) |[mernen](https://github.com/mernen) |[dguo](https://github.com/dguo) |[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) | +[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) |[eddyb](https://github.com/eddyb) |[Enet4](https://github.com/Enet4) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) | -[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) | +[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) |[SirVer](https://github.com/SirVer) |[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[discosultan](https://github.com/discosultan) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | +[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) |[SirVer](https://github.com/SirVer) |[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[discosultan](https://github.com/discosultan) | -[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) | +[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) |[jtdowney](https://github.com/jtdowney) |[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | +[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) |[jtdowney](https://github.com/jtdowney) |[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) | -[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) | +[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) |[mdaffin](https://github.com/mdaffin) |[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | +[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) |[mdaffin](https://github.com/mdaffin) |[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) | -[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) | +[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) |[Geogi](https://github.com/Geogi) |[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | +[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) |[Geogi](https://github.com/Geogi) |[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) | -[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) | +[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) |[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | +[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) |[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) | -[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) |[swatteau](https://github.com/swatteau) |[tchajed](https://github.com/tchajed) |[tspiteri](https://github.com/tspiteri) | +[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) |[mineo](https://github.com/mineo) | :---: |:---: |:---: |:---: |:---: |:---: | -[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) |[swatteau](https://github.com/swatteau) |[tchajed](https://github.com/tchajed) |[tspiteri](https://github.com/tspiteri) | +[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) |[mineo](https://github.com/mineo) | -[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) | +[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) |[ogham](https://github.com/ogham) | :---: |:---: |:---: |:---: |:---: |:---: | -[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) | - -[kennytm](https://github.com/kennytm) |[king6cong](https://github.com/king6cong) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) |[brennie](https://github.com/brennie) | -:---: |:---: |:---: |:---: |:---: | -[kennytm](https://github.com/kennytm) |[king6cong](https://github.com/king6cong) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) |[brennie](https://github.com/brennie) | +[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) |[ogham](https://github.com/ogham) | diff --git a/Cargo.toml b/Cargo.toml index 47498c1ee5ac..bcbc1331e827 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,38 @@ [package] - name = "clap" -version = "2.33.0" +version = "3.0.0-beta.1" authors = ["Kevin K. "] -exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] +exclude = [ + ".github/*", + "examples/*", + "tests/*", + "benches/*", + "clap-perf/*", + "etc/*", + "*.png", + "*.dot", + "*.yml", + "*.toml", + "*.md", + "clap-test.rs" +] +include = [ + "src/**/*", + "Cargo.toml", + "README.md" +] repository = "https://github.com/clap-rs/clap" documentation = "https://docs.rs/clap/" homepage = "https://clap.rs/" readme = "README.md" -license = "MIT" -keywords = ["argument", "cli", "arg", "parser", "parse"] +license = "MIT OR Apache-2.0" +keywords = [ + "argument", + "cli", + "arg", + "parser", + "parse" +] categories = ["command-line-interface"] description = """ A simple to use, efficient, and full-featured Command Line Argument Parser @@ -26,43 +49,36 @@ maintenance = {status = "actively-developed"} [dependencies] bitflags = "1.0" unicode-width = "0.1.4" -textwrap = "0.11.0" -strsim = { version = "0.8", optional = true } -yaml-rust = { version = "0.3.5", optional = true } -clippy = { version = "~0.0.166", optional = true } +textwrap = "0.11" +indexmap = "1.0.1" +strsim = { version = "0.9.0", optional = true } +yaml-rust = { version = "0.4", optional = true } atty = { version = "0.2.2", optional = true } vec_map = { version = "0.8", optional = true } -term_size = { version = "0.3.0", optional = true } +term_size = { version = "1.0.0-beta1", optional = true } +clap_derive = { git = "https://github.com/clap-rs/clap_derive", optional = true } [target.'cfg(not(windows))'.dependencies] -ansi_term = { version = "0.11", optional = true } +ansi_term = { version = "0.11.0", optional = true } [dev-dependencies] -regex = "1" -lazy_static = "1.3" +regex = "1.0" +lazy_static = "1" version-sync = "0.8" [features] -default = ["suggestions", "color", "vec_map"] +default = ["suggestions", "color", "vec_map", "derive"] suggestions = ["strsim"] color = ["ansi_term", "atty"] wrap_help = ["term_size", "textwrap/term_size"] +derive = ["clap_derive"] yaml = ["yaml-rust"] unstable = [] # for building with unstable clap features (doesn't require nightly Rust) (currently none) nightly = [] # for building with unstable Rust features (currently none) -lints = ["clippy"] # Requires nightly Rust debug = [] # Enables debug messages no_cargo = [] # Enable if you're not using Cargo, disables Cargo-env-var-dependent macros doc = ["yaml"] # All the features which add to documentation -[profile.release] -opt-level = 3 -debug = false -rpath = false -lto = true -debug-assertions = false -codegen-units = 1 - [profile.dev] opt-level = 0 debug = true diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000000..261eeb9e9f8b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index cfeaff16c5e1..5a972cfa5bb7 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,13 @@ clap ==== -[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap.svg)](https://crates.io/crates/clap) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/clap-rs/clap/blob/master/LICENSE-MIT) [![Coverage Status](https://coveralls.io/repos/kbknapp/clap-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/kbknapp/clap-rs?branch=master) [![Join the chat at https://gitter.im/kbknapp/clap-rs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kbknapp/clap-rs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap.svg)](https://crates.io/crates/clap) [![license](http://img.shields.io/badge/license-Apache 2.0-blue.svg)](https://github.com/clap-rs/clap/blob/master/LICENSE-APACHE) [![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/clap-rs/clap/blob/master/LICENSE-MIT) [![Coverage Status](https://coveralls.io/repos/clap-rs/clap/badge.svg?branch=master&service=github)](https://coveralls.io/github/clap-rs/clap?branch=master) [![Join the chat at https://gitter.im/clap-rs/clap](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/clap-rs/clap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Linux: [![Build Status](https://travis-ci.org/clap-rs/clap.svg?branch=master)](https://travis-ci.org/clap-rs/clap) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/ejg8c33dn31nhv36/branch/master?svg=true)](https://ci.appveyor.com/project/kbknapp/clap-rs/branch/master) Command Line Argument Parser for Rust -# Important note on clap 3.0 - -Clap 3.0 is under development and available on the [v3-master](https://github.com/clap-rs/clap/tree/v3-master). When sending a pull request, ensure that it is against v3-master branch and not already covered by the 3.0 refactor. Thanks! - - It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcommands when writing console/terminal applications. * [documentation](https://docs.rs/clap/) @@ -22,6 +17,7 @@ It is a simple-to-use, efficient, and full-featured library for parsing command Table of Contents ================= +* [What's New](#whats-new) * [About](#about) * [FAQ](#faq) * [Features](#features) @@ -44,6 +40,12 @@ Table of Contents Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) +## What's New + +Here's whats new in 3.0.0-alpha.1: + +For full details, see [CHANGELOG.md](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) + ## About `clap` is used to parse *and validate* the string of command line arguments provided by a user at runtime. You provide the list of valid possibilities, and `clap` handles the rest. This means you focus on your *applications* functionality, and less on the parsing and validating of arguments. @@ -60,6 +62,14 @@ For a full FAQ and more in depth details, see [the wiki page](https://github.com First, let me say that these comparisons are highly subjective, and not meant in a critical or harsh manner. All the argument parsing libraries out there (to include `clap`) have their own strengths and weaknesses. Sometimes it just comes down to personal taste when all other factors are equal. When in doubt, try them all and pick one that you enjoy :) There's plenty of room in the Rust community for multiple implementations! +#### How does `clap` compare to [structopt](https://github.com/TeXitoi/structopt)? + +Simple! `clap` *is* `stuctopt`. With the 3.0 release, `clap` imported the `structopt` code into it's own codebase as the [`clap_derive`](https://github.com/clap-rs/clap_derive) crate. Since `structopt` already used `clap` under the hood, the transition was nearly painless, and is 100% feature compatible. + +If you were using `structopt` before, the only thing you should have to do is change the attributes from `#[structopt(...)]` to `#[clap(...)]`. + +Also the derive statements changed from `#[derive(Structopt)]` to `#[derive(Clap)]`. There is also some additional functionality that's been added to the `clap_derive` crate. See the documentation for that crate, for more details. + #### How does `clap` compare to [getopts](https://github.com/rust-lang-nursery/getopts)? `getopts` is a very basic, fairly minimalist argument parsing library. This isn't a bad thing, sometimes you don't need tons of features, you just want to parse some simple arguments, and have some help text generated for you based on valid arguments you specify. The downside to this approach is that you must manually implement most of the common features (such as checking to display help messages, usage strings, etc.). If you want a highly custom argument parser, and don't mind writing the majority of the functionality yourself, `getopts` is an excellent base. @@ -82,15 +92,17 @@ Because `docopt` is doing a ton of work to parse your help messages and determin #### All else being equal, what are some reasons *not* to use `clap`? (The Anti Pitch) -Depending on the style in which you choose to define the valid arguments, `clap` can be very verbose. `clap` also offers so many fine-tuning knobs and dials, that learning everything can seem overwhelming. I strive to keep the simple cases simple, but when turning all those custom dials it can get complex. `clap` is also opinionated about parsing. Even though so much can be tweaked and tuned with `clap` (and I'm adding more all the time), there are still certain features which `clap` implements in specific ways which may be contrary to some users use-cases. Finally, `clap` is "stringly typed" when referring to arguments which can cause typos in code. This particular paper-cut is being actively worked on, and should be gone in v3.x. +Depending on the style in which you choose to define the valid arguments, `clap` can be very verbose. `clap` also offers so many finetuning knobs and dials, that learning everything can seem overwhelming. I strive to keep the simple cases simple, but when turning all those custom dials it can get complex. `clap` is also opinionated about parsing. Even though so much can be tweaked and tuned with `clap` (and I'm adding more all the time), there are still certain features which `clap` implements in specific ways which may be contrary to some users use-cases. Finally, `clap` is "stringly typed" when referring to arguments which can cause typos in code. This particular paper-cut is being actively worked on, and should be gone in v3.x. ## Features Below are a few of the features which `clap` supports, full descriptions and usage can be found in the [documentation](https://docs.rs/clap/) and [examples/](examples) directory +* Generate a CLI simply by defining a struct! * **Auto-generated Help, Version, and Usage information** - Can optionally be fully, or partially overridden if you want a custom help, version, or usage statements -* **Auto-generated completion scripts at compile time (Bash, Zsh, Fish, and PowerShell)** +* **Auto-generated completion scripts (Bash, Zsh, Fish, Elvish and PowerShell)** + - Using [`clap_generate`](https://github.com/clap-rs/clap_generate) - Even works through many multiple levels of subcommands - Works with options which only accept certain values - Works with subcommand aliases @@ -143,14 +155,82 @@ The following examples show a quick example of some of the very basic functional **NOTE:** All of these examples are functionally the same, but show different styles in which to use `clap`. These different styles are purely a matter of personal preference. -The first example shows a method using the 'Builder Pattern' which allows more advanced configuration options (not shown in this small example), or even dynamically generating arguments when desired. +The first example shows the simplest way to use `clap`, by defining a struct. If you're familiar with the `structopt` crate you're in luck, it's the same! (In fact it's the exact same code running under the covers!) ```rust -// (Full example with detailed comments in examples/01b_quick_example.rs) +// (Full example with detailed comments in examples/01d_quick_example.rs) // -// This example demonstrates clap's full 'builder pattern' style of creating arguments which is -// more verbose, but allows easier editing, and at times more advanced options, or the possibility -// to generate arguments dynamically. +// This example demonstrates clap's full 'custom derive' style of creating arguments which is the +// simplest method of use, but sacrifices some flexibility. +#[macro_use] +extern crate clap; + +/// This doc string acts as a help message when the user runs '--help' +/// as do all doc strings on fields +#[derive(Clap)] +#[clap(version = "1.0", author = "Kevin K.")] +struct Opts { + /// Sets a custom config file. Could have been an Option with no default too + #[clap(short = "c", long = "config", default_value = "default.conf")] + file: String, + /// Some input. Because this isn't an Option it's required to be used + input: String, + /// A level of verbosity, and can be used multiple times + #[clap(short = "v", long = "verbose", parse_from_occurrences)] + verbose: Option, + #[clap(subcommand)] + subcmd: SubCommand, +} + +#[derive(Clap)] +enum SubCommand { + /// A subcommand for controlling testing + #[clap(name = "test", version = "1.3", author = "Someone Else")] + Test { + /// Print debug info + #[clap(short = "d")] + debug: bool + } +} +fn main() { + let opts: Opts = Opts::parse(); + + // Gets a value for config if supplied by user, or defaults to "default.conf" + println!("Value for config: {}", opts.config); + println!("Using input file: {}", opts.input); + + // Vary the output based on how many times the user used the "verbose" flag + // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v' + match opts.verbose.unwrap_or(0) { + 0 => println!("No verbose info"), + 1 => println!("Some verbose info"), + 2 => println!("Tons of verbose info"), + 3 | _ => println!("Don't be crazy"), + } + + // You can handle information about subcommands by requesting their matches by name + // (as below), requesting just the name used, or both at the same time + match matches.subcmd { + SubCommand::Test @ t => { + if t.debug { + println!("Printing debug info..."); + } else { + println!("Printing normally..."); + } + } + } + + // more program logic goes here... +} +``` + +This second method shows a method using the 'Builder Pattern' which allows more advanced configuration options (not shown in this small example), or even dynamically generating arguments when desired. The downside is it's more verbose. + +```rust +// (Full example with detailed comments in examples/01a_quick_example.rs) +// +// This example demonstrates clap's "builder pattern" method of creating arguments +// which the most flexible, but also most verbose. extern crate clap; use clap::{Arg, App, SubCommand}; @@ -160,7 +240,7 @@ fn main() { .author("Kevin K. ") .about("Does awesome things") .arg(Arg::with_name("config") - .short("c") + .short('c') .long("config") .value_name("FILE") .help("Sets a custom config file") @@ -170,7 +250,7 @@ fn main() { .required(true) .index(1)) .arg(Arg::with_name("v") - .short("v") + .short('v') .multiple(true) .help("Sets the level of verbosity")) .subcommand(SubCommand::with_name("test") @@ -178,42 +258,45 @@ fn main() { .version("1.3") .author("Someone E. ") .arg(Arg::with_name("debug") - .short("d") + .short('d') .help("print debug information verbosely"))) .get_matches(); - // Gets a value for config if supplied by user, or defaults to "default.conf" - let config = matches.value_of("config").unwrap_or("default.conf"); - println!("Value for config: {}", config); + // Same as above examples... +} +``` - // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't - // required we could have used an 'if let' to conditionally get the value) - println!("Using input file: {}", matches.value_of("INPUT").unwrap()); +The next example shows a far less verbose method, but sacrifices some of the advanced configuration options (not shown in this small example). This method also takes a *very* minor runtime penalty. - // Vary the output based on how many times the user used the "verbose" flag - // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v' - match matches.occurrences_of("v") { - 0 => println!("No verbose info"), - 1 => println!("Some verbose info"), - 2 => println!("Tons of verbose info"), - 3 | _ => println!("Don't be crazy"), - } +```rust +// (Full example with detailed comments in examples/01a_quick_example.rs) +// +// This example demonstrates clap's "usage strings" method of creating arguments +// which is less verbose +extern crate clap; +use clap::{Arg, App, SubCommand}; - // You can handle information about subcommands by requesting their matches by name - // (as below), requesting just the name used, or both at the same time - if let Some(matches) = matches.subcommand_matches("test") { - if matches.is_present("debug") { - println!("Printing debug info..."); - } else { - println!("Printing normally..."); - } - } +fn main() { + let matches = App::new("myapp") + .version("1.0") + .author("Kevin K. ") + .about("Does awesome things") + .args_from_usage( + "-c, --config=[FILE] 'Sets a custom config file' + 'Sets the input file to use' + -v... 'Sets the level of verbosity'") + .subcommand(SubCommand::with_name("test") + .about("controls testing features") + .version("1.3") + .author("Someone E. ") + .arg("-d, --debug 'Print debug information'")) + .get_matches(); - // more program logic goes here... + // Same as previous example... } ``` -One could also optionally declare their CLI in YAML format and keep your Rust source tidy +This third method shows how you can use a YAML file to build your CLI and keep your Rust source tidy or support multiple localized translations by having different YAML files for each localization. First, create the `cli.yml` file to hold your CLI options, but it could be called anything we like: @@ -251,7 +334,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.33"` to `clap = {version = "2.33", features = ["yaml"]}`. +Simply change your `clap = "3.0.0-beta.1"` to `clap = {version = "3.0.0-beta.1", features = ["yaml"]}`. Finally we create our `main.rs` file just like we would have with the previous two examples: @@ -273,7 +356,7 @@ fn main() { } ``` -If you were to compile any of the above programs and run them with the flag `--help` or `-h` (or `help` subcommand, since we defined `test` as a subcommand) the following would be output +If you were to compile any of the above programs and run them with the flag `--help` or `-h` (or `help` subcommand, since we defined `test` as a subcommand) the following would be output (except the first example where the help message sort of explains the Rust code). ```sh $ myprog --help @@ -281,6 +364,9 @@ My Super Program 1.0 Kevin K. Does awesome things +ARGS: + INPUT The input file to use + USAGE: MyApp [FLAGS] [OPTIONS] [SUBCOMMAND] @@ -292,9 +378,6 @@ FLAGS: OPTIONS: -c, --config Sets a custom config file -ARGS: - INPUT The input file to use - SUBCOMMANDS: help Prints this message or the help of the given subcommand(s) test Controls testing features @@ -302,20 +385,13 @@ SUBCOMMANDS: **NOTE:** You could also run `myapp test --help` or `myapp help test` to see the help message for the `test` subcommand. -There are also two other methods to create CLIs. Which style you choose is largely a matter of personal preference. The two other methods are: - -* Using [usage strings (examples/01a_quick_example.rs)](examples/01a_quick_example.rs) similar to (but not exact) docopt style usage statements. This is far less verbose than the above methods, but incurs a slight runtime penalty. -* Using [a macro (examples/01c_quick_example.rs)](examples/01c_quick_example.rs) which is like a hybrid of the builder and usage string style. It's less verbose, but doesn't incur the runtime penalty of the usage string style. The downside is that it's harder to debug, and more opaque. - -Examples of each method can be found in the [examples/](examples) directory of this repository. - ## Try it! ### Pre-Built Test To try out the pre-built examples, use the following steps: -* Clone the repository `$ git clone https://github.com/clap-rs/clap && cd clap-rs/` +* Clone the repository `$ git clone https://github.com/clap-rs/clap && cd clap/` * Compile the example `$ cargo build --example ` * Run the help info `$ ./target/debug/examples/ --help` * Play with the arguments! @@ -329,17 +405,23 @@ To test out `clap`'s default auto-generated help/version follow these steps: ```toml [dependencies] -clap = "2" +clap = "3.0.0-beta.1" ``` * Add the following to your `src/main.rs` ```rust +#[macro_use] extern crate clap; use clap::App; +#[derive(Clap)] +#[clap(version = "v1.0-beta")] +/// My First clap CLI! +struct Opts; + fn main() { - App::new("fake").version("v1.0-beta").get_matches(); + Opts::parse(); } ``` @@ -352,7 +434,7 @@ For full usage, add `clap` as a dependency in your `Cargo.toml` () to use from c ```toml [dependencies] -clap = "~2.33" +clap = "~3.0.0-beta.1" ``` (**note**: If you are concerned with supporting a minimum version of Rust that is *older* than the current stable Rust minus 2 stable releases, it's recommended to use the `~major.minor.patch` style versions in your `Cargo.toml` which will only update the patch version automatically. For more information see the [Compatibility Policy](#compatibility-policy)) @@ -367,15 +449,16 @@ Then run `cargo build` or `cargo update && cargo build` for your project. #### Features enabled by default +* **derive**: Enables the custom derive (i.e. `#[derive(Clap)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above * **"suggestions"**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`) -* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term` only on non-Windows targets) +* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term`) * **"vec_map"**: Use [`VecMap`](https://crates.io/crates/vec_map) internally instead of a [`BTreeMap`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html). This feature provides a _slight_ performance improvement. (builds dependency `vec_map`) To disable these, add this to your `Cargo.toml`: ```toml [dependencies.clap] -version = "2.33" +version = "3.0.0-beta.1" default-features = false ``` @@ -383,7 +466,7 @@ You can also selectively enable only the features you'd like to include, by addi ```toml [dependencies.clap] -version = "2.33" +version = "3.0.0-beta.1" default-features = false # Cherry-pick the features you'd like to use @@ -398,13 +481,7 @@ features = [ "suggestions", "color" ] ### Dependencies Tree -The following graphic depicts `clap`s dependency graph (generated using [cargo-graph](https://github.com/kbknapp/cargo-graph)). - - * **Dashed** Line: Optional dependency - * **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) +@TODO-v3-beta: update ### More Information @@ -432,7 +509,7 @@ In order to keep from being surprised of breaking changes, it is **highly** reco ```toml [dependencies] -clap = "~2.33" +clap = "~3.0.0-beta.1" ``` This will cause *only* the patch version to be updated upon a `cargo update` call, and therefore cannot break due to new features, or bumped minimum versions of Rust. @@ -449,20 +526,20 @@ Right now Cargo's version resolution is pretty naive, it's just a brute-force se # In one Cargo.toml [dependencies] -clap = "~2.33.0" +clap = "~3.0.0-beta.1" # In another Cargo.toml [dependencies] -clap = "2.33.0" +clap = "3.0.0-beta.1" ``` This is inherently an unresolvable crate graph in Cargo right now. Cargo requires there's only one major version of a crate, and being in the same workspace these two crates must share a version. This is impossible in this location, though, as these version constraints cannot be met. #### Minimum Version of Rust -`clap` will officially support current stable Rust, minus two releases, but may work with prior releases as well. For example, current stable Rust at the time of this writing is 1.32.0, meaning `clap` is guaranteed to compile with 1.30.0 and beyond. +`clap` will officially support current stable Rust, minus two releases, but may work with prior releases as well. For example, current stable Rust at the time of this writing is 1.35.0, meaning `clap` is guaranteed to compile with 1.33.0 and beyond. -At the 1.33.0 stable release, `clap` will be guaranteed to compile with 1.31.0 and beyond, etc. +At the 1.36.0 stable release, `clap` will be guaranteed to compile with 1.34.0 and beyond, etc. Upon bumping the minimum version of Rust (assuming it's within the stable-2 range), it *must* be clearly annotated in the `CHANGELOG.md` @@ -474,21 +551,17 @@ Upon bumping the minimum version of Rust (assuming it's within the stable-2 rang * The breaking change is to be fixing a bug (i.e. relying on a bug as a feature) * The breaking change is a feature isn't used in the wild, or all users of said feature have given approval *prior* to the change -#### Compatibility with Wasm - -A best effort is made to ensure that `clap` will work on projects targeting `wasm32-unknown-unknown`. However there is no dedicated CI build -covering this specific target. - ## License -`clap` is licensed under the MIT license. Please read the [LICENSE-MIT](LICENSE-MIT) file in this repository for more information. +`clap` is distributed under the terms of both the MIT license and the Apache License (Version 2.0). + +See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files in this repository for more information. ## Related Crates There are several excellent crates which can be used with `clap`, I recommend checking them all out! If you've got a crate that would be a good fit to be used with `clap` open an issue and let me know, I'd love to add it! -* [`structopt`](https://github.com/TeXitoi/structopt) - This crate allows you to define a struct, and build a CLI from it! No more "stringly typed" and it uses `clap` behind the scenes! (*Note*: There is work underway to pull this crate into mainline `clap`). -* [`assert_cmd`](https://github.com/assert-rs/assert_cmd) - This crate allows you test your CLIs in a very intuitive and functional way! +* [`assert_cli`](https://github.com/assert-rs/assert_cli) - This crate allows you test your CLIs in a very intuitive and functional way! ## Recent Breaking Changes diff --git a/SPONSORS.md b/SPONSORS.md index 67f5544f9b46..5cc5a0b1c40c 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -4,14 +4,13 @@ If you are interested in becoming a sponsor for this project please our [sponsor ## Recurring Sponsors: -| [Noelia Seva-Gonzalez](https://noeliasg.com/about/) | [messense](https://github.com/messense) | [Josh](https://joshtriplett.org) | Stephen Oats | -|:-:|:-:|:-:|:-:| -|Noelia Seva-Gonzalez | Messense | Josh Triplett | Stephen Oats | +| [Noelia Seva-Gonzalez](https://noeliasg.com/about/) | [messense](https://github.com/messense) | [Josh](https://joshtriplett.org) | +|:-:|:-:|:-:| +|Noelia Seva-Gonzalez | Messense | Josh Triplett | ## Single-Donation and Former Sponsors: | [Rob Tsuk](https://github.com/rtsuk)| | | |:-:|:-:|:-:| -|Rob Tsuk| | | - +|Rob Tsuk| | | \ No newline at end of file diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000000..de8f58453bad --- /dev/null +++ b/TODO.md @@ -0,0 +1,19 @@ +- [ ] update-todo.sh +- [ ] src/lib.rs + -[ ] 578:@TODO @release @docs + -[ ] 580:@TODO @release @docs + -[ ] 583:@TODO @release @docs + -[ ] 592:@TODO @release @docs + -[ ] 598:@TODO @release @docs + -[ ] 608:@TODO @release @docs + -[ ] 610:@TODO @release @docs + -[ ] 614:@TODO @release @docs + -[ ] 616:@TODO @release @docs + -[ ] 619:@TODO @release @docs + -[ ] 623:@TODO @release @docs +- [ ] src/app/parser.rs + -[ ] 678:@TODO @perf: cloning all these Apps ins't great, but since it's just displaying the help +- [ ] src/app/mod.rs + -[ ] 1760:@TODO @v3-alpha @perf: should only propagate globals to subcmd we find, or for help +- [ ] src/args/arg.rs + -[ ] 3545:@TODO @p2 @docs @release: write docs diff --git a/benches/01_default.rs b/benches/01_default.rs index 24e619e6ebb8..ea3bc362b240 100644 --- a/benches/01_default.rs +++ b/benches/01_default.rs @@ -8,11 +8,7 @@ use clap::App; use test::Bencher; #[bench] -fn build_app(b: &mut Bencher) { - b.iter(|| App::new("claptests")); -} +fn build_app(b: &mut Bencher) { b.iter(|| App::new("claptests")); } #[bench] -fn parse_clean(b: &mut Bencher) { - b.iter(|| App::new("claptests").get_matches_from(vec![""])); -} +fn parse_clean(b: &mut Bencher) { b.iter(|| App::new("claptests").get_matches_from(vec![""])); } diff --git a/benches/02_simple.rs b/benches/02_simple.rs index 010fa866fa79..5ef74045e7dd 100644 --- a/benches/02_simple.rs +++ b/benches/02_simple.rs @@ -8,79 +8,64 @@ use clap::{App, Arg}; use test::Bencher; macro_rules! create_app { - () => ({ + () => {{ App::new("claptests") - .version("0.1") - .about("tests clap library") - .author("Kevin K. ") - .args_from_usage("-f --flag 'tests flags' - -o --option=[opt] 'tests options' - [positional] 'tests positional'") - }) + .version("0.1") + .about("tests clap library") + .author("Kevin K. ") + .arg("-f --flag 'tests flags'") + .arg("-o --option=[opt] 'tests options'") + .arg("[positional] 'tests positional'") + }}; } #[bench] -fn build_app(b: &mut Bencher) { - - b.iter(|| create_app!()); -} +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") - } + fn build_app() -> App<'static> { App::new("claptests") } - b.iter(|| build_app().arg(Arg::from_usage("-s, --some 'something'"))); + b.iter(|| build_app().arg(Arg::from("-s, --some 'something'"))); } #[bench] fn add_flag_ref(b: &mut Bencher) { - fn build_app() -> App<'static, 'static> { - App::new("claptests") - } + fn build_app() -> App<'static> { App::new("claptests") } b.iter(|| { - let arg = Arg::from_usage("-s, --some 'something'"); + let arg = Arg::from("-s, --some 'something'"); build_app().arg(&arg) }); } #[bench] fn add_opt(b: &mut Bencher) { - fn build_app() -> App<'static, 'static> { - App::new("claptests") - } + fn build_app() -> App<'static> { App::new("claptests") } - b.iter(|| build_app().arg(Arg::from_usage("-s, --some 'something'"))); + b.iter(|| build_app().arg(Arg::from("-s, --some 'something'"))); } #[bench] fn add_opt_ref(b: &mut Bencher) { - fn build_app() -> App<'static, 'static> { - App::new("claptests") - } + fn build_app() -> App<'static> { App::new("claptests") } b.iter(|| { - let arg = Arg::from_usage("-s, --some 'something'"); + let arg = Arg::from("-s, --some 'something'"); build_app().arg(&arg) }); } #[bench] fn add_pos(b: &mut Bencher) { - fn build_app() -> App<'static, 'static> { - App::new("claptests") - } + fn build_app() -> App<'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") - } + fn build_app() -> App<'static> { App::new("claptests") } b.iter(|| { let arg = Arg::with_name("some"); @@ -89,14 +74,10 @@ fn add_pos_ref(b: &mut Bencher) { } #[bench] -fn parse_clean(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec![""])); -} +fn parse_clean(b: &mut Bencher) { b.iter(|| create_app!().get_matches_from(vec![""])); } #[bench] -fn parse_flag(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec!["myprog", "-f"])); -} +fn parse_flag(b: &mut Bencher) { b.iter(|| create_app!().get_matches_from(vec!["myprog", "-f"])); } #[bench] fn parse_option(b: &mut Bencher) { diff --git a/benches/03_complex.rs b/benches/03_complex.rs index b00fc4eac569..f4025dcc431d 100644 --- a/benches/03_complex.rs +++ b/benches/03_complex.rs @@ -4,133 +4,166 @@ extern crate clap; extern crate test; -use clap::{App, Arg, SubCommand, AppSettings}; +use clap::{App, AppSettings, Arg, ArgSettings}; use test::Bencher; -static ARGS: &'static str = "-o --option=[opt]... 'tests options' - [positional] 'tests positionals'"; static OPT3_VALS: [&'static str; 2] = ["fast", "slow"]; static POS3_VALS: [&'static str; 2] = ["vi", "emacs"]; macro_rules! create_app { - () => ({ + () => {{ App::new("claptests") - .version("0.1") - .about("tests clap library") - .author("Kevin K. ") - .args_from_usage(ARGS) - .arg(Arg::from_usage("-f --flag... 'tests flags'") - .global(true)) - .args(&[ - Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("option2"), - Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), - Arg::from_usage("[positional2] 'tests positionals with exclusions'"), - Arg::from_usage("-O --Option [option3] 'tests options with specific value sets'").possible_values(&OPT3_VALS), - Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(&POS3_VALS), - Arg::from_usage("--multvals [one] [two] 'Tests multiple values, not mult occs'"), - Arg::from_usage("--multvalsmo... [one] [two] 'Tests multiple values, not mult occs'"), - Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), - Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) - ]) - .subcommand(SubCommand::with_name("subcmd") - .about("tests subcommands") - .version("0.1") - .author("Kevin K. ") - .arg_from_usage("-o --option [scoption]... 'tests options'") - .arg_from_usage("[scpositional] 'tests positionals'")) - }) + .version("0.1") + .about("tests clap library") + .author("Kevin K. ") + .arg("-o --option=[opt]... 'tests options'") + .arg("[positional] 'tests positionals'") + .arg(Arg::from("-f --flag... 'tests flags'").global(true)) + .args(&[ + Arg::from("[flag2] -F 'tests flags with exclusions'") + .conflicts_with("flag") + .requires("option2"), + Arg::from("--long-option-2 [option2] 'tests long options with exclusions'") + .conflicts_with("option") + .requires("positional2"), + Arg::from("[positional2] 'tests positionals with exclusions'"), + Arg::from("-O --Option [option3] 'tests options with specific value sets'") + .possible_values(&OPT3_VALS), + Arg::from("[positional3]... 'tests positionals with specific values'") + .possible_values(&POS3_VALS), + Arg::from("--multvals [one] [two] 'Tests mutliple values, not mult occs'"), + Arg::from("--multvalsmo... [one] [two] 'Tests mutliple values, not mult occs'"), + Arg::from("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), + Arg::from("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3), + ]) + .subcommand( + App::new("subcmd") + .about("tests subcommands") + .version("0.1") + .author("Kevin K. ") + .arg("-o --option [scoption]... 'tests options'") + .arg("[scpositional] 'tests positionals'"), + ) + }}; } #[bench] -fn create_app_from_usage(b: &mut Bencher) { - - b.iter(|| create_app!()); -} +fn create_app_from_usage(b: &mut Bencher) { b.iter(|| create_app!()); } #[bench] fn create_app_builder(b: &mut Bencher) { b.iter(|| { App::new("claptests") - .version("0.1") - .about("tests clap library") - .author("Kevin K. ") - .arg(Arg::with_name("opt") + .version("0.1") + .about("tests clap library") + .author("Kevin K. ") + .arg( + Arg::with_name("opt") .help("tests options") - .short("o") + .short('o') .long("option") - .takes_value(true) - .multiple(true)) - .arg(Arg::with_name("positional") + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences), + ) + .arg( + Arg::with_name("positional") .help("tests positionals") - .index(1)) - .arg(Arg::with_name("flag") - .short("f") - .help("tests flags") - .long("flag") - .multiple(true) - .global(true)) - .arg(Arg::with_name("flag2") - .short("F") + .index(1), + ) + .arg( + Arg::with_name("flag") + .short('f') + .help("tests flags") + .long("flag") + .global(true) + .settings(&[ArgSettings::MultipleOccurrences]), + ) + .arg( + Arg::with_name("flag2") + .short('F') .help("tests flags with exclusions") .conflicts_with("flag") - .requires("option2")) - .arg(Arg::with_name("option2") + .requires("option2"), + ) + .arg( + Arg::with_name("option2") .help("tests long options with exclusions") .conflicts_with("option") .requires("positional2") - .takes_value(true) - .long("long-option-2")) - .arg(Arg::with_name("positional2") + .setting(ArgSettings::TakesValue) + .long("long-option-2"), + ) + .arg( + Arg::with_name("positional2") .index(3) - .help("tests positionals with exclusions")) - .arg(Arg::with_name("option3") - .short("O") + .help("tests positionals with exclusions"), + ) + .arg( + Arg::with_name("option3") + .short('O') .long("Option") - .takes_value(true) + .setting(ArgSettings::TakesValue) .help("tests options with specific value sets") - .possible_values(&OPT3_VALS)) - .arg(Arg::with_name("positional3") - .multiple(true) + .possible_values(&OPT3_VALS), + ) + .arg( + Arg::with_name("positional3") + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences) .help("tests positionals with specific values") .index(4) - .possible_values(&POS3_VALS)) - .arg(Arg::with_name("multvals") + .possible_values(&POS3_VALS), + ) + .arg( + Arg::with_name("multvals") .long("multvals") - .takes_value(true) - .help("Tests multiple values, not mult occs") - .value_names(&["one", "two"])) - .arg(Arg::with_name("multvalsmo") + .help("Tests mutliple values, not mult occs") + .value_names(&["one", "two"]), + ) + .arg( + Arg::with_name("multvalsmo") .long("multvalsmo") - .takes_value(true) - .multiple(true) - .help("Tests multiple values, not mult occs") - .value_names(&["one", "two"])) - .arg(Arg::with_name("minvals") + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences) + .help("Tests mutliple values, not mult occs") + .value_names(&["one", "two"]), + ) + .arg( + Arg::with_name("minvals") .long("minvals2") - .multiple(true) - .takes_value(true) + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences) .help("Tests 2 min vals") - .min_values(2)) - .arg(Arg::with_name("maxvals") + .min_values(2), + ) + .arg( + Arg::with_name("maxvals") .long("maxvals3") - .takes_value(true) - .multiple(true) + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences) .help("Tests 3 max vals") - .max_values(3)) - .subcommand(SubCommand::with_name("subcmd") + .max_values(3), + ) + .subcommand( + App::new("subcmd") .about("tests subcommands") .version("0.1") .author("Kevin K. ") - .arg(Arg::with_name("scoption") - .short("o") - .long("option") - .multiple(true) - .takes_value(true) - .help("tests options")) - .arg(Arg::with_name("scpositional") - .index(1) - .help("tests positionals"))); + .arg( + Arg::with_name("scoption") + .short('o') + .long("option") + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences) + .help("tests options"), + ) + .arg( + Arg::with_name("scpositional") + .index(1) + .help("tests positionals"), + ), + ); }); } @@ -154,9 +187,9 @@ fn create_app_macros(b: &mut Bencher) { (@arg positional3: index(3) ... possible_value[vi emacs] "tests positionals with specific values") (@arg multvals: --multvals +takes_value value_name[one two] - "Tests multiple values, not mult occs") + "Tests mutliple values, not mult occs") (@arg multvalsmo: --multvalsmo ... +takes_value value_name[one two] - "Tests multiple values, not mult occs") + "Tests mutliple values, not mult occs") (@arg minvals: --minvals2 min_values(1) ... +takes_value "Tests 2 min vals") (@arg maxvals: --maxvals3 ... +takes_value max_values(3) "Tests 3 max vals") (@subcommand subcmd => @@ -170,14 +203,10 @@ fn create_app_macros(b: &mut Bencher) { } #[bench] -fn parse_clean(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec![""])); -} +fn parse_clean(b: &mut Bencher) { b.iter(|| create_app!().get_matches_from(vec![""])); } #[bench] -fn parse_flag(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec!["myprog", "-f"])); -} +fn parse_flag(b: &mut Bencher) { b.iter(|| create_app!().get_matches_from(vec!["myprog", "-f"])); } #[bench] fn parse_option(b: &mut Bencher) { @@ -211,20 +240,75 @@ fn parse_sc_positional(b: &mut Bencher) { #[bench] fn parse_complex1(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec!["myprog", "-ff", "-o", "option1", "arg1", "-O", "fast", "arg2", "--multvals", "one", "two", "emacs"])); + b.iter(|| { + create_app!().get_matches_from(vec![ + "myprog", + "-ff", + "-o", + "option1", + "arg1", + "-O", + "fast", + "arg2", + "--multvals", + "one", + "two", + "emacs", + ]) + }); } #[bench] fn parse_complex2(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec!["myprog", "arg1", "-f", "arg2", "--long-option-2", "some", "-O", "slow", "--multvalsmo", "one", "two", "--minvals2", "3", "2", "1"])); + b.iter(|| { + create_app!().get_matches_from(vec![ + "myprog", + "arg1", + "-f", + "arg2", + "--long-option-2", + "some", + "-O", + "slow", + "--multvalsmo", + "one", + "two", + "--minvals2", + "3", + "2", + "1", + ]) + }); } #[bench] fn parse_complex2_with_args_negate_scs(b: &mut Bencher) { - b.iter(|| create_app!().setting(AppSettings::ArgsNegateSubcommands).get_matches_from(vec!["myprog", "arg1", "-f", "arg2", "--long-option-2", "some", "-O", "slow", "--multvalsmo", "one", "two", "--minvals2", "3", "2", "1"])); + b.iter(|| { + create_app!() + .setting(AppSettings::ArgsNegateSubcommands) + .get_matches_from(vec![ + "myprog", + "arg1", + "-f", + "arg2", + "--long-option-2", + "some", + "-O", + "slow", + "--multvalsmo", + "one", + "two", + "--minvals2", + "3", + "2", + "1", + ]) + }); } #[bench] fn parse_sc_complex(b: &mut Bencher) { - b.iter(|| create_app!().get_matches_from(vec!["myprog", "subcmd", "-f", "-o", "option1", "arg1"])); + b.iter(|| { + create_app!().get_matches_from(vec!["myprog", "subcmd", "-f", "-o", "option1", "arg1"]) + }); } diff --git a/benches/04_new_help.rs b/benches/04_new_help.rs index f033efb5a19d..1680df5a27bd 100644 --- a/benches/04_new_help.rs +++ b/benches/04_new_help.rs @@ -8,191 +8,217 @@ use test::Bencher; use std::io::Cursor; use clap::App; -use clap::{Arg, SubCommand}; +use clap::{Arg, ArgSettings}; -fn build_help(app: &App) -> String { +fn build_help(app: &mut App) -> String { let mut buf = Cursor::new(Vec::with_capacity(50)); app.write_help(&mut buf).unwrap(); let content = buf.into_inner(); String::from_utf8(content).unwrap() } -fn app_example1<'b, 'c>() -> App<'b, 'c> { +fn app_example1<'c>() -> App<'c> { App::new("MyApp") .version("1.0") .author("Kevin K. ") .about("Does awesome things") - .args_from_usage("-c, --config=[FILE] 'Sets a custom config file' - 'Sets an optional output file' - -d... 'Turn debugging information on'") - .subcommand(SubCommand::with_name("test") - .about("does testing things") - .arg_from_usage("-l, --list 'lists test values'")) + .arg("-c, --config=[FILE] 'Sets a custom config file'") + .arg(" 'Sets an optional output file'") + .arg("-d... 'Turn debugging information on'") + .subcommand( + App::new("test") + .about("does testing things") + .arg("-l, --list 'lists test values'"), + ) } -fn app_example2<'b, 'c>() -> App<'b, 'c> { +fn app_example2<'c>() -> App<'c> { App::new("MyApp") .version("1.0") .author("Kevin K. ") .about("Does awesome things") } -fn app_example3<'b, 'c>() -> App<'b, 'c> { +fn app_example3<'c>() -> App<'c> { App::new("MyApp") - .arg(Arg::with_name("debug") - .help("turn on debugging information") - .short("d")) - .args(&[Arg::with_name("config") - .help("sets the config file to use") - .takes_value(true) - .short("c") - .long("config"), - Arg::with_name("input") - .help("the input file to use") - .index(1) - .required(true)]) - .arg_from_usage("--license 'display the license file'") - .args_from_usage("[output] 'Supply an output file to use' - -i, --int=[IFACE] 'Set an interface to use'") -} - -fn app_example4<'b, 'c>() -> App<'b, 'c> { + .arg( + Arg::with_name("debug") + .help("turn on debugging information") + .short('d'), + ) + .args(&[ + Arg::with_name("config") + .help("sets the config file to use") + .setting(ArgSettings::TakesValue) + .short('c') + .long("config"), + Arg::with_name("input") + .help("the input file to use") + .index(1) + .setting(ArgSettings::Required), + ]) + .arg("--license 'display the license file'") + .arg("[output] 'Supply an output file to use'") + .arg("-i, --int=[IFACE] 'Set an interface to use'") +} + +fn app_example4<'c>() -> App<'c> { App::new("MyApp") .about("Parses an input file to do awesome things") .version("1.0") .author("Kevin K. ") - .arg(Arg::with_name("debug") - .help("turn on debugging information") - .short("d") - .long("debug")) - .arg(Arg::with_name("config") - .help("sets the config file to use") - .short("c") - .long("config")) - .arg(Arg::with_name("input") - .help("the input file to use") - .index(1) - .required(true)) -} - -fn app_example5<'b, 'c>() -> App<'b, 'c> { - App::new("MyApp").arg(Arg::with_name("awesome") - .help("turns up the awesome") - .short("a") - .long("awesome") - .multiple(true) - .requires("config") - .conflicts_with("output")) -} - -fn app_example6<'b, 'c>() -> App<'b, 'c> { - App::new("MyApp") - .arg(Arg::with_name("input") - .help("the input file to use") - .index(1) + .arg( + Arg::with_name("debug") + .help("turn on debugging information") + .short('d') + .long("debug"), + ) + .arg( + Arg::with_name("config") + .help("sets the config file to use") + .short('c') + .long("config"), + ) + .arg( + Arg::with_name("input") + .help("the input file to use") + .index(1) + .setting(ArgSettings::Required), + ) +} + +fn app_example5<'c>() -> App<'c> { + App::new("MyApp").arg( + Arg::with_name("awesome") + .help("turns up the awesome") + .short('a') + .long("awesome") + .setting(ArgSettings::MultipleOccurrences) .requires("config") - .conflicts_with("output") - .required(true)) - .arg(Arg::with_name("config") - .help("the config file to use") - .index(2)) + .conflicts_with("output"), + ) } -fn app_example7<'b, 'c>() -> App<'b, 'c> { +fn app_example6<'c>() -> App<'c> { + App::new("MyApp") + .arg( + Arg::with_name("input") + .help("the input file to use") + .index(1) + .requires("config") + .conflicts_with("output") + .setting(ArgSettings::Required), + ) + .arg( + Arg::with_name("config") + .help("the config file to use") + .index(2), + ) +} + +fn app_example7<'c>() -> App<'c> { App::new("MyApp") .arg(Arg::with_name("config")) .arg(Arg::with_name("output")) - .arg(Arg::with_name("input") - .help("the input file to use") - .takes_value(true) - .short("i") - .long("input") - .multiple(true) - .required(true) - .requires("config") - .conflicts_with("output")) -} - -fn app_example8<'b, 'c>() -> App<'b, 'c> { + .arg( + Arg::with_name("input") + .help("the input file to use") + .settings(&[ + ArgSettings::MultipleValues, + ArgSettings::MultipleOccurrences, + ArgSettings::Required, + ]) + .short('i') + .long("input") + .requires("config") + .conflicts_with("output"), + ) +} + +fn app_example8<'c>() -> App<'c> { App::new("MyApp") .arg(Arg::with_name("config")) .arg(Arg::with_name("output")) - .arg(Arg::with_name("input") - .help("the input file to use") - .takes_value(true) - .short("i") - .long("input") - .multiple(true) - .required(true) - .requires("config") - .conflicts_with("output")) -} - -fn app_example10<'b, 'c>() -> App<'b, 'c> { - App::new("myapp") - .about("does awesome things") - .arg(Arg::with_name("CONFIG") + .arg( + Arg::with_name("input") + .help("the input file to use") + .settings(&[ + ArgSettings::MultipleValues, + ArgSettings::MultipleOccurrences, + ArgSettings::Required, + ]) + .short('i') + .long("input") + .requires("config") + .conflicts_with("output"), + ) +} + +fn app_example10<'c>() -> App<'c> { + App::new("myapp").about("does awesome things").arg( + Arg::with_name("CONFIG") .help("The config file to use (default is \"config.json\")") - .short("c") - .takes_value(true)) + .short('c') + .setting(ArgSettings::TakesValue), + ) } #[bench] fn example1(b: &mut Bencher) { - let app = app_example1(); - b.iter(|| build_help(&app)); + let mut app = app_example1(); + b.iter(|| build_help(&mut app)); } #[bench] fn example2(b: &mut Bencher) { - let app = app_example2(); - b.iter(|| build_help(&app)); + let mut app = app_example2(); + b.iter(|| build_help(&mut app)); } #[bench] fn example3(b: &mut Bencher) { - let app = app_example3(); - b.iter(|| build_help(&app)); + let mut app = app_example3(); + b.iter(|| build_help(&mut app)); } #[bench] fn example4(b: &mut Bencher) { - let app = app_example4(); - b.iter(|| build_help(&app)); + let mut app = app_example4(); + b.iter(|| build_help(&mut app)); } #[bench] fn example5(b: &mut Bencher) { - let app = app_example5(); - b.iter(|| build_help(&app)); + let mut app = app_example5(); + b.iter(|| build_help(&mut app)); } #[bench] fn example6(b: &mut Bencher) { - let app = app_example6(); - b.iter(|| build_help(&app)); + let mut app = app_example6(); + b.iter(|| build_help(&mut app)); } #[bench] fn example7(b: &mut Bencher) { - let app = app_example7(); - b.iter(|| build_help(&app)); + let mut app = app_example7(); + b.iter(|| build_help(&mut app)); } #[bench] fn example8(b: &mut Bencher) { - let app = app_example8(); - b.iter(|| build_help(&app)); + let mut app = app_example8(); + b.iter(|| build_help(&mut app)); } #[bench] fn example10(b: &mut Bencher) { - let app = app_example10(); - b.iter(|| build_help(&app)); + let mut app = app_example10(); + b.iter(|| build_help(&mut app)); } #[bench] fn example4_template(b: &mut Bencher) { - let app = app_example4().template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nFLAGS:\n{flags}\n\nARGS:\n{args}\n"); - b.iter(|| build_help(&app)); + let mut app = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nFLAGS:\n{flags}\n\nARGS:\n{args}\n"); + b.iter(|| build_help(&mut app)); } diff --git a/benches/05_ripgrep.rs b/benches/05_ripgrep.rs index 7db1552550b1..8b11713393f0 100644 --- a/benches/05_ripgrep.rs +++ b/benches/05_ripgrep.rs @@ -6,15 +6,15 @@ #![feature(test)] extern crate clap; -extern crate test; #[macro_use] extern crate lazy_static; +extern crate test; use clap::{App, AppSettings, Arg, ArgSettings}; -use test::Bencher; use std::collections::HashMap; use std::io::Cursor; +use test::Bencher; #[bench] fn build_app_short(b: &mut Bencher) { b.iter(|| app_short()); } @@ -24,14 +24,14 @@ fn build_app_long(b: &mut Bencher) { b.iter(|| app_long()); } #[bench] fn build_help_short(b: &mut Bencher) { - let app = app_short(); - b.iter(|| build_help(&app)); + let mut app = app_short(); + b.iter(|| build_help(&mut app)); } #[bench] fn build_help_long(b: &mut Bencher) { - let app = app_long(); - b.iter(|| build_help(&app)); + let mut app = app_long(); + b.iter(|| build_help(&mut app)); } #[bench] @@ -40,244 +40,187 @@ fn parse_clean(b: &mut Bencher) { b.iter(|| app_short().get_matches_from(vec!["r #[bench] fn parse_complex(b: &mut Bencher) { b.iter(|| { - app_short().get_matches_from(vec!["rg", - "pat", - "-cFlN", - "-pqr=some", - "--null", - "--no-filename", - "--no-messages", - "-SH", - "-C5", - "--follow", - "-e some"]) + app_short().get_matches_from(vec![ + "rg", + "pat", + "-cFlN", + "-pqr=some", + "--null", + "--no-filename", + "--no-messages", + "-SH", + "-C5", + "--follow", + "-e some", + ]) }); } #[bench] fn parse_lots(b: &mut Bencher) { b.iter(|| { - app_short() - .get_matches_from(vec!["rg", "pat", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some", "some", "some", "some", "some", - "some", "some", "some", "some"]) + app_short().get_matches_from(vec![ + "rg", "pat", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", + "some", "some", "some", "some", "some", "some", "some", + ]) }); } @@ -312,21 +255,19 @@ OPTIONS: {unified}"; /// Build a clap application with short help strings. -pub fn app_short() -> App<'static, 'static> { app(false, |k| USAGES[k].short) } +pub fn app_short() -> App<'static> { app(false, |k| USAGES[k].short) } /// Build a clap application with long help strings. -pub fn app_long() -> App<'static, 'static> { app(true, |k| USAGES[k].long) } - +pub fn app_long() -> App<'static> { app(true, |k| USAGES[k].long) } /// Build the help text of an application. -fn build_help(app: &App) -> String { +fn build_help(app: &mut App) -> String { let mut buf = Cursor::new(Vec::with_capacity(50)); app.write_help(&mut buf).unwrap(); let content = buf.into_inner(); String::from_utf8(content).unwrap() } - /// Build a clap application parameterized by usage strings. /// /// The function given should take a clap argument name and return a help @@ -334,10 +275,11 @@ fn build_help(app: &App) -> String { /// /// This is an intentionally stand-alone module so that it can be used easily /// in a `build.rs` script to build shell completion files. -fn app(next_line_help: bool, doc: F) -> App<'static, 'static> - where F: Fn(&'static str) -> &'static str +fn app(_next_line_help: bool, doc: F) -> App<'static> +where + F: Fn(&'static str) -> &'static str, { - let arg = |name| Arg::with_name(name).help(doc(name)).next_line_help(next_line_help); + let arg = |name| Arg::with_name(name).help(doc(name)); let flag = |name| arg(name).long(name); App::new("ripgrep") @@ -346,92 +288,141 @@ fn app(next_line_help: bool, doc: F) -> App<'static, 'static> .about(ABOUT) .max_term_width(100) .setting(AppSettings::UnifiedHelpMessage) - .usage(USAGE) - .template(TEMPLATE) + .override_usage(USAGE) + .help_template(TEMPLATE) // Handle help/version manually to make their output formatting // consistent with short/long views. - .arg(arg("help-short").short("h")) + .arg(arg("help-short").short('h')) .arg(flag("help")) - .arg(flag("version").short("V")) + .arg(flag("version").short('V')) // First, set up primary positional/flag arguments. - .arg(arg("pattern") - .required_unless_one(&[ - "file", "files", "help-short", "help", "regexp", "type-list", - "version", - ])) - .arg(arg("path").multiple(true)) - .arg(flag("regexp").short("e") - .takes_value(true).multiple(true).number_of_values(1) - .set(ArgSettings::AllowLeadingHyphen) - .value_name("pattern")) - .arg(flag("files") - // This should also conflict with `pattern`, but the first file - // path will actually be in `pattern`. - .conflicts_with_all(&["file", "regexp", "type-list"])) - .arg(flag("type-list") - .conflicts_with_all(&["file", "files", "pattern", "regexp"])) + .arg(arg("pattern").required_unless_one(&[ + "file", + "files", + "help-short", + "help", + "regexp", + "type-list", + "version", + ])) + .arg( + arg("path") + .setting(ArgSettings::MultipleValues) + .setting(ArgSettings::MultipleOccurrences), + ) + .arg( + flag("regexp") + .short('e') + .settings(&[ + ArgSettings::AllowHyphenValues, + ArgSettings::MultipleOccurrences, + ArgSettings::TakesValue, + ]) + .value_name("pattern"), + ) + .arg( + flag("files") + // This should also conflict with `pattern`, but the first file + // path will actually be in `pattern`. + .conflicts_with_all(&["file", "regexp", "type-list"]), + ) + .arg(flag("type-list").conflicts_with_all(&["file", "files", "pattern", "regexp"])) // Second, set up common flags. - .arg(flag("text").short("a")) - .arg(flag("count").short("c")) - .arg(flag("color") - .value_name("WHEN") - .takes_value(true) - .hide_possible_values(true) - .possible_values(&["never", "auto", "always", "ansi"])) - .arg(flag("colors").value_name("SPEC") - .takes_value(true).multiple(true).number_of_values(1)) - .arg(flag("fixed-strings").short("F")) - .arg(flag("glob").short("g") - .takes_value(true).multiple(true).number_of_values(1) - .value_name("GLOB")) - .arg(flag("ignore-case").short("i")) - .arg(flag("line-number").short("n")) - .arg(flag("no-line-number").short("N")) - .arg(flag("quiet").short("q")) - .arg(flag("type").short("t") - .takes_value(true).multiple(true).number_of_values(1) - .value_name("TYPE")) - .arg(flag("type-not").short("T") - .takes_value(true).multiple(true).number_of_values(1) - .value_name("TYPE")) - .arg(flag("unrestricted").short("u") - .multiple(true)) - .arg(flag("invert-match").short("v")) - .arg(flag("word-regexp").short("w")) + .arg(flag("text").short('a')) + .arg(flag("count").short('c')) + .arg( + flag("color") + .value_name("WHEN") + .setting(ArgSettings::HidePossibleValues) + .possible_values(&["never", "auto", "always", "ansi"]), + ) + .arg( + flag("colors") + .value_name("SPEC") + .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]), + ) + .arg(flag("fixed-strings").short('F')) + .arg( + flag("glob") + .short('g') + .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]) + .value_name("GLOB"), + ) + .arg(flag("ignore-case").short('i')) + .arg(flag("line-number").short('n')) + .arg(flag("no-line-number").short('N')) + .arg(flag("quiet").short('q')) + .arg( + flag("type") + .short('t') + .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]) + .value_name("TYPE"), + ) + .arg( + flag("type-not") + .short('T') + .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]) + .value_name("TYPE"), + ) + .arg( + flag("unrestricted") + .short('u') + .setting(ArgSettings::MultipleOccurrences), + ) + .arg(flag("invert-match").short('v')) + .arg(flag("word-regexp").short('w')) // Third, set up less common flags. - .arg(flag("after-context").short("A") - .value_name("NUM").takes_value(true) - .validator(validate_number)) - .arg(flag("before-context").short("B") - .value_name("NUM").takes_value(true) - .validator(validate_number)) - .arg(flag("context").short("C") - .value_name("NUM").takes_value(true) - .validator(validate_number)) + .arg( + flag("after-context") + .short('A') + .value_name("NUM") + .validator(validate_number), + ) + .arg( + flag("before-context") + .short('B') + .value_name("NUM") + .validator(validate_number), + ) + .arg( + flag("context") + .short('C') + .value_name("NUM") + .validator(validate_number), + ) .arg(flag("column")) - .arg(flag("context-separator") - .value_name("SEPARATOR").takes_value(true)) + .arg(flag("context-separator").value_name("SEPARATOR")) .arg(flag("debug")) - .arg(flag("file").short("f") - .value_name("FILE").takes_value(true) - .multiple(true).number_of_values(1)) - .arg(flag("files-with-matches").short("l")) + .arg( + flag("file") + .short('f') + .value_name("FILE") + .setting(ArgSettings::MultipleOccurrences), + ) + .arg(flag("files-with-matches").short('l')) .arg(flag("files-without-match")) - .arg(flag("with-filename").short("H")) + .arg(flag("with-filename").short('H')) .arg(flag("no-filename")) .arg(flag("heading").overrides_with("no-heading")) .arg(flag("no-heading").overrides_with("heading")) .arg(flag("hidden")) - .arg(flag("ignore-file") - .value_name("FILE").takes_value(true) - .multiple(true).number_of_values(1)) - .arg(flag("follow").short("L")) - .arg(flag("max-count") - .short("m").value_name("NUM").takes_value(true) - .validator(validate_number)) - .arg(flag("maxdepth") - .value_name("NUM").takes_value(true) - .validator(validate_number)) + .arg( + flag("ignore-file") + .value_name("FILE") + .setting(ArgSettings::MultipleOccurrences), + ) + .arg(flag("follow").short('L')) + .arg( + flag("max-count") + .short('m') + .value_name("NUM") + .validator(validate_number), + ) + .arg( + flag("maxdepth") + .value_name("NUM") + .validator(validate_number), + ) .arg(flag("mmap")) .arg(flag("no-messages")) .arg(flag("no-mmap")) @@ -439,22 +430,29 @@ fn app(next_line_help: bool, doc: F) -> App<'static, 'static> .arg(flag("no-ignore-parent")) .arg(flag("no-ignore-vcs")) .arg(flag("null")) - .arg(flag("path-separator").value_name("SEPARATOR").takes_value(true)) - .arg(flag("pretty").short("p")) - .arg(flag("replace").short("r").value_name("ARG").takes_value(true)) - .arg(flag("case-sensitive").short("s")) - .arg(flag("smart-case").short("S")) + .arg(flag("path-separator").value_name("SEPARATOR")) + .arg(flag("pretty").short('p')) + .arg(flag("replace").short('r').value_name("ARG")) + .arg(flag("case-sensitive").short('s')) + .arg(flag("smart-case").short('S')) .arg(flag("sort-files")) - .arg(flag("threads") - .short("j").value_name("ARG").takes_value(true) - .validator(validate_number)) + .arg( + flag("threads") + .short('j') + .value_name("ARG") + .validator(validate_number), + ) .arg(flag("vimgrep")) - .arg(flag("type-add") - .value_name("TYPE").takes_value(true) - .multiple(true).number_of_values(1)) - .arg(flag("type-clear") - .value_name("TYPE").takes_value(true) - .multiple(true).number_of_values(1)) + .arg( + flag("type-add") + .value_name("TYPE") + .setting(ArgSettings::MultipleOccurrences), + ) + .arg( + flag("type-clear") + .value_name("TYPE") + .setting(ArgSettings::MultipleOccurrences), + ) } struct Usage { @@ -467,311 +465,458 @@ macro_rules! doc { doc!($map, $name, $short, $short) }; ($map:expr, $name:expr, $short:expr, $long:expr) => { - $map.insert($name, Usage { - short: $short, - long: concat!($long, "\n "), - }); + $map.insert( + $name, + Usage { + short: $short, + long: concat!($long, "\n "), + }, + ); }; } lazy_static! { static ref USAGES: HashMap<&'static str, Usage> = { let mut h = HashMap::new(); - doc!(h, "help-short", - "Show short help output.", - "Show short help output. Use --help to show more details."); - doc!(h, "help", - "Show verbose help output.", - "When given, more details about flags are provided."); - doc!(h, "version", - "Prints version information."); - - doc!(h, "pattern", - "A regular expression used for searching.", - "A regular expression used for searching. Multiple patterns \ - may be given. To match a pattern beginning with a -, use [-]."); - doc!(h, "regexp", - "A regular expression used for searching.", - "A regular expression used for searching. Multiple patterns \ - may be given. To match a pattern beginning with a -, use [-]."); - doc!(h, "path", - "A file or directory to search.", - "A file or directory to search. Directories are searched \ - recursively."); - doc!(h, "files", - "Print each file that would be searched.", - "Print each file that would be searched without actually \ - performing the search. This is useful to determine whether a \ - particular file is being searched or not."); - doc!(h, "type-list", - "Show all supported file types.", - "Show all supported file types and their corresponding globs."); - - doc!(h, "text", - "Search binary files as if they were text."); - doc!(h, "count", - "Only show count of matches for each file."); - doc!(h, "color", - "When to use color. [default: auto]", - "When to use color in the output. The possible values are \ - never, auto, always or ansi. The default is auto. When always \ - is used, coloring is attempted based on your environment. When \ - ansi used, coloring is forcefully done using ANSI escape color \ - codes."); - doc!(h, "colors", - "Configure color settings and styles.", - "This flag specifies color settings for use in the output. \ - This flag may be provided multiple times. Settings are applied \ - iteratively. Colors are limited to one of eight choices: \ - red, blue, green, cyan, magenta, yellow, white and black. \ - Styles are limited to nobold, bold, nointense or intense.\n\n\ - The format of the flag is {type}:{attribute}:{value}. {type} \ - should be one of path, line or match. {attribute} can be fg, bg \ - or style. {value} is either a color (for fg and bg) or a text \ - style. A special format, {type}:none, will clear all color \ - settings for {type}.\n\nFor example, the following command will \ - change the match color to magenta and the background color for \ - line numbers to yellow:\n\n\ - rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo."); - doc!(h, "fixed-strings", - "Treat the pattern as a literal string.", - "Treat the pattern as a literal string instead of a regular \ - expression. When this flag is used, special regular expression \ - meta characters such as (){}*+. do not need to be escaped."); - doc!(h, "glob", - "Include or exclude files/directories.", - "Include or exclude files/directories for searching that \ - match the given glob. This always overrides any other \ - ignore logic. Multiple glob flags may be used. Globbing \ - rules match .gitignore globs. Precede a glob with a ! \ - to exclude it."); - doc!(h, "ignore-case", - "Case insensitive search.", - "Case insensitive search. This is overridden by \ - --case-sensitive."); - doc!(h, "line-number", - "Show line numbers.", - "Show line numbers (1-based). This is enabled by default when \ - searching in a tty."); - doc!(h, "no-line-number", - "Suppress line numbers.", - "Suppress line numbers. This is enabled by default when NOT \ - searching in a tty."); - doc!(h, "quiet", - "Do not print anything to stdout.", - "Do not print anything to stdout. If a match is found in a file, \ - stop searching. This is useful when ripgrep is used only for \ - its exit code."); - doc!(h, "type", - "Only search files matching TYPE.", - "Only search files matching TYPE. Multiple type flags may be \ - provided. Use the --type-list flag to list all available \ - types."); - doc!(h, "type-not", - "Do not search files matching TYPE.", - "Do not search files matching TYPE. Multiple type-not flags may \ - be provided. Use the --type-list flag to list all available \ - types."); - doc!(h, "unrestricted", - "Reduce the level of \"smart\" searching.", - "Reduce the level of \"smart\" searching. A single -u \ - won't respect .gitignore (etc.) files. Two -u flags will \ - additionally search hidden files and directories. Three \ - -u flags will additionally search binary files. -uu is \ - roughly equivalent to grep -r and -uuu is roughly \ - equivalent to grep -a -r."); - doc!(h, "invert-match", - "Invert matching.", - "Invert matching. Show lines that don't match given patterns."); - doc!(h, "word-regexp", - "Only show matches surrounded by word boundaries.", - "Only show matches surrounded by word boundaries. This is \ - equivalent to putting \\b before and after all of the search \ - patterns."); - - doc!(h, "after-context", - "Show NUM lines after each match."); - doc!(h, "before-context", - "Show NUM lines before each match."); - doc!(h, "context", - "Show NUM lines before and after each match."); - doc!(h, "column", - "Show column numbers", - "Show column numbers (1-based). This only shows the column \ - numbers for the first match on each line. This does not try \ - to account for Unicode. One byte is equal to one column. This \ - implies --line-number."); - doc!(h, "context-separator", - "Set the context separator string. [default: --]", - "The string used to separate non-contiguous context lines in the \ - output. Escape sequences like \\x7F or \\t may be used. The \ - default value is --."); - doc!(h, "debug", - "Show debug messages.", - "Show debug messages. Please use this when filing a bug report."); - doc!(h, "file", - "Search for patterns from the given file.", - "Search for patterns from the given file, with one pattern per \ - line. When this flag is used or multiple times or in \ - combination with the -e/--regexp flag, then all patterns \ - provided are searched. Empty pattern lines will match all input \ - lines, and the newline is not counted as part of the pattern."); - doc!(h, "files-with-matches", - "Only show the path of each file with at least one match."); - doc!(h, "files-without-match", - "Only show the path of each file that contains zero matches."); - doc!(h, "with-filename", - "Show file name for each match.", - "Prefix each match with the file name that contains it. This is \ - the default when more than one file is searched."); - doc!(h, "no-filename", - "Never show the file name for a match.", - "Never show the file name for a match. This is the default when \ - one file is searched."); - doc!(h, "heading", - "Show matches grouped by each file.", - "This shows the file name above clusters of matches from each \ - file instead of showing the file name for every match. This is \ - the default mode at a tty."); - doc!(h, "no-heading", - "Don't group matches by each file.", - "Don't group matches by each file. If -H/--with-filename is \ - enabled, then file names will be shown for every line matched. \ - This is the default mode when not at a tty."); - doc!(h, "hidden", - "Search hidden files and directories.", - "Search hidden files and directories. By default, hidden files \ - and directories are skipped."); - doc!(h, "ignore-file", - "Specify additional ignore files.", - "Specify additional ignore files for filtering file paths. \ - Ignore files should be in the gitignore format and are matched \ - relative to the current working directory. These ignore files \ - have lower precedence than all other ignore files. When \ - specifying multiple ignore files, earlier files have lower \ - precedence than later files."); - doc!(h, "follow", - "Follow symbolic links."); - doc!(h, "max-count", - "Limit the number of matches.", - "Limit the number of matching lines per file searched to NUM."); - doc!(h, "maxdepth", - "Descend at most NUM directories.", - "Limit the depth of directory traversal to NUM levels beyond \ - the paths given. A value of zero only searches the \ - starting-points themselves.\n\nFor example, \ - 'rg --maxdepth 0 dir/' is a no-op because dir/ will not be \ - descended into. 'rg --maxdepth 1 dir/' will search only the \ - direct children of dir/."); - doc!(h, "mmap", - "Searching using memory maps when possible.", - "Search using memory maps when possible. This is enabled by \ - default when ripgrep thinks it will be faster. Note that memory \ - map searching doesn't currently support all options, so if an \ - incompatible option (e.g., --context) is given with --mmap, \ - then memory maps will not be used."); - doc!(h, "no-messages", - "Suppress all error messages.", - "Suppress all error messages. This is equivalent to redirecting \ - stderr to /dev/null."); - doc!(h, "no-mmap", - "Never use memory maps.", - "Never use memory maps, even when they might be faster."); - doc!(h, "no-ignore", - "Don't respect ignore files.", - "Don't respect ignore files (.gitignore, .ignore, etc.). This \ - implies --no-ignore-parent and --no-ignore-vcs."); - doc!(h, "no-ignore-parent", - "Don't respect ignore files in parent directories.", - "Don't respect ignore files (.gitignore, .ignore, etc.) in \ - parent directories."); - doc!(h, "no-ignore-vcs", - "Don't respect VCS ignore files", - "Don't respect version control ignore files (.gitignore, etc.). \ - This implies --no-ignore-parent. Note that .ignore files will \ - continue to be respected."); - doc!(h, "null", - "Print NUL byte after file names", - "Whenever a file name is printed, follow it with a NUL byte. \ - This includes printing file names before matches, and when \ - printing a list of matching files such as with --count, \ - --files-with-matches and --files. This option is useful for use \ - with xargs."); - doc!(h, "path-separator", - "Path separator to use when printing file paths.", - "The path separator to use when printing file paths. This \ - defaults to your platform's path separator, which is / on Unix \ - and \\ on Windows. This flag is intended for overriding the \ - default when the environment demands it (e.g., cygwin). A path \ - separator is limited to a single byte."); - doc!(h, "pretty", - "Alias for --color always --heading -n."); - doc!(h, "replace", - "Replace matches with string given.", - "Replace every match with the string given when printing \ - results. Neither this flag nor any other flag will modify your \ - files.\n\nCapture group indices (e.g., $5) and names \ - (e.g., $foo) are supported in the replacement string.\n\n\ - Note that the replacement by default replaces each match, and \ - NOT the entire line. To replace the entire line, you should \ - match the entire line."); - doc!(h, "case-sensitive", - "Search case sensitively.", - "Search case sensitively. This overrides -i/--ignore-case and \ - -S/--smart-case."); - doc!(h, "smart-case", - "Smart case search.", - "Searches case insensitively if the pattern is all lowercase. \ - Search case sensitively otherwise. This is overridden by \ - either -s/--case-sensitive or -i/--ignore-case."); - doc!(h, "sort-files", - "Sort results by file path. Implies --threads=1.", - "Sort results by file path. Note that this currently \ - disables all parallelism and runs search in a single thread."); - doc!(h, "threads", - "The approximate number of threads to use.", - "The approximate number of threads to use. A value of 0 (which \ - is the default) causes ripgrep to choose the thread count \ - using heuristics."); - doc!(h, "vimgrep", - "Show results in vim compatible format.", - "Show results with every match on its own line, including \ - line numbers and column numbers. With this option, a line with \ - more than one match will be printed more than once."); - - doc!(h, "type-add", - "Add a new glob for a file type.", - "Add a new glob for a particular file type. Only one glob can be \ - added at a time. Multiple --type-add flags can be provided. \ - Unless --type-clear is used, globs are added to any existing \ - globs defined inside of ripgrep.\n\nNote that this MUST be \ - passed to every invocation of ripgrep. Type settings are NOT \ - persisted.\n\nExample: \ - rg --type-add 'foo:*.foo' -tfoo PATTERN.\n\n\ - --type-add can also be used to include rules from other types \ - with the special include directive. The include directive \ - permits specifying one or more other type names (separated by a \ - comma) that have been defined and its rules will automatically \ - be imported into the type specified. For example, to create a \ - type called src that matches C++, Python and Markdown files, one \ - can use:\n\n\ - --type-add 'src:include:cpp,py,md'\n\n\ - Additional glob rules can still be added to the src type by \ - using the --type-add flag again:\n\n\ - --type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'\n\n\ - Note that type names must consist only of Unicode letters or \ - numbers. Punctuation characters are not allowed."); - doc!(h, "type-clear", - "Clear globs for given file type.", - "Clear the file type globs previously defined for TYPE. This \ - only clears the default type definitions that are found inside \ - of ripgrep.\n\nNote that this MUST be passed to every \ - invocation of ripgrep. Type settings are NOT persisted."); + doc!( + h, + "help-short", + "Show short help output.", + "Show short help output. Use --help to show more details." + ); + doc!( + h, + "help", + "Show verbose help output.", + "When given, more details about flags are provided." + ); + doc!(h, "version", "Prints version information."); + + doc!( + h, + "pattern", + "A regular expression used for searching.", + "A regular expression used for searching. Multiple patterns \ + may be given. To match a pattern beginning with a -, use [-]." + ); + doc!( + h, + "regexp", + "A regular expression used for searching.", + "A regular expression used for searching. Multiple patterns \ + may be given. To match a pattern beginning with a -, use [-]." + ); + doc!( + h, + "path", + "A file or directory to search.", + "A file or directory to search. Directories are searched \ + recursively." + ); + doc!( + h, + "files", + "Print each file that would be searched.", + "Print each file that would be searched without actually \ + performing the search. This is useful to determine whether a \ + particular file is being searched or not." + ); + doc!( + h, + "type-list", + "Show all supported file types.", + "Show all supported file types and their corresponding globs." + ); + + doc!(h, "text", "Search binary files as if they were text."); + doc!(h, "count", "Only show count of matches for each file."); + doc!( + h, + "color", + "When to use color. [default: auto]", + "When to use color in the output. The possible values are \ + never, auto, always or ansi. The default is auto. When always \ + is used, coloring is attempted based on your environment. When \ + ansi used, coloring is forcefully done using ANSI escape color \ + codes." + ); + doc!( + h, + "colors", + "Configure color settings and styles.", + "This flag specifies color settings for use in the output. \ + This flag may be provided multiple times. Settings are applied \ + iteratively. Colors are limited to one of eight choices: \ + red, blue, green, cyan, magenta, yellow, white and black. \ + Styles are limited to nobold, bold, nointense or intense.\n\n\ + The format of the flag is {type}:{attribute}:{value}. {type} \ + should be one of path, line or match. {attribute} can be fg, bg \ + or style. {value} is either a color (for fg and bg) or a text \ + style. A special format, {type}:none, will clear all color \ + settings for {type}.\n\nFor example, the following command will \ + change the match color to magenta and the background color for \ + line numbers to yellow:\n\n\ + rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo." + ); + doc!( + h, + "fixed-strings", + "Treat the pattern as a literal string.", + "Treat the pattern as a literal string instead of a regular \ + expression. When this flag is used, special regular expression \ + meta characters such as (){}*+. do not need to be escaped." + ); + doc!( + h, + "glob", + "Include or exclude files/directories.", + "Include or exclude files/directories for searching that \ + match the given glob. This always overrides any other \ + ignore logic. Multiple glob flags may be used. Globbing \ + rules match .gitignore globs. Precede a glob with a ! \ + to exclude it." + ); + doc!( + h, + "ignore-case", + "Case insensitive search.", + "Case insensitive search. This is overridden by \ + --case-sensitive." + ); + doc!( + h, + "line-number", + "Show line numbers.", + "Show line numbers (1-based). This is enabled by default when \ + searching in a tty." + ); + doc!( + h, + "no-line-number", + "Suppress line numbers.", + "Suppress line numbers. This is enabled by default when NOT \ + searching in a tty." + ); + doc!( + h, + "quiet", + "Do not print anything to stdout.", + "Do not print anything to stdout. If a match is found in a file, \ + stop searching. This is useful when ripgrep is used only for \ + its exit code." + ); + doc!( + h, + "type", + "Only search files matching TYPE.", + "Only search files matching TYPE. Multiple type flags may be \ + provided. Use the --type-list flag to list all available \ + types." + ); + doc!( + h, + "type-not", + "Do not search files matching TYPE.", + "Do not search files matching TYPE. Multiple type-not flags may \ + be provided. Use the --type-list flag to list all available \ + types." + ); + doc!( + h, + "unrestricted", + "Reduce the level of \"smart\" searching.", + "Reduce the level of \"smart\" searching. A single -u \ + won't respect .gitignore (etc.) files. Two -u flags will \ + additionally search hidden files and directories. Three \ + -u flags will additionally search binary files. -uu is \ + roughly equivalent to grep -r and -uuu is roughly \ + equivalent to grep -a -r." + ); + doc!( + h, + "invert-match", + "Invert matching.", + "Invert matching. Show lines that don't match given patterns." + ); + doc!( + h, + "word-regexp", + "Only show matches surrounded by word boundaries.", + "Only show matches surrounded by word boundaries. This is \ + equivalent to putting \\b before and after all of the search \ + patterns." + ); + + doc!(h, "after-context", "Show NUM lines after each match."); + doc!(h, "before-context", "Show NUM lines before each match."); + doc!(h, "context", "Show NUM lines before and after each match."); + doc!( + h, + "column", + "Show column numbers", + "Show column numbers (1-based). This only shows the column \ + numbers for the first match on each line. This does not try \ + to account for Unicode. One byte is equal to one column. This \ + implies --line-number." + ); + doc!( + h, + "context-separator", + "Set the context separator string. [default: --]", + "The string used to separate non-contiguous context lines in the \ + output. Escape sequences like \\x7F or \\t may be used. The \ + default value is --." + ); + doc!( + h, + "debug", + "Show debug messages.", + "Show debug messages. Please use this when filing a bug report." + ); + doc!( + h, + "file", + "Search for patterns from the given file.", + "Search for patterns from the given file, with one pattern per \ + line. When this flag is used or multiple times or in \ + combination with the -e/--regexp flag, then all patterns \ + provided are searched. Empty pattern lines will match all input \ + lines, and the newline is not counted as part of the pattern." + ); + doc!( + h, + "files-with-matches", + "Only show the path of each file with at least one match." + ); + doc!( + h, + "files-without-match", + "Only show the path of each file that contains zero matches." + ); + doc!( + h, + "with-filename", + "Show file name for each match.", + "Prefix each match with the file name that contains it. This is \ + the default when more than one file is searched." + ); + doc!( + h, + "no-filename", + "Never show the file name for a match.", + "Never show the file name for a match. This is the default when \ + one file is searched." + ); + doc!( + h, + "heading", + "Show matches grouped by each file.", + "This shows the file name above clusters of matches from each \ + file instead of showing the file name for every match. This is \ + the default mode at a tty." + ); + doc!( + h, + "no-heading", + "Don't group matches by each file.", + "Don't group matches by each file. If -H/--with-filename is \ + enabled, then file names will be shown for every line matched. \ + This is the default mode when not at a tty." + ); + doc!( + h, + "hidden", + "Search hidden files and directories.", + "Search hidden files and directories. By default, hidden files \ + and directories are skipped." + ); + doc!( + h, + "ignore-file", + "Specify additional ignore files.", + "Specify additional ignore files for filtering file paths. \ + Ignore files should be in the gitignore format and are matched \ + relative to the current working directory. These ignore files \ + have lower precedence than all other ignore files. When \ + specifying multiple ignore files, earlier files have lower \ + precedence than later files." + ); + doc!(h, "follow", "Follow symbolic links."); + doc!( + h, + "max-count", + "Limit the number of matches.", + "Limit the number of matching lines per file searched to NUM." + ); + doc!( + h, + "maxdepth", + "Descend at most NUM directories.", + "Limit the depth of directory traversal to NUM levels beyond \ + the paths given. A value of zero only searches the \ + starting-points themselves.\n\nFor example, \ + 'rg --maxdepth 0 dir/' is a no-op because dir/ will not be \ + descended into. 'rg --maxdepth 1 dir/' will search only the \ + direct children of dir/." + ); + doc!( + h, + "mmap", + "Searching using memory maps when possible.", + "Search using memory maps when possible. This is enabled by \ + default when ripgrep thinks it will be faster. Note that memory \ + map searching doesn't currently support all options, so if an \ + incompatible option (e.g., --context) is given with --mmap, \ + then memory maps will not be used." + ); + doc!( + h, + "no-messages", + "Suppress all error messages.", + "Suppress all error messages. This is equivalent to redirecting \ + stderr to /dev/null." + ); + doc!( + h, + "no-mmap", + "Never use memory maps.", + "Never use memory maps, even when they might be faster." + ); + doc!( + h, + "no-ignore", + "Don't respect ignore files.", + "Don't respect ignore files (.gitignore, .ignore, etc.). This \ + implies --no-ignore-parent and --no-ignore-vcs." + ); + doc!( + h, + "no-ignore-parent", + "Don't respect ignore files in parent directories.", + "Don't respect ignore files (.gitignore, .ignore, etc.) in \ + parent directories." + ); + doc!( + h, + "no-ignore-vcs", + "Don't respect VCS ignore files", + "Don't respect version control ignore files (.gitignore, etc.). \ + This implies --no-ignore-parent. Note that .ignore files will \ + continue to be respected." + ); + doc!( + h, + "null", + "Print NUL byte after file names", + "Whenever a file name is printed, follow it with a NUL byte. \ + This includes printing file names before matches, and when \ + printing a list of matching files such as with --count, \ + --files-with-matches and --files. This option is useful for use \ + with xargs." + ); + doc!( + h, + "path-separator", + "Path separator to use when printing file paths.", + "The path separator to use when printing file paths. This \ + defaults to your platform's path separator, which is / on Unix \ + and \\ on Windows. This flag is intended for overriding the \ + default when the environment demands it (e.g., cygwin). A path \ + separator is limited to a single byte." + ); + doc!(h, "pretty", "Alias for --color always --heading -n."); + doc!( + h, + "replace", + "Replace matches with string given.", + "Replace every match with the string given when printing \ + results. Neither this flag nor any other flag will modify your \ + files.\n\nCapture group indices (e.g., $5) and names \ + (e.g., $foo) are supported in the replacement string.\n\n\ + Note that the replacement by default replaces each match, and \ + NOT the entire line. To replace the entire line, you should \ + match the entire line." + ); + doc!( + h, + "case-sensitive", + "Search case sensitively.", + "Search case sensitively. This overrides -i/--ignore-case and \ + -S/--smart-case." + ); + doc!( + h, + "smart-case", + "Smart case search.", + "Searches case insensitively if the pattern is all lowercase. \ + Search case sensitively otherwise. This is overridden by \ + either -s/--case-sensitive or -i/--ignore-case." + ); + doc!( + h, + "sort-files", + "Sort results by file path. Implies --threads=1.", + "Sort results by file path. Note that this currently \ + disables all parallelism and runs search in a single thread." + ); + doc!( + h, + "threads", + "The approximate number of threads to use.", + "The approximate number of threads to use. A value of 0 (which \ + is the default) causes ripgrep to choose the thread count \ + using heuristics." + ); + doc!( + h, + "vimgrep", + "Show results in vim compatible format.", + "Show results with every match on its own line, including \ + line numbers and column numbers. With this option, a line with \ + more than one match will be printed more than once." + ); + + doc!( + h, + "type-add", + "Add a new glob for a file type.", + "Add a new glob for a particular file type. Only one glob can be \ + added at a time. Multiple --type-add flags can be provided. \ + Unless --type-clear is used, globs are added to any existing \ + globs defined inside of ripgrep.\n\nNote that this MUST be \ + passed to every invocation of ripgrep. Type settings are NOT \ + persisted.\n\nExample: \ + rg --type-add 'foo:*.foo' -tfoo PATTERN.\n\n\ + --type-add can also be used to include rules from other types \ + with the special include directive. The include directive \ + permits specifying one or more other type names (separated by a \ + comma) that have been defined and its rules will automatically \ + be imported into the type specified. For example, to create a \ + type called src that matches C++, Python and Markdown files, one \ + can use:\n\n\ + --type-add 'src:include:cpp,py,md'\n\n\ + Additional glob rules can still be added to the src type by \ + using the --type-add flag again:\n\n\ + --type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'\n\n\ + Note that type names must consist only of Unicode letters or \ + numbers. Punctuation characters are not allowed." + ); + doc!( + h, + "type-clear", + "Clear globs for given file type.", + "Clear the file type globs previously defined for TYPE. This \ + only clears the default type definitions that are found inside \ + of ripgrep.\n\nNote that this MUST be passed to every \ + invocation of ripgrep. Type settings are NOT persisted." + ); h }; } fn validate_number(s: String) -> Result<(), String> { - s.parse::().map(|_| ()).map_err(|err| err.to_string()) + s.parse::() + .map(|_| ()) + .map_err(|err| err.to_string()) } diff --git a/benches/06_rustup.rs b/benches/06_rustup.rs index f9ba477182ca..adedfa285060 100644 --- a/benches/06_rustup.rs +++ b/benches/06_rustup.rs @@ -7,7 +7,7 @@ extern crate clap; extern crate test; -use clap::{App, AppSettings, Arg, Shell, SubCommand, ArgGroup}; +use clap::{App, AppSettings, Arg, ArgGroup, ArgSettings}; use test::Bencher; @@ -22,7 +22,7 @@ fn parse_subcommands(b: &mut Bencher) { b.iter(|| build_cli().get_matches_from(vec!["rustup override add stable"])); } -pub fn build_cli() -> App<'static, 'static> { +pub fn build_cli() -> App<'static> { App::new("rustup") .version("0.9.0") // Simulating .about("The Rust toolchain installer") @@ -30,220 +30,306 @@ pub fn build_cli() -> App<'static, 'static> { .setting(AppSettings::VersionlessSubcommands) .setting(AppSettings::DeriveDisplayOrder) // .setting(AppSettings::SubcommandRequiredElseHelp) - .arg(Arg::with_name("verbose") - .help("Enable verbose output") - .short("v") - .long("verbose")) - .subcommand(SubCommand::with_name("show") - .about("Show the active and installed toolchains") - .after_help(SHOW_HELP)) - .subcommand(SubCommand::with_name("install") - .about("Update Rust toolchains") - .after_help(TOOLCHAIN_INSTALL_HELP) - .setting(AppSettings::Hidden) // synonym for 'toolchain install' - .arg(Arg::with_name("toolchain") - .required(true))) - .subcommand(SubCommand::with_name("update") - .about("Update Rust toolchains") - .after_help(UPDATE_HELP) - .arg(Arg::with_name("toolchain").required(false)) - .arg(Arg::with_name("no-self-update") - .help("Don't perform self update when running the `rustup` command") - .long("no-self-update") - .takes_value(false) - .hidden(true))) - .subcommand(SubCommand::with_name("default") - .about("Set the default toolchain") - .after_help(DEFAULT_HELP) - .arg(Arg::with_name("toolchain").required(true))) - .subcommand(SubCommand::with_name("toolchain") - .about("Modify or query the installed toolchains") - .after_help(TOOLCHAIN_HELP) - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("list").about("List installed toolchains")) - .subcommand(SubCommand::with_name("install") - .about("Install or update a given toolchain") - .arg(Arg::with_name("toolchain").required(true))) - .subcommand(SubCommand::with_name("uninstall") - .about("Uninstall a toolchain") - .arg(Arg::with_name("toolchain").required(true))) - .subcommand(SubCommand::with_name("link") - .about("Create a custom toolchain by symlinking to a directory") - .arg(Arg::with_name("toolchain").required(true)) - .arg(Arg::with_name("path").required(true))) - .subcommand(SubCommand::with_name("update") - .setting(AppSettings::Hidden) // synonym for 'install' - .arg(Arg::with_name("toolchain") - .required(true))) - .subcommand(SubCommand::with_name("add") - .setting(AppSettings::Hidden) // synonym for 'install' - .arg(Arg::with_name("toolchain") - .required(true))) - .subcommand(SubCommand::with_name("remove") - .setting(AppSettings::Hidden) // synonym for 'uninstall' - .arg(Arg::with_name("toolchain") - .required(true)))) - .subcommand(SubCommand::with_name("target") - .about("Modify a toolchain's supported targets") - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("list") - .about("List installed and available targets") - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true))) - .subcommand(SubCommand::with_name("add") - .about("Add a target to a Rust toolchain") - .arg(Arg::with_name("target").required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true))) - .subcommand(SubCommand::with_name("remove") - .about("Remove a target from a Rust toolchain") - .arg(Arg::with_name("target").required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true))) - .subcommand(SubCommand::with_name("install") - .setting(AppSettings::Hidden) // synonym for 'add' - .arg(Arg::with_name("target") - .required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true))) - .subcommand(SubCommand::with_name("uninstall") - .setting(AppSettings::Hidden) // synonym for 'remove' - .arg(Arg::with_name("target") - .required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true)))) - .subcommand(SubCommand::with_name("component") - .about("Modify a toolchain's installed components") - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("list") - .about("List installed and available components") - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true))) - .subcommand(SubCommand::with_name("add") - .about("Add a component to a Rust toolchain") - .arg(Arg::with_name("component").required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true)) - .arg(Arg::with_name("target") - .long("target") - .takes_value(true))) - .subcommand(SubCommand::with_name("remove") - .about("Remove a component from a Rust toolchain") - .arg(Arg::with_name("component").required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true)) - .arg(Arg::with_name("target") - .long("target") - .takes_value(true)))) - .subcommand(SubCommand::with_name("override") - .about("Modify directory toolchain overrides") - .after_help(OVERRIDE_HELP) - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("list").about("List directory toolchain overrides")) - .subcommand(SubCommand::with_name("set") - .about("Set the override toolchain for a directory") - .arg(Arg::with_name("toolchain").required(true))) - .subcommand(SubCommand::with_name("unset") - .about("Remove the override toolchain for a directory") - .after_help(OVERRIDE_UNSET_HELP) - .arg(Arg::with_name("path") - .long("path") - .takes_value(true) - .help("Path to the directory")) - .arg(Arg::with_name("nonexistent") - .long("nonexistent") - .takes_value(false) - .help("Remove override toolchain for all nonexistent directories"))) - .subcommand(SubCommand::with_name("add") - .setting(AppSettings::Hidden) // synonym for 'set' - .arg(Arg::with_name("toolchain") - .required(true))) - .subcommand(SubCommand::with_name("remove") - .setting(AppSettings::Hidden) // synonym for 'unset' - .about("Remove the override toolchain for a directory") - .arg(Arg::with_name("path") - .long("path") - .takes_value(true)) - .arg(Arg::with_name("nonexistent") - .long("nonexistent") - .takes_value(false) - .help("Remove override toolchain for all nonexistent directories")))) - .subcommand(SubCommand::with_name("run") - .about("Run a command with an environment configured for a given toolchain") - .after_help(RUN_HELP) - .setting(AppSettings::TrailingVarArg) - .arg(Arg::with_name("toolchain").required(true)) - .arg(Arg::with_name("command") - .required(true) - .multiple(true) - .use_delimiter(false))) - .subcommand(SubCommand::with_name("which") - .about("Display which binary will be run for a given command") - .arg(Arg::with_name("command").required(true))) - .subcommand(SubCommand::with_name("doc") - .about("Open the documentation for the current toolchain") - .after_help(DOC_HELP) - .arg(Arg::with_name("book") - .long("book") - .help("The Rust Programming Language book")) - .arg(Arg::with_name("std") - .long("std") - .help("Standard library API documentation")) - .group(ArgGroup::with_name("page").args(&["book", "std"]))) - .subcommand(SubCommand::with_name("man") - .about("View the man page for a given command") - .arg(Arg::with_name("command").required(true)) - .arg(Arg::with_name("toolchain") - .long("toolchain") - .takes_value(true))) - .subcommand(SubCommand::with_name("self") - .about("Modify the rustup installation") - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("update") - .about("Download and install updates to rustup")) - .subcommand(SubCommand::with_name("uninstall") - .about("Uninstall rustup.") - .arg(Arg::with_name("no-prompt").short("y"))) - .subcommand(SubCommand::with_name("upgrade-data") - .about("Upgrade the internal data format."))) - .subcommand(SubCommand::with_name("telemetry") - .about("rustup telemetry commands") - .setting(AppSettings::Hidden) - .setting(AppSettings::VersionlessSubcommands) - .setting(AppSettings::DeriveDisplayOrder) - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("enable").about("Enable rustup telemetry")) - .subcommand(SubCommand::with_name("disable").about("Disable rustup telemetry")) - .subcommand(SubCommand::with_name("analyze").about("Analyze stored telemetry"))) - .subcommand(SubCommand::with_name("set") - .about("Alter rustup settings") - // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("default-host") - .about("The triple used to identify toolchains when not specified") - .arg(Arg::with_name("host_triple").required(true)))) - .subcommand(SubCommand::with_name("completions") - .about("Generate completion scripts for your shell") - .after_help(COMPLETIONS_HELP) - .setting(AppSettings::ArgRequiredElseHelp) - .arg(Arg::with_name("shell").possible_values(&Shell::variants()))) + .arg( + Arg::with_name("verbose") + .help("Enable verbose output") + .short('v') + .long("verbose"), + ) + .subcommand( + App::new("show") + .about("Show the active and installed toolchains") + .after_help(SHOW_HELP), + ) + .subcommand( + App::new("install") + .about("Update Rust toolchains") + .after_help(TOOLCHAIN_INSTALL_HELP) + .setting(AppSettings::Hidden) // synonym for 'toolchain install' + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("update") + .about("Update Rust toolchains") + .after_help(UPDATE_HELP) + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)) + .arg( + Arg::with_name("no-self-update") + .help("Don't perform self update when running the `rustup` command") + .long("no-self-update") + .setting(ArgSettings::Hidden), + ), + ) + .subcommand( + App::new("default") + .about("Set the default toolchain") + .after_help(DEFAULT_HELP) + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("toolchain") + .about("Modify or query the installed toolchains") + .after_help(TOOLCHAIN_HELP) + .setting(AppSettings::DeriveDisplayOrder) + // .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(App::new("list").about("List installed toolchains")) + .subcommand( + App::new("install") + .about("Install or update a given toolchain") + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("uninstall") + .about("Uninstall a toolchain") + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("link") + .about("Create a custom toolchain by symlinking to a directory") + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)) + .arg(Arg::with_name("path").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("update") + .setting(AppSettings::Hidden) // synonym for 'install' + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("add") + .setting(AppSettings::Hidden) // synonym for 'install' + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("remove") + .setting(AppSettings::Hidden) // synonym for 'uninstall' + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ), + ) + .subcommand( + App::new("target") + .about("Modify a toolchain's supported targets") + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + // .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + App::new("list") + .about("List installed and available targets") + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("add") + .about("Add a target to a Rust toolchain") + .arg(Arg::with_name("target").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("remove") + .about("Remove a target from a Rust toolchain") + .arg(Arg::with_name("target").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("install") + .setting(AppSettings::Hidden) // synonym for 'add' + .arg(Arg::with_name("target").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("uninstall") + .setting(AppSettings::Hidden) // synonym for 'remove' + .arg(Arg::with_name("target").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ), + ) + .subcommand( + App::new("component") + .about("Modify a toolchain's installed components") + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + // .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + App::new("list") + .about("List installed and available components") + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("add") + .about("Add a component to a Rust toolchain") + .arg(Arg::with_name("component").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ) + .arg( + Arg::with_name("target") + .long("target") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("remove") + .about("Remove a component from a Rust toolchain") + .arg(Arg::with_name("component").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ) + .arg( + Arg::with_name("target") + .long("target") + .setting(ArgSettings::TakesValue), + ), + ), + ) + .subcommand( + App::new("override") + .about("Modify directory toolchain overrides") + .after_help(OVERRIDE_HELP) + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + // .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(App::new("list").about("List directory toolchain overrides")) + .subcommand( + App::new("set") + .about("Set the override toolchain for a directory") + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("unset") + .about("Remove the override toolchain for a directory") + .after_help(OVERRIDE_UNSET_HELP) + .arg( + Arg::with_name("path") + .long("path") + .setting(ArgSettings::TakesValue) + .help("Path to the directory"), + ) + .arg( + Arg::with_name("nonexistent") + .long("nonexistent") + .help("Remove override toolchain for all nonexistent directories"), + ), + ) + .subcommand( + App::new("add") + .setting(AppSettings::Hidden) // synonym for 'set' + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("remove") + .setting(AppSettings::Hidden) // synonym for 'unset' + .about("Remove the override toolchain for a directory") + .arg( + Arg::with_name("path") + .long("path") + .setting(ArgSettings::TakesValue), + ) + .arg( + Arg::with_name("nonexistent") + .long("nonexistent") + .help("Remove override toolchain for all nonexistent directories"), + ), + ), + ) + .subcommand( + App::new("run") + .about("Run a command with an environment configured for a given toolchain") + .after_help(RUN_HELP) + .setting(AppSettings::TrailingVarArg) + .arg(Arg::with_name("toolchain").setting(ArgSettings::Required)) + .arg(Arg::with_name("command").settings(&[ + ArgSettings::Required, + ArgSettings::MultipleValues, + ArgSettings::MultipleOccurrences, + ])), + ) + .subcommand( + App::new("which") + .about("Display which binary will be run for a given command") + .arg(Arg::with_name("command").setting(ArgSettings::Required)), + ) + .subcommand( + App::new("doc") + .about("Open the documentation for the current toolchain") + .after_help(DOC_HELP) + .arg( + Arg::with_name("book") + .long("book") + .help("The Rust Programming Language book"), + ) + .arg( + Arg::with_name("std") + .long("std") + .help("Standard library API documentation"), + ) + .group(ArgGroup::with_name("page").args(&["book", "std"])), + ) + .subcommand( + App::new("man") + .about("View the man page for a given command") + .arg(Arg::with_name("command").setting(ArgSettings::Required)) + .arg( + Arg::with_name("toolchain") + .long("toolchain") + .setting(ArgSettings::TakesValue), + ), + ) + .subcommand( + App::new("self") + .about("Modify the rustup installation") + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + .subcommand(App::new("update").about("Download and install updates to rustup")) + .subcommand( + App::new("uninstall") + .about("Uninstall rustup.") + .arg(Arg::with_name("no-prompt").short('y')), + ) + .subcommand(App::new("upgrade-data").about("Upgrade the internal data format.")), + ) + .subcommand( + App::new("telemetry") + .about("rustup telemetry commands") + .setting(AppSettings::Hidden) + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + .subcommand(App::new("enable").about("Enable rustup telemetry")) + .subcommand(App::new("disable").about("Disable rustup telemetry")) + .subcommand(App::new("analyze").about("Analyze stored telemetry")), + ) + .subcommand( + App::new("set").about("Alter rustup settings").subcommand( + App::new("default-host") + .about("The triple used to identify toolchains when not specified") + .arg(Arg::with_name("host_triple").setting(ArgSettings::Required)), + ), + ) } static RUSTUP_HELP: &'static str = r" diff --git a/clap-test.rs b/clap-test.rs index 7d57ac4f3083..757f56f17655 100644 --- a/clap-test.rs +++ b/clap-test.rs @@ -5,7 +5,7 @@ mod test { use regex::Regex; - use clap::{App, Arg, SubCommand, ArgGroup}; + use clap::{App, Arg, ArgGroup}; fn compare(l: S, r: S2) -> bool where S: AsRef, @@ -19,7 +19,7 @@ mod test { let right = re.replace_all(&*rs, ""); let b = left == right; if !b { - println!(); + println!(""); println!("--> left"); println!("{}", left); println!("--> right"); @@ -31,17 +31,19 @@ mod test { pub fn compare_output(l: App, args: &str, right: &str, stderr: bool) -> bool { let mut buf = Cursor::new(Vec::with_capacity(50)); - let res = l.get_matches_from_safe(args.split(' ').collect::>()); + let res = l.try_get_matches_from(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()); + assert_eq!(stderr, err.use_stderr(), + "Should Use STDERR failed. Should be {} but is {}", 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 res = l.try_get_matches_from(args.split(' ').collect::>()); let err = res.unwrap_err(); err.write_to(&mut buf).unwrap(); let content = buf.into_inner(); @@ -50,37 +52,36 @@ mod test { compare(&*left, right1) || compare(&*left, right2) } - // Legacy tests from the Python script days + // Legacy tests from the pyhton script days - pub fn complex_app() -> App<'static, 'static> { - let args = "-o --option=[opt]... 'tests options' - [positional] 'tests positionals'"; + pub fn complex_app() -> App<'static> { let opt3_vals = ["fast", "slow"]; let pos3_vals = ["vi", "emacs"]; App::new("clap-test") .version("v1.4.8") .about("tests clap library") .author("Kevin K. ") - .args_from_usage(args) - .arg(Arg::from_usage("-f --flag... 'tests flags'") + .arg("-o --option=[opt]... 'tests options'") + .arg("[positional] 'tests positionals'") + .arg(Arg::from("-f --flag... 'tests flags'") .global(true)) .args(&[ - Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("long-option-2"), - Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), - Arg::from_usage("[positional2] 'tests positionals with exclusions'"), - Arg::from_usage("-O --Option [option3] 'specific vals'").possible_values(&opt3_vals), - Arg::from_usage("[positional3]... 'tests specific values'").possible_values(&pos3_vals), - Arg::from_usage("--multvals [one] [two] 'Tests multiple values, not mult occs'"), - Arg::from_usage("--multvalsmo... [one] [two] 'Tests multiple values, and mult occs'"), - Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), - Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) + Arg::from("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("long-option-2"), + Arg::from("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), + Arg::from("[positional2] 'tests positionals with exclusions'"), + Arg::from("-O --Option [option3] 'specific vals'").possible_values(&opt3_vals), + Arg::from("[positional3]... 'tests specific values'").possible_values(&pos3_vals), + Arg::from("--multvals [one] [two] 'Tests mutliple values, not mult occs'"), + Arg::from("--multvalsmo... [one] [two] 'Tests mutliple values, and mult occs'"), + Arg::from("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), + Arg::from("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) ]) - .subcommand(SubCommand::with_name("subcmd") + .subcommand(App::new("subcmd") .about("tests subcommands") .version("0.1") .author("Kevin K. ") - .arg_from_usage("-o --option [scoption]... 'tests options'") - .arg_from_usage("-s --subcmdarg [subcmdarg] 'tests other args'") - .arg_from_usage("[scpositional] 'tests positionals'")) + .arg("-o --option [scoption]... 'tests options'") + .arg("-s --subcmdarg [subcmdarg] 'tests other args'") + .arg("[scpositional] 'tests positionals'")) } } diff --git a/clap_test/Cargo.toml b/clap_test/Cargo.toml new file mode 100644 index 000000000000..76b0833449dd --- /dev/null +++ b/clap_test/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "clap_test" +version = "0.1.0" +authors = ["Kevin K. "] + +[dependencies] +clap = {path = "../"} +regex = "0.2" \ No newline at end of file diff --git a/clap_test/src/lib.rs b/clap_test/src/lib.rs new file mode 100644 index 000000000000..7da913e6079a --- /dev/null +++ b/clap_test/src/lib.rs @@ -0,0 +1,60 @@ +use std::str; +use std::io::Cursor; + +extern crate clap; +extern crate regex; + +use regex::Regex; + +use clap::App; + +fn compare(l: S, r: S2) -> bool +where + S: AsRef, + S2: AsRef, +{ + let re = Regex::new("\x1b[^m]*m").unwrap(); + // Strip out any mismatching \r character on windows that might sneak in on either side + let ls = l.as_ref().trim().replace("\r", ""); + let rs = r.as_ref().trim().replace("\r", ""); + let left = re.replace_all(&*ls, ""); + let right = re.replace_all(&*rs, ""); + let b = left == right; + if !b { + println!(""); + println!("--> left"); + println!("{}", left); + println!("--> right"); + println!("{}", right); + println!("--") + } + b +} + +pub fn compare_output(l: App, args: &str, right: &str, stderr: bool) -> bool { + let mut buf = Cursor::new(Vec::with_capacity(50)); + let res = l.try_get_matches_from(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(), + "Should Use STDERR failed. Should be {} but is {}", + 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.try_get_matches_from(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) +} diff --git a/etc/count-tests.sh b/etc/count-tests.sh new file mode 100755 index 000000000000..5999d2e688bc --- /dev/null +++ b/etc/count-tests.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +IFS=$'\n' + +touch .tmp.out + +echo -n "Testing" +for TEST in $(find tests/ -type f -name "*.rs" -exec basename {} .rs \;); do + echo -n "." + echo -n -e "$TEST:\t" >> .tmp.out + cargo test --test $TEST 2>&1 | grep -o -e '[0-9]* failed;' >> .tmp.out +done + +echo "Done" +column -t < .tmp.out +rm .tmp.out +unset IFS diff --git a/etc/update-todo.sh b/etc/update-todo.sh new file mode 100755 index 000000000000..c85f7cd23764 --- /dev/null +++ b/etc/update-todo.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +rg -h 1>/dev/null || (echo "ripgrep not found" && false) + +IFS=$'\n' + +mv TODO.md TODO.bak || true +touch TODO.md + +for FILE in $(rg '@TODO' --ignore-file='update-todo.sh' --files-with-matches); do + echo "- [ ] $FILE" >> TODO.md + for LINE in $(rg -noe '@TODO([ @a-zA-Z-]+):?(.*)$' $FILE); do + echo " -[ ] $LINE" >> TODO.md + done; +done; +unset IFS + +rm TODO.bak || true \ No newline at end of file diff --git a/examples/01a_quick_example.rs b/examples/01a_quick_example.rs index c7fa20f90488..4dd1be9a6a46 100644 --- a/examples/01a_quick_example.rs +++ b/examples/01a_quick_example.rs @@ -1,9 +1,8 @@ extern crate clap; -use clap::{App, SubCommand}; +use clap::App; fn main() { - // This example shows how to create an application with several arguments using usage strings, which can be // far less verbose that shown in 01b_QuickExample.rs, but is more readable. The downside is you cannot set // the more advanced configuration options using this method (well...actually you can, you'll see ;) ) @@ -33,16 +32,18 @@ fn main() { // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) // + Used by "$ myapp help" (same functionality as "-h" or "--help") let matches = App::new("MyApp") - .version("1.0") - .author("Kevin K. ") - .about("Does awesome things") - .args_from_usage("-c, --config=[FILE] 'Sets a custom config file' - 'Sets an optional output file' - -d... 'Turn debugging information on'") - .subcommand(SubCommand::with_name("test") - .about("does testing things") - .arg_from_usage("-l, --list 'lists test values'")) - .get_matches(); + .version("1.0") + .author("Kevin K. ") + .about("Does awesome things") + .arg("-c, --config=[FILE] 'Sets a custom config file'") + .arg(" 'Sets an optional output file'") + .arg("-d... 'Turn debugging information on'") + .subcommand( + App::new("test") + .about("does testing things") + .arg("-l, --list 'lists test values'"), + ) + .get_matches(); // You can check the value provided by positional arguments, or option arguments if let Some(o) = matches.value_of("output") { @@ -60,11 +61,11 @@ fn main() { 1 => println!("Debug mode is kind of on"), 2 => println!("Debug mode is on"), 3 | _ => println!("Don't be crazy"), - } + } // You can check for the existence of subcommands, and if found use their // matches just as you would the top level app - if let Some(matches) = matches.subcommand_matches("test") { + if let Some(ref matches) = matches.subcommand_matches("test") { // "$ myapp test" was run if matches.is_present("list") { // "$ myapp test -l" was run @@ -74,6 +75,5 @@ fn main() { } } - // Continued program logic goes here... } diff --git a/examples/01b_quick_example.rs b/examples/01b_quick_example.rs index 7f455a826573..05796e5adb1f 100644 --- a/examples/01b_quick_example.rs +++ b/examples/01b_quick_example.rs @@ -1,15 +1,14 @@ extern crate clap; -use clap::{App, Arg, SubCommand}; +use clap::{App, Arg}; fn main() { - // This method shows the traditional, and slightly more configurable way to set up arguments. This method is // more verbose, but allows setting more configuration options, and even supports easier dynamic generation. // // The example below is functionally identical to the 01a_quick_example.rs and 01c_quick_example.rs // - // *NOTE:* You can actually achieve the best of both worlds by using Arg::from_usage() (instead of Arg::with_name()) + // *NOTE:* You can actually achieve the best of both worlds by using Arg::from() (instead of Arg::with_name()) // and *then* setting any additional properties. // // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) @@ -35,28 +34,34 @@ fn main() { // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) // + Used by "$ myapp help" (same functionality as "-h" or "--help") let matches = App::new("MyApp") - .version("1.0") - .author("Kevin K. ") - .about("Does awesome things") - .arg(Arg::with_name("config") - .short("c") - .long("config") - .value_name("FILE") - .help("Sets a custom config file") - .takes_value(true)) - .arg(Arg::with_name("output") - .help("Sets an optional output file") - .index(1)) - .arg(Arg::with_name("debug") - .short("d") - .multiple(true) - .help("Turn debugging information on")) - .subcommand(SubCommand::with_name("test") - .about("does testing things") - .arg(Arg::with_name("list") - .short("l") - .help("lists test values"))) - .get_matches(); + .version("1.0") + .author("Kevin K. ") + .about("Does awesome things") + .arg( + Arg::with_name("config") + .short('c') + .long("config") + .value_name("FILE") + .help("Sets a custom config file") + .takes_value(true), + ) + .arg( + Arg::with_name("output") + .help("Sets an optional output file") + .index(1), + ) + .arg( + Arg::with_name("debug") + .short('d') + .multiple(true) + .help("Turn debugging information on"), + ) + .subcommand( + App::new("test") + .about("does testing things") + .arg(Arg::with_name("list").short('l').help("lists test values")), + ) + .get_matches(); // You can check the value provided by positional arguments, or option arguments if let Some(o) = matches.value_of("output") { @@ -74,11 +79,11 @@ fn main() { 1 => println!("Debug mode is kind of on"), 2 => println!("Debug mode is on"), 3 | _ => println!("Don't be crazy"), - } + } // You can check for the existence of subcommands, and if found use their // matches just as you would the top level app - if let Some(matches) = matches.subcommand_matches("test") { + if let Some(ref matches) = matches.subcommand_matches("test") { // "$ myapp test" was run if matches.is_present("list") { // "$ myapp test -l" was run @@ -88,6 +93,5 @@ fn main() { } } - // Continued program logic goes here... } diff --git a/examples/01c_quick_example.rs b/examples/01c_quick_example.rs index 071bdc0a10b1..2197a6429caa 100644 --- a/examples/01c_quick_example.rs +++ b/examples/01c_quick_example.rs @@ -42,7 +42,8 @@ fn main() { (author: "Someone E. ") (@arg verbose: -v --verbose "Print test information verbosely") ) - ).get_matches(); + ) + .get_matches(); // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't // required we could have used an 'if let' to conditionally get the value) diff --git a/examples/02_apps.rs b/examples/02_apps.rs index 8e456404742a..29e16e99e282 100644 --- a/examples/02_apps.rs +++ b/examples/02_apps.rs @@ -11,7 +11,7 @@ fn main() { // another option, usage(), which is an exception to the rule. This should only be used when // the default usage string automatically generated by clap doesn't suffice. // - // You also set all the valid arguments your App should accept via the arg(), args(), arg_from_usage() + // You also set all the valid arguments your App should accept via the arg(), args(), arg() // and args_from_usage() (as well as subcommands via the subcommand() and subcommands() methods) which // will be covered later. // diff --git a/examples/03_args.rs b/examples/03_args.rs index c62d57637d5f..e0274cd3c1d9 100644 --- a/examples/03_args.rs +++ b/examples/03_args.rs @@ -5,14 +5,14 @@ use clap::{App, Arg}; fn main() { // Args describe a possible valid argument which may be supplied by the user at runtime. There // are three different types of arguments (flags, options, and positional) as well as a fourth - // special type of argument, called SubCommands (which will be discussed separately). + // special type of argument, called s (which will be discussed separately). // // Args are described in the same manner as Apps using the "builder pattern" with multiple // methods describing various settings for the individual arguments. Or by supplying a "usage" // string. Both methods have their pros and cons. // // Arguments can be added to applications in two manners, one at a time with the arg(), and - // arg_from_usage() method, or multiple arguments at once via a Vec inside the args() method, + // arg() method, or multiple arguments at once via a Vec inside the args() method, // or a single &str describing multiple Args (one per line) supplied to args_from_usage(). // // There are various options which can be set for a given argument, some apply to any of the @@ -26,46 +26,51 @@ fn main() { // safely override "-V" and "-h" to your own arguments, and "--help" and "--version" will still // be automatically generated for you. let matches = App::new("MyApp") - // All application settings go here... - - // A simple "Flag" argument example (i.e. "-d") using the builder pattern - .arg(Arg::with_name("debug") - .help("turn on debugging information") - .short("d")) - - // Two arguments, one "Option" argument (i.e. one that takes a value) such - // as "-c some", and one positional argument (i.e. "myapp some_file") - .args(&[ - Arg::with_name("config") - .help("sets the config file to use") - .takes_value(true) - .short("c") - .long("config"), - Arg::with_name("input") - .help("the input file to use") - .index(1) - .required(true) - ]) - - // *Note* the following two examples are convenience methods, if you wish - // to still get the full configurability of Arg::with_name() and the readability - // of arg_from_usage(), you can instantiate a new Arg with Arg::from_usage() and - // still be able to set all the additional properties, just like Arg::with_name() - // - // - // One "Flag" using a usage string - .arg_from_usage("--license 'display the license file'") - - // Two args, one "Positional", and one "Option" using a usage string - .args_from_usage("[output] 'Supply an output file to use' - -i, --int=[IFACE] 'Set an interface to use'") - .get_matches(); + // All application settings go here... + // A simple "Flag" argument example (i.e. "-d") using the builder pattern + .arg( + Arg::with_name("debug") + .help("turn on debugging information") + .short('d'), + ) + // Two arguments, one "Option" argument (i.e. one that takes a value) such + // as "-c some", and one positional argument (i.e. "myapp some_file") + .args(&[ + Arg::with_name("config") + .help("sets the config file to use") + .takes_value(true) + .short('c') + .long("config"), + Arg::with_name("input") + .help("the input file to use") + .index(1) + .required(true), + ]) + // *Note* the following two examples are convenience methods, if you wish + // to still get the full configurability of Arg::with_name() and the readability + // of arg(), you can instantiate a new Arg with Arg::from() and + // still be able to set all the additional properties, just like Arg::with_name() + // + // + // One "Flag" using a usage string + .arg("--license 'display the license file'") + // Two args, one "Positional", and one "Option" using a usage string + .arg("[output] 'Supply an output file to use'") + .arg("-i, --int=[IFACE] 'Set an interface to use'") + .get_matches(); // Here are some examples of using the arguments defined above. Keep in mind that this is only // an example, and may be somewhat contrived // // First we check if debugging should be on or not - println!("Debugging mode is: {}", if matches.is_present("debug") { "ON" } else { "OFF" }); + println!( + "Debugging mode is: {}", + if matches.is_present("debug") { + "ON" + } else { + "OFF" + } + ); // Next we print the config file we're using, if any was defined with either -c or // --config diff --git a/examples/04_using_matches.rs b/examples/04_using_matches.rs index a0a986f98307..af2e7d045729 100644 --- a/examples/04_using_matches.rs +++ b/examples/04_using_matches.rs @@ -3,7 +3,6 @@ extern crate clap; use clap::{App, Arg}; fn main() { - // Once all App settings (including all arguments) have been set, you call get_matches() which // parses the string provided by the user, and returns all the valid matches to the ones you // specified. @@ -18,37 +17,46 @@ fn main() { // argument which is the input file we want to work with, this will be the only required // argument. let matches = App::new("MyApp") - .about("Parses an input file to do awesome things") - .version("1.0") - .author("Kevin K. ") - .arg(Arg::with_name("debug") - .help("turn on debugging information") - .short("d") - .long("debug")) - .arg(Arg::with_name("config") - .help("sets the config file to use") - .short("c") - .long("config")) - .arg(Arg::with_name("input") - .help("the input file to use") - .index(1) - .required(true)) - .get_matches(); + .about("Parses an input file to do awesome things") + .version("1.0") + .author("Kevin K. ") + .arg( + Arg::with_name("debug") + .help("turn on debugging information") + .short('d') + .long("debug"), + ) + .arg( + Arg::with_name("config") + .help("sets the config file to use") + .short('c') + .long("config"), + ) + .arg( + Arg::with_name("input") + .help("the input file to use") + .index(1) + .required(true), + ) + .get_matches(); // We can find out whether or not debugging was turned on if matches.is_present("debug") { println!("Debugging is turned on"); } - // If we wanted to do some custom initialization based off some configuration file - // provided by the user, we could get the file (A string of the file) - if let Some(file) = matches.value_of("config") { + // If we wanted to some custom initialization based off some configuration file provided + // by the user, we could get the file (A string of the file) + if let Some(ref file) = matches.value_of("config") { println!("Using config file: {}", file); } // Because "input" is required we can safely call unwrap() because had the user NOT // specified a value, clap would have explained the error the user, and exited. - println!("Doing real work with file: {}", matches.value_of("input").unwrap() ); + println!( + "Doing real work with file: {}", + matches.value_of("input").unwrap() + ); // Continued program logic goes here... } diff --git a/examples/05_flag_args.rs b/examples/05_flag_args.rs index a6b894569712..190c81ced40d 100644 --- a/examples/05_flag_args.rs +++ b/examples/05_flag_args.rs @@ -3,45 +3,44 @@ extern crate clap; use clap::{App, Arg}; fn main() { - // Of the three argument types, flags are the most simple. Flags are simple switches which can // be either "on" or "off" // // clap also supports multiple occurrences of flags, the common example is "verbosity" where a // user could want a little information with "-v" or tons of information with "-v -v" or "-vv" let matches = App::new("MyApp") - // Regular App configuration goes here... - - // We'll add a flag that represents an awesome meter... - // - // I'll explain each possible setting that "flags" accept. Keep in mind - // that you DO NOT need to set each of these for every flag, only the ones - // you want for your individual case. - .arg(Arg::with_name("awesome") - .help("turns up the awesome") // Displayed when showing help info - .short("a") // Trigger this arg with "-a" - .long("awesome") // Trigger this arg with "--awesome" - .multiple(true) // This flag should allow multiple - // occurrences such as "-aaa" or "-a -a" - .requires("config") // Says, "If the user uses -a, they MUST - // also use this other 'config' arg too" - // Can also specify a list using - // requires_all(Vec<&str>) - .conflicts_with("output") // Opposite of requires(), says "if the - // user uses -a, they CANNOT use 'output'" - // also has a conflicts_with_all(Vec<&str>) - ) - // NOTE: In order to compile this example, comment out requires() and - // conflicts_with() because we have not defined an "output" or "config" - // argument. - .get_matches(); + // Regular App configuration goes here... + // We'll add a flag that represents an awesome meter... + // + // I'll explain each possible setting that "flags" accept. Keep in mind + // that you DO NOT need to set each of these for every flag, only the ones + // you want for your individual case. + .arg( + Arg::with_name("awesome") + .help("turns up the awesome") // Displayed when showing help info + .short('a') // Trigger this arg with "-a" + .long("awesome") // Trigger this arg with "--awesome" + .multiple(true) // This flag should allow multiple + // occurrences such as "-aaa" or "-a -a" + .requires("config") // Says, "If the user uses -a, they MUST + // also use this other 'config' arg too" + // Can also specifiy a list using + // requires_all(Vec<&str>) + .conflicts_with("output"), // Opposite of requires(), says "if the + // user uses -a, they CANNOT use 'output'" + // also has a conflicts_with_all(Vec<&str>) + ) + // NOTE: In order to compile this example, comment out requires() and + // conflicts_with() because we have not defined an "output" or "config" + // argument. + .get_matches(); // We can find out whether or not awesome was used if matches.is_present("awesome") { println!("Awesomeness is turned on"); } - // If we set the multiple() option of a flag we can check how many times the user specified + // If we set the mutliple() option of a flag we can check how many times the user specified // // Note: if we did not specify the multiple() option, and the user used "awesome" we would get // a 1 (no matter how many times they actually used it), or a 0 if they didn't use it at all diff --git a/examples/06_positional_args.rs b/examples/06_positional_args.rs index 1f29612e9704..799c76126118 100644 --- a/examples/06_positional_args.rs +++ b/examples/06_positional_args.rs @@ -3,44 +3,44 @@ extern crate clap; use clap::{App, Arg}; fn main() { - // Positional arguments are those values after the program name which are not preceded by any // identifier (such as "myapp some_file"). Positionals support many of the same options as // flags, as well as a few additional ones. let matches = App::new("MyApp") - // Regular App configuration goes here... - - // We'll add two positional arguments, a input file, and a config file. - // - // I'll explain each possible setting that "positionals" accept. Keep in - // mind that you DO NOT need to set each of these for every flag, only the - // ones that apply to your individual case. - .arg(Arg::with_name("input") - .help("the input file to use") // Displayed when showing help info - .index(1) // Set the order in which the user must - // specify this argument (Starts at 1) - .requires("config") // Says, "If the user uses "input", they MUST - // also use this other 'config' arg too" - // Can also specify a list using - // requires_all(Vec<&str>) - .conflicts_with("output") // Opposite of requires(), says "if the - // user uses -a, they CANNOT use 'output'" - // also has a conflicts_with_all(Vec<&str>) - .required(true) // By default this argument MUST be present - // NOTE: mutual exclusions take precedence over - // required arguments - ) - .arg(Arg::with_name("config") - .help("the config file to use") - .index(2)) // Note, we do not need to specify required(true) - // if we don't want to, because "input" already - // requires "config" - // Note, we also do not need to specify requires("input") - // because requires lists are automatically two-way - - // NOTE: In order to compile this example, comment out conflicts_with() - // because we have not defined an "output" argument. - .get_matches(); + // Regular App configuration goes here... + // We'll add two positional arguments, a input file, and a config file. + // + // I'll explain each possible setting that "positionals" accept. Keep in + // mind that you DO NOT need to set each of these for every flag, only the + // ones that apply to your individual case. + .arg( + Arg::with_name("input") + .help("the input file to use") // Displayed when showing help info + .index(1) // Set the order in which the user must + // specify this argument (Starts at 1) + .requires("config") // Says, "If the user uses "input", they MUST + // also use this other 'config' arg too" + // Can also specifiy a list using + // requires_all(Vec<&str>) + .conflicts_with("output") // Opposite of requires(), says "if the + // user uses -a, they CANNOT use 'output'" + // also has a conflicts_with_all(Vec<&str>) + .required(true), // By default this argument MUST be present + // NOTE: mutual exclusions take precedence over + // required arguments + ) + .arg( + Arg::with_name("config") + .help("the config file to use") + .index(2), + ) // Note, we do not need to specify required(true) + // if we don't want to, because "input" already + // requires "config" + // Note, we also do not need to specify requires("input") + // because requires lists are automatically two-way + // NOTE: In order to compile this example, comment out conflicts_with() + // because we have not defined an "output" argument. + .get_matches(); // We can find out whether or not "input" or "config" were used if matches.is_present("input") { @@ -48,9 +48,13 @@ fn main() { } // We can also get the values for those arguments - if let Some(in_file) = matches.value_of("input") { + if let Some(ref in_file) = matches.value_of("input") { // It's safe to call unwrap() because of the required options we set above - println!("Doing work with {} and {}", in_file, matches.value_of("config").unwrap()); + println!( + "Doing work with {} and {}", + in_file, + matches.value_of("config").unwrap() + ); } // Continued program logic goes here... } diff --git a/examples/07_option_args.rs b/examples/07_option_args.rs index 85ff0e53d304..27ac0cbc6ee1 100644 --- a/examples/07_option_args.rs +++ b/examples/07_option_args.rs @@ -3,42 +3,41 @@ extern crate clap; use clap::{App, Arg}; fn main() { - // Option arguments are those that take an additional value, such as "-c value". In clap they // support three types of specification, those with short() as "-o some", or those with long() // as "--option value" or "--option=value" // // Options also support a multiple setting, which is discussed in the example below. let matches = App::new("MyApp") - // Regular App configuration goes here... - - // Assume we have an application that accepts an input file via the "-i file" - // or the "--input file" (as well as "--input=file"). - // Below every setting supported by option arguments is discussed. - // NOTE: You DO NOT need to specify each setting, only those which apply - // to your particular case. - .arg(Arg::with_name("input") - .help("the input file to use") // Displayed when showing help info - .takes_value(true) // MUST be set to true in order to be an "option" argument - .short("i") // This argument is triggered with "-i" - .long("input") // This argument is triggered with "--input" - .multiple(true) // Set to true if you wish to allow multiple occurrences - // such as "-i file -i other_file -i third_file" - .required(true) // By default this argument MUST be present - // NOTE: mutual exclusions take precedence over - // required arguments - .requires("config") // Says, "If the user uses "input", they MUST - // also use this other 'config' arg too" - // Can also specify a list using - // requires_all(Vec<&str>) - .conflicts_with("output") // Opposite of requires(), says "if the - // user uses -a, they CANNOT use 'output'" - // also has a conflicts_with_all(Vec<&str>) - ) - // NOTE: In order to compile this example, comment out conflicts_with() - // and requires() because we have not defined an "output" or "config" - // argument. - .get_matches(); + // Regular App configuration goes here... + // Assume we have an application that accepts an input file via the "-i file" + // or the "--input file" (as well as "--input=file"). + // Below every setting supported by option arguments is discussed. + // NOTE: You DO NOT need to specify each setting, only those which apply + // to your particular case. + .arg( + Arg::with_name("input") + .help("the input file to use") // Displayed when showing help info + .takes_value(true) // MUST be set to true in order to be an "option" argument + .short('i') // This argument is triggered with "-i" + .long("input") // This argument is triggered with "--input" + .multiple(true) // Set to true if you wish to allow multiple occurrences + // such as "-i file -i other_file -i third_file" + .required(true) // By default this argument MUST be present + // NOTE: mutual exclusions take precedence over + // required arguments + .requires("config") // Says, "If the user uses "input", they MUST + // also use this other 'config' arg too" + // Can also specifiy a list using + // requires_all(Vec<&str>) + .conflicts_with("output"), // Opposite of requires(), says "if the + // user uses -a, they CANNOT use 'output'" + // also has a conflicts_with_all(Vec<&str>) + ) + // NOTE: In order to compile this example, comment out conflicts_with() + // and requires() because we have not defined an "output" or "config" + // argument. + .get_matches(); // We can find out whether or not "input" was used if matches.is_present("input") { @@ -49,7 +48,7 @@ fn main() { // // NOTE: If we specified multiple(), this will only return the _FIRST_ // occurrence - if let Some(in_file) = matches.value_of("input") { + if let Some(ref in_file) = matches.value_of("input") { println!("An input file: {}", in_file); } @@ -65,7 +64,10 @@ fn main() { // NOTE: Just like with flags, if we did not specify the multiple() setting this will only // return 1 no matter how many times the argument was used (unless it wasn't used at all, in // in which case 0 is returned) - println!("The \"input\" argument was used {} times", matches.occurrences_of("input")); + println!( + "The \"input\" argument was used {} times", + matches.occurrences_of("input") + ); // Continued program logic goes here... } diff --git a/examples/08_subcommands.rs b/examples/08_subcommands.rs index 73bd09830a22..dc584edc5afa 100644 --- a/examples/08_subcommands.rs +++ b/examples/08_subcommands.rs @@ -1,11 +1,10 @@ extern crate clap; -use clap::{App, Arg, SubCommand}; +use clap::{App, Arg}; fn main() { - - // SubCommands function exactly like sub-Apps, because that's exactly what they are. Each - // instance of a SubCommand can have it's own version, author(s), Args, and even it's own + // s function exactly like sub-Apps, because that's exactly what they are. Each + // instance of a can have it's own version, author(s), Args, and even it's own // subcommands. // // # Help and Version @@ -17,23 +16,26 @@ fn main() { // subcommand along with "-h" and "--help" (applies to sub-subcommands as well). // // Just like arg() and args(), subcommands can be specified one at a time via subcommand() or - // multiple ones at once with a Vec provided to subcommands(). + // multiple ones at once with a Vec<> provided to subcommands(). let matches = App::new("MyApp") - // Normal App and Arg configuration goes here... - - // In the following example assume we wanted an application which - // supported an "add" subcommand, this "add" subcommand also took - // one positional argument of a file to add: - .subcommand(SubCommand::with_name("add") // The name we call argument with - .about("Adds files to myapp") // The message displayed in "myapp -h" - // or "myapp help" - .version("0.1") // Subcommands can have independent version - .author("Kevin K.") // And authors - .arg(Arg::with_name("input") // And their own arguments - .help("the file to add") - .index(1) - .required(true))) - .get_matches(); + // Normal App and Arg configuration goes here... + // In the following example assume we wanted an application which + // supported an "add" subcommand, this "add" subcommand also took + // one positional argument of a file to add: + .subcommand( + App::new("add") // The name we call argument with + .about("Adds files to myapp") // The message displayed in "myapp -h" + // or "myapp help" + .version("0.1") // Subcommands can have independent version + .author("Kevin K.") // And authors + .arg( + Arg::with_name("input") // And their own arguments + .help("the file to add") + .index(1) + .required(true), + ), + ) + .get_matches(); // You can check if a subcommand was used like normal if matches.is_present("add") { @@ -41,7 +43,7 @@ fn main() { } // You can get the independent subcommand matches (which function exactly like App matches) - if let Some(matches) = matches.subcommand_matches("add") { + if let Some(ref matches) = matches.subcommand_matches("add") { // Safe to use unwrap() because of the required() option println!("Adding file: {}", matches.value_of("input").unwrap()); } @@ -49,8 +51,8 @@ fn main() { // You can also match on a subcommand's name match matches.subcommand_name() { Some("add") => println!("'myapp add' was used"), - None => println!("No subcommand was used"), - _ => println!("Some other subcommand was used"), + None => println!("No subcommand was used"), + _ => println!("Some other subcommand was used"), } // Continued program logic goes here... diff --git a/examples/09_auto_version.rs b/examples/09_auto_version.rs index dfd221f082b8..ea6d75f964fd 100644 --- a/examples/09_auto_version.rs +++ b/examples/09_auto_version.rs @@ -20,9 +20,9 @@ fn main() { // Thanks to https://github.com/jhelwig for pointing this out App::new("myapp") .about("does awesome things") - // use crate_version! to pull the version number - .version(crate_version!()) - .get_matches(); + // use crate_version! to pull the version number + .version(crate_version!()) + .get_matches(); // running this app with the -V or --version will display whatever version is in your // Cargo.toml, the default being: myapp 0.0.1 diff --git a/examples/10_default_values.rs b/examples/10_default_values.rs index ca509817b35b..16b5722eea3c 100644 --- a/examples/10_default_values.rs +++ b/examples/10_default_values.rs @@ -9,24 +9,28 @@ fn main() { // I'll demo both here. // // First, we'll use clap's Arg::default_value with an "INPUT" file. - let matches = App::new("myapp").about("does awesome things") - .arg(Arg::with_name("INPUT") - .help("The input file to use") // Note, we don't need to specify - // anything like, "Defaults to..." - // because clap will automatically - // generate that for us, and place - // it in the help text - .default_value("input.txt") - .index(1)) - - // Next we'll use the Option::unwrap_or method on this "CONFIG" option - .arg(Arg::with_name("CONFIG") - // Note that we have to manually include some verbiage to the user - // telling them what the default will be. - .help("The config file to use (default is \"config.json\")") - .short("c") - .takes_value(true)) - .get_matches(); + let matches = App::new("myapp") + .about("does awesome things") + .arg( + Arg::with_name("INPUT") + .help("The input file to use") // Note, we don't need to specify + // anything like, "Defaults to..." + // because clap will automatically + // generate that for us, and place + // it in the help text + .default_value("input.txt") + .index(1), + ) + // Next we'll use the Option::unwrap_or method on this "CONFIG" option + .arg( + Arg::with_name("CONFIG") + // Note that we have to manaully include some verbage to the user + // telling them what the default will be. + .help("The config file to use (default is \"config.json\")") + .short('c') + .takes_value(true), + ) + .get_matches(); // It's safe to call unwrap because the value with either be what the user input at runtime // or "input.txt" diff --git a/examples/11_only_specific_values.rs b/examples/11_only_specific_values.rs index 3445218fbec6..73c6cee3e422 100644 --- a/examples/11_only_specific_values.rs +++ b/examples/11_only_specific_values.rs @@ -12,22 +12,25 @@ fn main() { // // For this example, assume you want one positional argument of either "fast" or "slow" // i.e. the only possible ways to run the program are "myprog fast" or "myprog slow" - let matches = App::new("myapp").about("does awesome things") - .arg(Arg::with_name("MODE") - .help("What mode to run the program in") - .index(1) - .possible_values(&["fast", "slow"]) - .required(true)) - .get_matches(); + let matches = App::new("myapp") + .about("does awesome things") + .arg( + Arg::with_name("MODE") + .help("What mode to run the program in") + .index(1) + .possible_values(&["fast", "slow"]) + .required(true), + ) + .get_matches(); // Note, it's safe to call unwrap() because the arg is required match matches.value_of("MODE").unwrap() { "fast" => { // Do fast things... - }, + } "slow" => { // Do slow things... - }, - _ => unreachable!() + } + _ => unreachable!(), } } diff --git a/examples/12_typed_values.rs b/examples/12_typed_values.rs index 3d03e4fd4c50..a59486ecedbc 100644 --- a/examples/12_typed_values.rs +++ b/examples/12_typed_values.rs @@ -26,15 +26,14 @@ fn main() { // **NOTE:** to use the macros, you must include #[macro_use] just above the 'extern crate clap;' // declaration in your crate root. let matches = App::new("myapp") - // Create two arguments, a required positional which accepts multiple values - // and an optional '-l value' - .args_from_usage( - "... 'A sequence of whole positive numbers, i.e. 20 25 30' - -l [len] 'A length to use, defaults to 10 when omitted'") - .get_matches(); + // Create two arguments, a required positional which accepts multiple values + // and an optional '-l value' + .arg("... 'A sequence of whole positive numbers, i.e. 20 25 30'") + .arg("-l [len] 'A length to use, defaults to 10 when omitted'") + .get_matches(); // Here we get a value of type u32 from our optional -l argument. - // If the value provided to len fails to parse, we default to 10 + // If the value provided to len failes to parse, we default to 10 // // Using other methods such as unwrap_or_else(|e| println!("{}",e)) // are possible too. diff --git a/examples/13a_enum_values_automatic.rs b/examples/13a_enum_values_automatic.rs index 1abe5cb9abc9..3c10db0e3a8c 100644 --- a/examples/13a_enum_values_automatic.rs +++ b/examples/13a_enum_values_automatic.rs @@ -18,7 +18,7 @@ use clap::{App, Arg}; // Using arg_enum! is more like traditional enum declarations // // **NOTE:** Only bare variants are supported -arg_enum!{ +arg_enum! { #[derive(Debug)] pub enum Oof { Rab, @@ -27,7 +27,7 @@ arg_enum!{ } } -arg_enum!{ +arg_enum! { #[derive(Debug)] enum Foo { Bar, @@ -40,27 +40,27 @@ fn main() { // Create the application like normal let enum_vals = ["fast", "slow"]; let m = App::new("myapp") - // Use a single positional argument that is required - .arg(Arg::from_usage(" 'The Foo to use'") - .possible_values(&Foo::variants())) - .arg(Arg::from_usage(" 'The speed to use'") - // You can define a list of possible values if you want the values to be - // displayed in the help information. Whether you use possible_values() or - // not, the valid values will ALWAYS be displayed on a failed parse. - .possible_values(&enum_vals)) - // For the second positional, lets not use possible_values() just to show the difference - .arg_from_usage(" 'The Oof to use'") - .get_matches(); + // Use a single positional argument that is required + .arg(Arg::from(" 'The Foo to use'").possible_values(&Foo::variants())) + .arg( + Arg::from(" 'The speed to use'") + // You can define a list of possible values if you want the values to be + // displayed in the help information. Whether you use possible_values() or + // not, the valid values will ALWAYS be displayed on a failed parse. + .possible_values(&enum_vals), + ) + // For the second positional, lets not use possible_values() just to show the difference + .arg(" 'The Oof to use'") + .get_matches(); let t = value_t!(m.value_of("foo"), Foo).unwrap_or_else(|e| e.exit()); let t2 = value_t!(m.value_of("oof"), Oof).unwrap_or_else(|e| e.exit()); - // Now we can use our enum like normal. match t { Foo::Bar => println!("Found a Bar"), Foo::Baz => println!("Found a Baz"), - Foo::Qux => println!("Found a Qux") + Foo::Qux => println!("Found a Qux"), } // Since our Oof derives Debug, we can do this: diff --git a/examples/13b_enum_values_manual.rs b/examples/13b_enum_values_manual.rs index 81ffe5ebdcd8..3cef8d32de9b 100644 --- a/examples/13b_enum_values_manual.rs +++ b/examples/13b_enum_values_manual.rs @@ -15,7 +15,7 @@ enum Vals { Foo, Bar, Baz, - Qux + Qux, } // Implement the trait @@ -28,7 +28,7 @@ impl FromStr for Vals { "Bar" => Ok(Vals::Bar), "Baz" => Ok(Vals::Baz), "Qux" => Ok(Vals::Qux), - _ => Err("no match") + _ => Err("no match"), } } } @@ -36,11 +36,13 @@ impl FromStr for Vals { fn main() { // Create the application like normal let m = App::new("myapp") - // Use a single positional argument that is required - .arg(Arg::from_usage(" 'The type to use'") - // Define the list of possible values - .possible_values(&["Foo", "Bar", "Baz", "Qux"])) - .get_matches(); + // Use a single positional argument that is required + .arg( + Arg::from(" 'The type to use'") + // Define the list of possible values + .possible_values(&["Foo", "Bar", "Baz", "Qux"]), + ) + .get_matches(); let t = value_t!(m, "type", Vals).unwrap_or_else(|e| e.exit()); @@ -49,6 +51,6 @@ fn main() { Vals::Foo => println!("Found a Foo"), Vals::Bar => println!("Found a Bar"), Vals::Baz => println!("Found a Baz"), - Vals::Qux => println!("Found a Qux") + Vals::Qux => println!("Found a Qux"), } } diff --git a/examples/14_groups.rs b/examples/14_groups.rs index e160464c6c45..fe56d3b3a81f 100644 --- a/examples/14_groups.rs +++ b/examples/14_groups.rs @@ -3,11 +3,11 @@ /// instead of having to list each individually, or when you want a rule to apply "any but not all" /// arguments. /// -/// For instance, you can make an entire `ArgGroup` required, this means that one (and *only* one) -/// argument from that group must be present. Using more than one argument from an `ArgGroup` causes +/// For instance, you can make an entire ArgGroup required, this means that one (and *only* one) +/// argument. from that group must be present. Using more than one argument from an ArgGroup causes /// a failure (graceful exit). /// -/// You can also do things such as name an `ArgGroup` as a confliction or requirement, meaning any +/// You can also do things such as name an ArgGroup as a confliction or requirement, meaning any /// of the arguments that belong to that group will cause a failure if present, or must present /// respectively. /// @@ -20,7 +20,6 @@ /// of the three numbers. So you create three flags `--major`, `--minor`, and `--patch`. All of /// these arguments shouldn't be used at one time but you want to specify that *at least one* of /// them is used. For this, you can create a group. - extern crate clap; use clap::{App, Arg, ArgGroup}; @@ -28,28 +27,30 @@ use clap::{App, Arg, ArgGroup}; fn main() { // Create application like normal let matches = App::new("myapp") - // Add the version arguments - .args_from_usage("--set-ver [ver] 'set version manually' - --major 'auto inc major' - --minor 'auto inc minor' - --patch 'auto inc patch'") - // Create a group, make it required, and add the above arguments - .group(ArgGroup::with_name("vers") - .required(true) - .args(&["ver", "major", "minor", "patch"])) - // Arguments can also be added to a group individually, these two arguments - // are part of the "input" group which is not required - .arg(Arg::from_usage("[INPUT_FILE] 'some regular input'") - .group("input")) - .arg(Arg::from_usage("--spec-in [SPEC_IN] 'some special input argument'") - .group("input")) - // Now let's assume we have a -c [config] argument which requires one of - // (but **not** both) the "input" arguments - .arg(Arg::with_name("config") - .short("c") - .takes_value(true) - .requires("input")) - .get_matches(); + // Add the version arguments + .arg("--set-ver [ver] 'set version manually'") + .arg("--major 'auto inc major'") + .arg("--minor 'auto inc minor'") + .arg("--patch 'auto inc patch'") + // Create a group, make it required, and add the above arguments + .group( + ArgGroup::with_name("vers") + .required(true) + .args(&["ver", "major", "minor", "patch"]), + ) + // Arguments can also be added to a group individually, these two arguments + // are part of the "input" group which is not required + .arg(Arg::from("[INPUT_FILE] 'some regular input'").group("input")) + .arg(Arg::from("--spec-in [SPEC_IN] 'some special input argument'").group("input")) + // Now let's assume we have a -c [config] argument which requires one of + // (but **not** both) the "input" arguments + .arg( + Arg::with_name("config") + .short('c') + .takes_value(true) + .requires("input"), + ) + .get_matches(); // Let's assume the old version 1.2.3 let mut major = 1; @@ -61,14 +62,16 @@ fn main() { format!("{}", ver) } else { // Increment the one requested (in a real program, we'd reset the lower numbers) - let (maj, min, pat) = (matches.is_present("major"), - matches.is_present("minor"), - matches.is_present("patch")); + let (maj, min, pat) = ( + matches.is_present("major"), + matches.is_present("minor"), + matches.is_present("patch"), + ); match (maj, min, pat) { (true, _, _) => major += 1, (_, true, _) => minor += 1, (_, _, true) => patch += 1, - _ => unreachable!(), + _ => unreachable!(), }; format!("{}.{}.{}", major, minor, patch) }; @@ -77,11 +80,13 @@ fn main() { // Check for usage of -c if matches.is_present("config") { - let input = matches.value_of("INPUT_FILE").unwrap_or(matches.value_of("SPEC_IN").unwrap()); - println!("Doing work using input {} and config {}", - input, - matches.value_of("config").unwrap()); + let input = matches + .value_of("INPUT_FILE") + .unwrap_or(matches.value_of("SPEC_IN").unwrap()); + println!( + "Doing work using input {} and config {}", + input, + matches.value_of("config").unwrap() + ); } - - } diff --git a/examples/15_custom_validator.rs b/examples/15_custom_validator.rs index a5c0d42f5ec7..d470b1684628 100644 --- a/examples/15_custom_validator.rs +++ b/examples/15_custom_validator.rs @@ -8,14 +8,16 @@ fn main() { // displayed to the user. let matches = App::new("myapp") - // Application logic goes here... - .arg(Arg::with_name("input") - .help("the input file to use") - .index(1) - .required(true) - // You can pass in a closure, or a function - .validator(is_png)) - .get_matches(); + // Application logic goes here... + .arg( + Arg::with_name("input") + .help("the input file to use") + .index(1) + .required(true) + // You can pass in a closure, or a function + .validator(is_png), + ) + .get_matches(); // Here we can call .unwrap() because the argument is required. println!("The .PNG file is: {}", matches.value_of("input").unwrap()); diff --git a/examples/16_app_settings.rs b/examples/16_app_settings.rs index ab1d1850b9cc..2f4ef0c23e26 100644 --- a/examples/16_app_settings.rs +++ b/examples/16_app_settings.rs @@ -1,6 +1,6 @@ extern crate clap; -use clap::{App, AppSettings, SubCommand}; +use clap::{App, AppSettings}; fn main() { // You can use AppSettings to change the application level behavior of clap. .setting() function @@ -12,20 +12,16 @@ fn main() { // information. let matches = App::new("myapp") - .setting(AppSettings::SubcommandsNegateReqs) - // Negates requirement of parent command. - - .arg_from_usage(" 'input file to use'") - // Required positional argument called input. This - // will be only required if subcommand is not present. - - .subcommand(SubCommand::with_name("test") - .about("does some testing")) - // if program is invoked with subcommand, you do not - // need to specify the argument anymore due to - // the AppSettings::SubcommandsNegateReqs setting. - - .get_matches(); + .setting(AppSettings::SubcommandsNegateReqs) + // Negates requirement of parent command. + .arg(" 'input file to use'") + // Required positional argument called input. This + // will be only required if subcommand is not present. + .subcommand(App::new("test").about("does some testing")) + // if program is invoked with subcommand, you do not + // need to specify the argument anymore due to + // the AppSettings::SubcommandsNegateReqs setting. + .get_matches(); // Calling unwrap() on "input" would not be advised here, because although it's required, // if the user uses a subcommand, those requirements are no longer required. Hence, we should @@ -36,6 +32,6 @@ fn main() { match matches.subcommand() { ("test", _) => println!("The 'test' subcommand was used"), - _ => unreachable!() + _ => unreachable!(), } } diff --git a/examples/17_yaml.rs b/examples/17_yaml.rs index 3353d73a92b9..c2d9a0d7f66f 100644 --- a/examples/17_yaml.rs +++ b/examples/17_yaml.rs @@ -1,4 +1,4 @@ -// In order to use YAML to define your CLI you must compile clap with the "yaml" feature because +// In order to use YAML to define your CLI you must compile clap with the "yaml" feature becasue // it's **not** included by default. // // In order to do this, ensure your Cargo.toml looks like one of the following: @@ -11,9 +11,11 @@ // [dependencies] // clap = { features = ["yaml"] } - // Using yaml requires calling a clap macro `load_yaml!()` so we must use the '#[macro_use]' // directive +// Note: If you're using clap as a dependency and don't have a feature for your users called +// "yaml", you'll need to remove the #[cfg(feature = "yaml")] conditional compilation attribute +#[cfg(feature = "yaml")] #[macro_use] extern crate clap; @@ -23,14 +25,14 @@ fn main() { // To load a yaml file containing our CLI definition such as the example '17_yaml.yml' we can // use the convenience macro which loads the file at compile relative to the current file - // similar to how modules are found. + // similiar to how modules are found. // // Then we pass that yaml object to App to build the CLI. // // Finally we call get_matches() to start the parsing process. We use the matches just as we // normally would let yml = load_yaml!("17_yaml.yml"); - let m = App::from_yaml(yml).get_matches(); + let m = App::from(yml).get_matches(); // Because the example 17_yaml.yml is rather large we'll just look a single arg so you can // see that it works... @@ -38,7 +40,7 @@ fn main() { match mode { "vi" => println!("You are using vi"), "emacs" => println!("You are using emacs..."), - _ => unreachable!() + _ => unreachable!(), } } else { println!("--mode wasn't used..."); diff --git a/examples/17_yaml.yml b/examples/17_yaml.yml index b0d58b3933a6..5f8eb89c6bbf 100644 --- a/examples/17_yaml.yml +++ b/examples/17_yaml.yml @@ -82,7 +82,7 @@ subcommands: help: example subcommand positional index: 1 -# ArgGroups are supported as well, and must be sepcified in the 'groups:' +# ArgGroups are supported as well, and must be specified in the 'groups:' # object of this file groups: # the name of the ArgGoup is specified here diff --git a/examples/18_builder_macro.rs b/examples/18_builder_macro.rs index 6bdce47d330a..ac28f31806d3 100644 --- a/examples/18_builder_macro.rs +++ b/examples/18_builder_macro.rs @@ -5,7 +5,6 @@ extern crate clap; // $crate:: internally fn main() { - // Validation example testing that a file exists let file_exists = |path| { if std::fs::metadata(path).is_ok() { @@ -16,7 +15,7 @@ fn main() { }; // External module may contain this subcommand. If this exists in another module, a function is - // required to access it. Recommend `fn clap() -> Clap::SubCommand`. + // required to access it. Recommend `fn clap() -> Clap::`. let external_sub_command = clap_app!( @subcommand foo => (@arg bar: -b "Bar") ); @@ -41,7 +40,7 @@ fn main() { (@arg list: -l "Lists test values") (@arg test_req: -r requires[list] "Tests requirement for listing") (@arg aaaa: --aaaa +takes_value { - |a| if a.contains('a') { + |a| if a.contains("a") { Ok(()) } else { Err(String::from("string does not contain at least one a")) @@ -70,7 +69,7 @@ fn main() { // You can check for the existence of subcommands, and if found use their // matches just as you would the top level app - if let Some(matches) = matches.subcommand_matches("test") { + if let Some(ref matches) = matches.subcommand_matches("test") { // "$ myapp test" was run if matches.is_present("list") { // "$ myapp test -l" was run diff --git a/examples/19_auto_authors.rs b/examples/19_auto_authors.rs index afbb9853b941..cfb540e8688a 100644 --- a/examples/19_auto_authors.rs +++ b/examples/19_auto_authors.rs @@ -6,9 +6,9 @@ use clap::App; fn main() { App::new("myapp") .about("does awesome things") - // use crate_authors! to pull the author(s) names from the Cargo.toml - .author(crate_authors!()) - .get_matches(); + // use crate_authors! to pull the author(s) names from the Cargo.toml + .author(crate_authors!()) + .get_matches(); // running this app with -h will display whatever author(s) are in your // Cargo.toml diff --git a/examples/20_subcommands.rs b/examples/20_subcommands.rs index f80f46d71350..b0d4b562f765 100644 --- a/examples/20_subcommands.rs +++ b/examples/20_subcommands.rs @@ -1,7 +1,7 @@ // Working with subcommands is simple. There are a few key points to remember when working with -// subcommands in clap. First, SubCommands are really just Apps. This means they can have their own +// subcommands in clap. First, s are really just Apps. This means they can have their own // settings, version, authors, args, and even their own subcommands. The next thing to remember is -// that subcommands are set up in a tree like hierarchy. +// that subcommands are set up in a tree like heirachy. // // An ASCII art depiction may help explain this better. Using a fictional version of git as the demo // subject. Imagine the following are all subcommands of git (note, the author is aware these aren't @@ -30,7 +30,7 @@ // $ git clone url push origin path // // It's also important to know that subcommands each have their own set of matches and may have args -// with the same name as other subcommands in a different part of the tree hierarchy (i.e. the arg +// with the same name as other subcommands in a different part of the tree heirachy (i.e. the arg // names aren't in a flat namespace). // // In order to use subcommands in clap, you only need to know which subcommand you're at in your @@ -41,40 +41,50 @@ extern crate clap; -use clap::{App, Arg, SubCommand, AppSettings}; +use clap::{App, AppSettings, Arg}; fn main() { - let matches = App::new("git") .about("A fictional versioning CLI") .version("1.0") .author("Me") - .subcommand(SubCommand::with_name("clone") - .about("clones repos") - .arg(Arg::with_name("repo") - .help("The repo to clone") - .required(true))) - .subcommand(SubCommand::with_name("push") - .about("pushes things") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("remote") // Subcommands can have their own subcommands, - // which in turn have their own subcommands - .about("pushes remote things") - .arg(Arg::with_name("repo") - .required(true) - .help("The remote repo to push things to"))) - .subcommand(SubCommand::with_name("local") - .about("pushes local things"))) - .subcommand(SubCommand::with_name("add") - .about("adds things") - .author("Someone Else") // Subcommands can list different authors - .version("v2.0 (I'm versioned differently") // or different version from their parents - .setting(AppSettings::ArgRequiredElseHelp) // They can even have different settings - .arg(Arg::with_name("stuff") - .long("stuff") - .help("Stuff to add") - .takes_value(true) - .multiple(true))) + .subcommand( + App::new("clone").about("clones repos").arg( + Arg::with_name("repo") + .help("The repo to clone") + .required(true), + ), + ) + .subcommand( + App::new("push") + .about("pushes things") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + App::new("remote") // Subcommands can have thier own subcommands, + // which in turn have their own subcommands + .about("pushes remote things") + .arg( + Arg::with_name("repo") + .required(true) + .help("The remote repo to push things to"), + ), + ) + .subcommand(App::new("local").about("pushes local things")), + ) + .subcommand( + App::new("add") + .about("adds things") + .author("Someone Else") // Subcommands can list different authors + .version("v2.0 (I'm versioned differently") // or different version from their parents + .setting(AppSettings::ArgRequiredElseHelp) // They can even have different settings + .arg( + Arg::with_name("stuff") + .long("stuff") + .help("Stuff to add") + .takes_value(true) + .multiple(true), + ), + ) .get_matches(); // At this point, the matches we have point to git. Keep this in mind... @@ -92,7 +102,7 @@ fn main() { // grandchildren, great grandchildren, etc. // // i.e. if the command `git push remove --stuff foo` was run, the above will only print out, - // `git push` was used. We'd need to get push's matches to see further into the tree + // `git push` was used. We'd need to get push's matches to see futher into the tree } // An alternative to checking the name is matching on known names. Again notice that only the @@ -101,8 +111,8 @@ fn main() { Some("clone") => println!("'git clone' was used"), Some("push") => println!("'git push' was used"), Some("add") => println!("'git add' was used"), - None => println!("No subcommand was used"), - _ => unreachable!(), // Assuming you've listed all direct children above, this is unreachable + None => println!("No subcommand was used"), + _ => unreachable!(), // Assuming you've listed all direct children above, this is unreachable } // You could get the independent subcommand matches, although this is less common @@ -114,29 +124,36 @@ fn main() { // The most common way to handle subcommands is via a combined approach using // `ArgMatches::subcommand` which returns a tuple of both the name and matches match matches.subcommand() { - ("clone", Some(clone_matches)) =>{ + ("clone", Some(clone_matches)) => { // Now we have a reference to clone's matches println!("Cloning {}", clone_matches.value_of("repo").unwrap()); - }, - ("push", Some(push_matches)) =>{ + } + ("push", Some(push_matches)) => { // Now we have a reference to push's matches match push_matches.subcommand() { - ("remote", Some(remote_matches)) =>{ + ("remote", Some(remote_matches)) => { // Now we have a reference to remote's matches println!("Pushing to {}", remote_matches.value_of("repo").unwrap()); - }, - ("local", Some(_)) =>{ + } + ("local", Some(_)) => { println!("'git push local' was used"); - }, - _ => unreachable!(), + } + _ => unreachable!(), } - }, - ("add", Some(add_matches)) =>{ + } + ("add", Some(add_matches)) => { // Now we have a reference to add's matches - println!("Adding {}", add_matches.values_of("stuff").unwrap().collect::>().join(", ")); - }, - ("", None) => println!("No subcommand was used"), // If no subcommand was used it'll match the tuple ("", None) - _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!() + println!( + "Adding {}", + add_matches + .values_of("stuff") + .unwrap() + .collect::>() + .join(", ") + ); + } + ("", None) => println!("No subcommand was used"), // If no subcommand was usd it'll match the tuple ("", None) + _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!() } // Continued program logic goes here... diff --git a/examples/21_aliases.rs b/examples/21_aliases.rs index 3be04458b505..1fd4b724f707 100644 --- a/examples/21_aliases.rs +++ b/examples/21_aliases.rs @@ -1,21 +1,23 @@ extern crate clap; -use clap::{App, Arg, SubCommand}; +use clap::{App, Arg}; fn main() { - let matches = App::new("MyApp") - .subcommand(SubCommand::with_name("ls") - .aliases(&["list", "dir"]) - .about("Adds files to myapp") - .version("0.1") - .author("Kevin K.") - .arg(Arg::with_name("input") - .help("the file to add") - .index(1) - .required(true)) - ) - .get_matches(); + .subcommand( + App::new("ls") + .aliases(&["list", "dir"]) + .about("Adds files to myapp") + .version("0.1") + .author("Kevin K.") + .arg( + Arg::with_name("input") + .help("the file to add") + .index(1) + .required(true), + ), + ) + .get_matches(); // You can check if a subcommand was used like normal if matches.is_present("add") { @@ -23,7 +25,7 @@ fn main() { } // You can get the independent subcommand matches (which function exactly like App matches) - if let Some(matches) = matches.subcommand_matches("add") { + if let Some(ref matches) = matches.subcommand_matches("add") { // Safe to use unwrap() because of the required() option println!("Adding file: {}", matches.value_of("input").unwrap()); } @@ -31,8 +33,8 @@ fn main() { // You can also match on a subcommand's name match matches.subcommand_name() { Some("add") => println!("'myapp add' was used"), - None => println!("No subcommand was used"), - _ => println!("Some other subcommand was used"), + None => println!("No subcommand was used"), + _ => println!("Some other subcommand was used"), } // Continued program logic goes here... diff --git a/examples/22_stop_parsing_with_--.rs b/examples/22_stop_parsing_with_--.rs index a5ba5b38ff9e..bb0e4fa124c8 100644 --- a/examples/22_stop_parsing_with_--.rs +++ b/examples/22_stop_parsing_with_--.rs @@ -4,22 +4,20 @@ use clap::{App, Arg}; /// myprog -f -p=bob -- sloppy slop slop fn main() { - let matches = App::new("myprog") - .arg(Arg::with_name("eff") - .short("f")) - .arg(Arg::with_name("pea") - .short("p") - .takes_value(true)) - .arg(Arg::with_name("slop") - .multiple(true) - .last(true)) + .arg(Arg::with_name("eff").short('f')) + .arg(Arg::with_name("pea").short('p').takes_value(true)) + .arg(Arg::with_name("slop").multiple(true).last(true)) .get_matches(); - println!("-f used: {:?}", matches.is_present("eff")); println!("-p's value: {:?}", matches.value_of("pea")); - println!("'slops' values: {:?}", matches.values_of("slop").map(|vals| vals.collect::>())); + println!( + "'slops' values: {:?}", + matches + .values_of("slop") + .map(|vals| vals.collect::>()) + ); // Continued program logic goes here... } diff --git a/index.html b/index.html new file mode 100644 index 000000000000..600dff932375 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + diff --git a/justfile b/justfile index 0768764b30c2..7a5ce30f2f0f 100644 --- a/justfile +++ b/justfile @@ -1,3 +1,4 @@ + @update-contributors: echo 'Removing old CONTRIBUTORS.md' mv CONTRIBUTORS.md CONTRIBUTORS.md.bak @@ -5,17 +6,17 @@ echo "the following is a list of contributors:" > CONTRIBUTORS.md echo "" >> CONTRIBUTORS.md echo "" >> CONTRIBUTORS.md - githubcontrib --owner clap-rs --repo clap --sha master --cols 6 --format md --showlogin true --sortBy contributions --sortOrder desc >> CONTRIBUTORS.md + githubcontrib --owner kbknapp --repo clap-rs --sha master --cols 6 --format md --showlogin true --sortBy contributions --sortOrder desc >> CONTRIBUTORS.md echo "" >> CONTRIBUTORS.md echo "" >> CONTRIBUTORS.md echo "This list was generated by [mgechev/github-contributors-list](https://github.com/mgechev/github-contributors-list)" >> CONTRIBUTORS.md rm CONTRIBUTORS.md.bak -run-test TEST: - cargo test --test {{TEST}} +run-test TESTG TEST="": + cargo test --test {{TESTG}} -- {{TEST}} -debug TEST: - cargo test --test {{TEST}} --features debug +debug TESTG TEST="": + cargo test --test {{TESTG}} --features debug -- {{TEST}} run-tests: cargo test --features "yaml unstable" @@ -37,3 +38,24 @@ clean: find . -type f -name "*.orig" -exec rm {} \; find . -type f -name "*.bk" -exec rm {} \; find . -type f -name ".*~" -exec rm {} \; + +top-errors NUM="95": + @cargo check 2>&1 | head -n {{NUM}} + +count-errors: + @cargo check 2>&1 | grep -e '^error' | wc -l + +find-errors: + @cargo check 2>&1 | grep --only-matching -e '-->[^:]*' | sort | uniq -c | sort -nr + +count-warnings: + @cargo check 2>&1 | grep -e '^warning' | wc -l + +find-warnings: + @cargo check 2>&1 | grep -A1 -e 'warning' | grep --only-matching -e '-->[^:]*' | sort | uniq -c | sort -nr + +@update-todo: + ./etc/update-todo.sh + +@count-failures: + ./etc/count-tests.sh diff --git a/rustfmt.toml b/rustfmt.toml index 0136d86e3137..0d83eda4bd3d 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,2 @@ format_strings = false -chain_overflow_last = false -same_line_if_else = true fn_single_line = true diff --git a/src/app/meta.rs b/src/app/meta.rs deleted file mode 100644 index c7f128fe5378..000000000000 --- a/src/app/meta.rs +++ /dev/null @@ -1,33 +0,0 @@ -#[doc(hidden)] -#[allow(missing_debug_implementations)] -#[derive(Default, Clone)] -pub struct AppMeta<'b> { - pub name: String, - pub bin_name: Option, - pub author: Option<&'b str>, - pub version: Option<&'b str>, - pub long_version: Option<&'b str>, - pub about: Option<&'b str>, - pub long_about: Option<&'b str>, - pub more_help: Option<&'b str>, - pub pre_help: Option<&'b str>, - pub aliases: Option>, // (name, visible) - pub usage_str: Option<&'b str>, - pub usage: Option, - pub help_str: Option<&'b str>, - pub disp_ord: usize, - pub term_w: Option, - pub max_w: Option, - pub template: Option<&'b str>, -} - -impl<'b> AppMeta<'b> { - pub fn new() -> Self { Default::default() } - pub fn with_name(s: String) -> Self { - AppMeta { - name: s, - disp_ord: 999, - ..Default::default() - } - } -} diff --git a/src/app/parser.rs b/src/app/parser.rs deleted file mode 100644 index decfde4f8d2a..000000000000 --- a/src/app/parser.rs +++ /dev/null @@ -1,2167 +0,0 @@ -// Std -use std::ffi::{OsStr, OsString}; -use std::fmt::Display; -use std::fs::File; -use std::io::{self, BufWriter, Write}; -#[cfg(all(feature = "debug", not(any(target_os = "windows", target_arch = "wasm32"))))] -use std::os::unix::ffi::OsStrExt; -#[cfg(all(feature = "debug", any(target_os = "windows", target_arch = "wasm32")))] -use osstringext::OsStrExt3; -use std::path::PathBuf; -use std::slice::Iter; -use std::iter::Peekable; -use std::cell::Cell; - -// Internal -use INTERNAL_ERROR_MSG; -use INVALID_UTF8; -use SubCommand; -use app::App; -use app::help::Help; -use app::meta::AppMeta; -use app::settings::AppFlags; -use args::{AnyArg, Arg, ArgGroup, ArgMatcher, Base, FlagBuilder, OptBuilder, PosBuilder, Switched}; -use args::settings::ArgSettings; -use completions::ComplGen; -use errors::{Error, ErrorKind}; -use errors::Result as ClapResult; -use fmt::ColorWhen; -use osstringext::OsStrExt2; -use completions::Shell; -use suggestions; -use app::settings::AppSettings as AS; -use app::validator::Validator; -use app::usage; -use map::{self, VecMap}; - -#[derive(Debug, PartialEq, Copy, Clone)] -#[doc(hidden)] -pub enum ParseResult<'a> { - Flag, - Opt(&'a str), - Pos(&'a str), - MaybeHyphenValue, - MaybeNegNum, - NotFound, - ValuesDone, -} - -#[allow(missing_debug_implementations)] -#[doc(hidden)] -#[derive(Clone, Default)] -pub struct Parser<'a, 'b> -where - 'a: 'b, -{ - pub meta: AppMeta<'b>, - settings: AppFlags, - pub g_settings: AppFlags, - pub flags: Vec>, - pub opts: Vec>, - pub positionals: VecMap>, - pub subcommands: Vec>, - pub groups: Vec>, - pub global_args: Vec>, - pub required: Vec<&'a str>, - pub r_ifs: Vec<(&'a str, &'b str, &'a str)>, - pub overrides: Vec<(&'b str, &'a str)>, - help_short: Option, - version_short: Option, - cache: Option<&'a str>, - pub help_message: Option<&'a str>, - pub version_message: Option<&'a str>, - cur_idx: Cell, -} - -impl<'a, 'b> Parser<'a, 'b> -where - 'a: 'b, -{ - pub fn with_name(n: String) -> Self { - Parser { - meta: AppMeta::with_name(n), - g_settings: AppFlags::zeroed(), - cur_idx: Cell::new(0), - ..Default::default() - } - } - - pub fn help_short(&mut self, s: &str) { - let c = s.trim_left_matches(|c| c == '-') - .chars() - .nth(0) - .unwrap_or('h'); - self.help_short = Some(c); - } - - pub fn version_short(&mut self, s: &str) { - let c = s.trim_left_matches(|c| c == '-') - .chars() - .nth(0) - .unwrap_or('V'); - self.version_short = Some(c); - } - - pub fn gen_completions_to(&mut self, for_shell: Shell, buf: &mut W) { - if !self.is_set(AS::Propagated) { - self.propagate_help_version(); - self.build_bin_names(); - self.propagate_globals(); - self.propagate_settings(); - self.set(AS::Propagated); - } - - ComplGen::new(self).generate(for_shell, buf) - } - - pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) { - use std::error::Error; - - let out_dir = PathBuf::from(od); - let name = &*self.meta.bin_name.as_ref().unwrap().clone(); - let file_name = match for_shell { - Shell::Bash => format!("{}.bash", name), - Shell::Fish => format!("{}.fish", name), - Shell::Zsh => format!("_{}", name), - Shell::PowerShell => format!("_{}.ps1", name), - Shell::Elvish => format!("{}.elv", name), - }; - - let mut file = match File::create(out_dir.join(file_name)) { - Err(why) => panic!("couldn't create completion file: {}", why.description()), - Ok(file) => file, - }; - self.gen_completions_to(for_shell, &mut file) - } - - #[inline] - fn app_debug_asserts(&self) -> bool { - assert!(self.verify_positionals()); - let should_err = self.groups.iter().all(|g| { - g.args.iter().all(|arg| { - (self.flags.iter().any(|f| &f.b.name == arg) - || self.opts.iter().any(|o| &o.b.name == arg) - || self.positionals.values().any(|p| &p.b.name == arg) - || self.groups.iter().any(|g| &g.name == arg)) - }) - }); - let g = self.groups.iter().find(|g| { - g.args.iter().any(|arg| { - !(self.flags.iter().any(|f| &f.b.name == arg) - || self.opts.iter().any(|o| &o.b.name == arg) - || self.positionals.values().any(|p| &p.b.name == arg) - || self.groups.iter().any(|g| &g.name == arg)) - }) - }); - assert!( - should_err, - "The group '{}' contains the arg '{}' that doesn't actually exist.", - g.unwrap().name, - g.unwrap() - .args - .iter() - .find(|arg| !(self.flags.iter().any(|f| &&f.b.name == arg) - || self.opts.iter().any(|o| &&o.b.name == arg) - || self.positionals.values().any(|p| &&p.b.name == arg) - || self.groups.iter().any(|g| &&g.name == arg))) - .unwrap() - ); - true - } - - #[inline] - 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 { - 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 { - 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 - }; - 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 - ); - 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 - ); - 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] - fn add_conditional_reqs(&mut self, a: &Arg<'a, 'b>) { - if let Some(ref r_ifs) = a.r_ifs { - for &(arg, val) in r_ifs { - self.r_ifs.push((arg, val, a.b.name)); - } - } - } - - #[inline] - fn add_arg_groups(&mut self, a: &Arg<'a, 'b>) { - if let Some(ref grps) = a.b.groups { - for g in grps { - let mut found = false; - if let Some(ref mut ag) = self.groups.iter_mut().find(|grp| &grp.name == g) { - ag.args.push(a.b.name); - found = true; - } - if !found { - let mut ag = ArgGroup::with_name(g); - ag.args.push(a.b.name); - self.groups.push(ag); - } - } - } - } - - #[inline] - fn add_reqs(&mut self, a: &Arg<'a, 'b>) { - if a.is_set(ArgSettings::Required) { - // If the arg is required, add all it's requirements to master required list - self.required.push(a.b.name); - if let Some(ref areqs) = a.b.requires { - for name in areqs - .iter() - .filter(|&&(val, _)| val.is_none()) - .map(|&(_, name)| name) - { - self.required.push(name); - } - } - } - } - - #[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); - } - if let Some(l) = a.s.long { - if l == "version" { - self.unset(AS::NeedsLongVersion); - } else if l == "help" { - self.unset(AS::NeedsLongHelp); - } - } - } - - // 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); - } - 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) - } else { - a.index.unwrap() as usize - }; - self.positionals - .insert(i, PosBuilder::from_arg(a, i as u64)); - } else if a.is_set(ArgSettings::TakesValue) { - let mut ob = OptBuilder::from(a); - ob.s.unified_ord = self.flags.len() + self.opts.len(); - self.opts.push(ob); - } else { - let mut fb = FlagBuilder::from(a); - fb.s.unified_ord = self.flags.len() + self.opts.len(); - self.flags.push(fb); - } - } - // actually adds the arguments but from a borrow (which means we have to do some cloning) - pub fn add_arg_ref(&mut self, a: &Arg<'a, 'b>) { - 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) - } else { - a.index.unwrap() as usize - }; - let pb = PosBuilder::from_arg_ref(a, i as u64); - self.positionals.insert(i, pb); - } else if a.is_set(ArgSettings::TakesValue) { - let mut ob = OptBuilder::from(a); - ob.s.unified_ord = self.flags.len() + self.opts.len(); - self.opts.push(ob); - } else { - let mut fb = FlagBuilder::from(a); - fb.s.unified_ord = self.flags.len() + self.opts.len(); - self.flags.push(fb); - } - if a.is_set(ArgSettings::Global) { - self.global_args.push(a.into()); - } - } - - pub fn add_group(&mut self, group: ArgGroup<'a>) { - if group.required { - self.required.push(group.name); - 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 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); - grp.args.extend_from_slice(&group.args); - grp.requires = group.requires.clone(); - grp.conflicts = group.conflicts.clone(); - grp.required = group.required; - } else { - self.groups.push(group); - } - } - - pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { - debugln!( - "Parser::add_subcommand: term_w={:?}, name={}", - self.meta.term_w, - subcmd.p.meta.name - ); - subcmd.p.meta.term_w = self.meta.term_w; - if subcmd.p.meta.name == "help" { - self.unset(AS::NeedsSubcommandHelp); - } - - self.subcommands.push(subcmd); - } - - pub fn propagate_settings(&mut self) { - debugln!( - "Parser::propagate_settings: self={}, g_settings={:#?}", - self.meta.name, - self.g_settings - ); - for sc in &mut self.subcommands { - debugln!( - "Parser::propagate_settings: sc={}, settings={:#?}, g_settings={:#?}", - sc.p.meta.name, - sc.p.settings, - sc.p.g_settings - ); - // We have to create a new scope in order to tell rustc the borrow of `sc` is - // done and to recursively call this method - { - let vsc = self.settings.is_set(AS::VersionlessSubcommands); - let gv = self.settings.is_set(AS::GlobalVersion); - - if vsc { - sc.p.set(AS::DisableVersion); - } - 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()); - } - sc.p.settings = sc.p.settings | self.g_settings; - sc.p.g_settings = sc.p.g_settings | self.g_settings; - sc.p.meta.term_w = self.meta.term_w; - sc.p.meta.max_w = self.meta.max_w; - } - sc.p.propagate_settings(); - } - } - - #[cfg_attr(feature = "lints", allow(needless_borrow))] - pub fn derive_display_order(&mut self) { - 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) - { - 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) - { - 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) - { - sc.p.meta.disp_ord = i; - } - } - for sc in &mut self.subcommands { - sc.p.derive_display_order(); - } - } - - pub fn required(&self) -> Iter<&str> { self.required.iter() } - - #[cfg_attr(feature = "lints", allow(needless_borrow))] - #[inline] - pub fn has_args(&self) -> bool { - !(self.flags.is_empty() && self.opts.is_empty() && self.positionals.is_empty()) - } - - #[inline] - pub fn has_opts(&self) -> bool { !self.opts.is_empty() } - - #[inline] - pub fn has_flags(&self) -> bool { !self.flags.is_empty() } - - #[inline] - pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() } - - #[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 { - self.has_subcommands() - && self.subcommands - .iter() - .filter(|sc| sc.p.meta.name != "help") - .any(|sc| !sc.p.is_set(AS::Hidden)) - } - - #[inline] - pub fn is_set(&self, s: AS) -> bool { self.settings.is_set(s) } - - #[inline] - pub fn set(&mut self, s: AS) { self.settings.set(s) } - - #[inline] - 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(&self) -> bool { - // Because you must wait until all arguments have been supplied, this is the first chance - // to make assertions on positional argument indexes - // - // First 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() { - assert!( - !(idx != self.positionals.len()), - "Found positional argument \"{}\" whose 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.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)) - || last.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 ok = second_to_last.is_set(ArgSettings::Multiple) || last.is_set(ArgSettings::Last); - assert!( - ok, - "Only the last positional argument, or second to last positional \ - argument may be set to .multiple(true)" - ); - - let count = self.positionals - .values() - .filter(|p| p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none()) - .count(); - let ok = count <= 1 - || (last.is_set(ArgSettings::Last) && last.is_set(ArgSettings::Multiple) - && second_to_last.is_set(ArgSettings::Multiple) - && count == 2); - assert!( - ok, - "Only one positional argument with .multiple(true) set is allowed per \ - command, unless the second one also has .last(true) set" - ); - } - - 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) { - 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; - } - found = true; - continue; - } else { - found = false; - } - } - } 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 { - 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 propagate_globals(&mut self) { - for sc in &mut self.subcommands { - // We have to create a new scope in order to tell rustc the borrow of `sc` is - // done and to recursively call this method - { - for a in &self.global_args { - sc.p.add_arg_ref(a); - } - } - sc.p.propagate_globals(); - } - } - - // Checks if the arg matches a subcommand name, or any of it's aliases (if defined) - fn possible_subcommand(&self, arg_os: &OsStr) -> (bool, Option<&str>) { - #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] - use std::os::unix::ffi::OsStrExt; - #[cfg(any(target_os = "windows", target_arch = "wasm32"))] - use osstringext::OsStrExt3; - debugln!("Parser::possible_subcommand: arg={:?}", arg_os); - fn starts(h: &str, n: &OsStr) -> bool { - let n_bytes = n.as_bytes(); - let h_bytes = OsStr::new(h).as_bytes(); - - h_bytes.starts_with(n_bytes) - } - - 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::>(); - - for sc in &v { - if OsStr::new(sc) == arg_os { - return (true, Some(sc)); - } - } - - if v.len() == 1 { - return (true, Some(v[0])); - } - } - (false, None) - } - - fn parse_help_subcommand(&self, it: &mut I) -> ClapResult> - where - I: Iterator, - T: Into, - { - debugln!("Parser::parse_help_subcommand;"); - let cmds: Vec = it.map(|c| c.into()).collect(); - let mut help_help = false; - let mut bin_name = self.meta - .bin_name - .as_ref() - .unwrap_or(&self.meta.name) - .clone(); - let mut sc = { - let mut sc: &Parser = self; - for (i, cmd) in cmds.iter().enumerate() { - if &*cmd.to_string_lossy() == "help" { - // cmd help help - help_help = true; - } - if let Some(c) = sc.subcommands - .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) - { - sc = c; - if i == cmds.len() - 1 { - break; - } - } else { - return Err(Error::unrecognized_subcommand( - cmd.to_string_lossy().into_owned(), - self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), - self.color(), - )); - } - bin_name = format!("{} {}", bin_name, &*sc.meta.name); - } - sc.clone() - }; - if help_help { - let mut pb = PosBuilder::new("subcommand", 1); - pb.b.help = Some("The subcommand whose help message to display"); - pb.set(ArgSettings::Multiple); - sc.positionals.insert(1, pb); - sc.settings = sc.settings | self.g_settings; - } else { - sc.create_help_and_version(); - } - if sc.meta.bin_name != self.meta.bin_name { - sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name)); - } - Err(sc._help(false)) - } - - // 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) -> 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) { - let a = arg_os.to_string_lossy(); - if a.parse::().is_ok() || a.parse::().is_ok() { - self.set(AS::ValidNegNumFound); - true - } else { - false - } - } else { - false - }; - let arg_allows_tac = match needs_val_of { - ParseResult::Opt(name) => { - let o = self.opts - .iter() - .find(|o| o.b.name == name) - .expect(INTERNAL_ERROR_MSG); - (o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) - } - ParseResult::Pos(name) => { - let p = self.positionals - .values() - .find(|p| p.b.name == name) - .expect(INTERNAL_ERROR_MSG); - (p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) - } - ParseResult::ValuesDone => return true, - _ => false, - }; - 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"--") { - debugln!("Parser::is_new_arg: -- found"); - if arg_os.len() == 2 && !arg_allows_tac { - return true; // We have to return true so override everything else - } else if arg_allows_tac { - return false; - } - true - } else if arg_os.starts_with(b"-") { - 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 { - debugln!("Parser::is_new_arg: probably value"); - false - }; - - ret = ret && !arg_allows_tac; - - debugln!("Parser::is_new_arg: starts_new_arg={:?}", ret); - ret - } - - // The actual parsing function - #[cfg_attr(feature = "lints", allow(while_let_on_iterator, collapsible_if))] - pub fn get_matches_with( - &mut self, - matcher: &mut ArgMatcher<'a>, - it: &mut Peekable, - ) -> ClapResult<()> - where - I: Iterator, - T: Into + Clone, - { - debugln!("Parser::get_matches_with;"); - // Verify all positional assertions pass - debug_assert!(self.app_debug_asserts()); - if self.positionals.values().any(|a| { - a.b.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len()) - }) - && self.positionals - .values() - .last() - .map_or(false, |p| !p.is_set(ArgSettings::Last)) - { - self.settings.set(AS::LowIndexMultiplePositional); - } - let has_args = self.has_args(); - - // Next we create the `--help` and `--version` arguments and add them if - // necessary - self.create_help_and_version(); - - let mut subcmd_name: Option = None; - let mut needs_val_of: ParseResult<'a> = ParseResult::NotFound; - let mut pos_counter = 1; - let mut sc_is_external = false; - while let Some(arg) = it.next() { - let arg_os = arg.into(); - debugln!( - "Parser::get_matches_with: Begin parsing '{:?}' ({:?})", - arg_os, - &*arg_os.as_bytes() - ); - - self.unset(AS::ValidNegNumFound); - // Is this a new argument, or values from a previous option? - let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of); - if !self.is_set(AS::TrailingValues) && arg_os.starts_with(b"--") && arg_os.len() == 2 - && starts_new_arg - { - debugln!("Parser::get_matches_with: setting TrailingVals=true"); - self.set(AS::TrailingValues); - continue; - } - - // 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) - { - match needs_val_of { - ParseResult::Opt(_) | ParseResult::Pos(_) => (), - _ => { - 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) { - self.parse_help_subcommand(it)?; - } - subcmd_name = Some(sc_name.to_owned()); - break; - } - } - } - } - - if starts_new_arg { - let check_all = self.is_set(AS::AllArgsOverrideSelf); - { - let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); - matcher.process_arg_overrides( - any_arg, - &mut self.overrides, - &mut self.required, - check_all, - ); - } - - if arg_os.starts_with(b"--") { - needs_val_of = self.parse_long_arg(matcher, &arg_os, it)?; - 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 - .iter() - .find(|o| o.b.name == name) - .expect(INTERNAL_ERROR_MSG); - // get the OptBuilder so we can check the settings - needs_val_of = self.add_val_to_arg(arg, &arg_os, matcher)?; - // get the next value from the iterator - continue; - } - } - } - - if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) - && !self.is_set(AS::InferSubcommands) && !self.is_set(AS::AllowExternalSubcommands) - { - 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(), - )); - } - } - - let low_index_mults = self.is_set(AS::LowIndexMultiplePositional) - && pos_counter == (self.positionals.len() - 1); - let missing_pos = self.is_set(AS::AllowMissingPositional) - && (pos_counter == (self.positionals.len() - 1) - && !self.is_set(AS::TrailingValues)); - 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(); - needs_val_of = if needs_val_of != ParseResult::ValuesDone { - if let Some(p) = self.positionals.get(pos_counter) { - ParseResult::Pos(p.b.name) - } else { - ParseResult::ValuesDone - } - } else { - ParseResult::ValuesDone - }; - 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; - } - } else { - debugln!("Parser::get_matches_with: Bumping the positional counter..."); - pos_counter += 1; - } - } else if (self.is_set(AS::AllowMissingPositional) && self.is_set(AS::TrailingValues)) - || (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(), - )); - } - 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 check_all = self.is_set(AS::AllArgsOverrideSelf); - { - let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); - matcher.process_arg_overrides( - any_arg, - &mut self.overrides, - &mut self.required, - check_all, - ); - } - 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 - let sc_name = match arg_os.to_str() { - Some(s) => s.to_string(), - None => { - if !self.is_set(AS::StrictUtf8) { - return Err(Error::invalid_utf8( - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } - arg_os.to_string_lossy().into_owned() - } - }; - - // Collect the external subcommand args - let mut sc_m = ArgMatcher::new(); - 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( - &*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(), - }); - sc_is_external = true; - } else if !((self.is_set(AS::AllowLeadingHyphen) - || self.is_set(AS::AllowNegativeNumbers)) - && arg_os.starts_with(b"-")) - && !self.is_set(AS::InferSubcommands) - { - return Err(Error::unknown_argument( - &*arg_os.to_string_lossy(), - "", - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } 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 { - 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(), - )); - } - } else { - return Err(Error::unknown_argument( - &*arg_os.to_string_lossy(), - "", - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } - } - - if !sc_is_external { - 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() - }; - 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![]; - self.write_help_err(&mut out)?; - return Err(Error { - message: String::from_utf8_lossy(&*out).into_owned(), - kind: ErrorKind::MissingArgumentOrSubcommand, - info: None, - }); - } - } - - // In case the last arg was new, we need to process it's overrides - let check_all = self.is_set(AS::AllArgsOverrideSelf); - { - let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); - matcher.process_arg_overrides( - any_arg, - &mut self.overrides, - &mut self.required, - check_all, - ); - } - - 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;"); - self.create_help_and_version(); - for sc in &mut self.subcommands { - sc.p.propagate_help_version(); - } - } - - fn build_bin_names(&mut self) { - 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() { - sdebugln!("No"); - let bin_name = format!( - "{}{}{}", - self.meta - .bin_name - .as_ref() - .unwrap_or(&self.meta.name.clone()), - if self.meta.bin_name.is_some() { - " " - } else { - "" - }, - &*sc.p.meta.name - ); - debugln!( - "Parser::build_bin_names:iter: Setting bin_name of {} to {}", - self.meta.name, - bin_name - ); - sc.p.meta.bin_name = Some(bin_name); - } else { - sdebugln!("yes ({:?})", sc.p.meta.bin_name); - } - debugln!( - "Parser::build_bin_names:iter: Calling build_bin_names from...{}", - sc.p.meta.name - ); - sc.p.build_bin_names(); - } - } - - fn parse_subcommand( - &mut self, - sc_name: &str, - matcher: &mut ArgMatcher<'a>, - it: &mut Peekable, - ) -> ClapResult<()> - where - I: Iterator, - T: Into + Clone, - { - use std::fmt::Write; - 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(); - for k in matcher.arg_names() { - hs.push(k); - } - 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) - { - 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()), - if self.meta.bin_name.is_some() { - &*mid_string - } else { - "" - }, - &*sc.p.meta.name - )); - sc.p.meta.bin_name = Some(format!( - "{}{}{}", - self.meta.bin_name.as_ref().unwrap_or(&String::new()), - if self.meta.bin_name.is_some() { - " " - } else { - "" - }, - &*sc.p.meta.name - )); - debugln!( - "Parser::parse_subcommand: About to parse sc={}", - sc.p.meta.name - ); - debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings); - sc.p.get_matches_with(&mut sc_matcher, it)?; - matcher.subcommand(SubCommand { - name: sc.p.meta.name.clone(), - matches: sc_matcher.into(), - }); - } - Ok(()) - } - - pub fn groups_for_arg(&self, name: &str) -> Option> { - debugln!("Parser::groups_for_arg: name={}", name); - - if self.groups.is_empty() { - debugln!("Parser::groups_for_arg: No groups defined"); - return None; - } - let mut res = vec![]; - debugln!("Parser::groups_for_arg: Searching through groups..."); - for grp in &self.groups { - for a in &grp.args { - if a == &name { - sdebugln!("\tFound '{}'", grp.name); - res.push(&*grp.name); - } - } - } - if res.is_empty() { - return None; - } - - Some(res) - } - - pub fn args_in_group(&self, group: &str) -> Vec { - debug_assert!(self.app_debug_asserts()); - - 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 - { - 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) { - args.push(p.b.name.to_owned()); - } else { - g_vec.push(*n); - } - } - - for av in g_vec.iter().map(|g| self.args_in_group(g)) { - args.extend(av); - } - args.dedup(); - args.iter().map(ToOwned::to_owned).collect() - } - - 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 - { - if self.groups.iter().any(|g| g.name == *n) { - args.extend(self.arg_names_in_group(n)); - g_vec.push(*n); - } else if !args.contains(n) { - args.push(*n); - } - } - - args.iter().map(|s| *s).collect() - } - - pub fn create_help_and_version(&mut self) { - debugln!("Parser::create_help_and_version;"); - // name is "hclap_help" because flags are sorted by name - if !self.is_set(AS::DisableHelpFlags) && !self.contains_long("help") { - debugln!("Parser::create_help_and_version: Building --help"); - if self.help_short.is_none() && !self.contains_short('h') { - self.help_short = Some('h'); - } - let arg = FlagBuilder { - b: Base { - name: "hclap_help", - help: self.help_message.or(Some("Prints help information")), - ..Default::default() - }, - s: Switched { - short: self.help_short, - long: Some("help"), - ..Default::default() - }, - }; - self.flags.push(arg); - } - if !self.is_set(AS::DisableVersion) && !self.contains_long("version") { - debugln!("Parser::create_help_and_version: Building --version"); - if self.version_short.is_none() && !self.contains_short('V') { - self.version_short = Some('V'); - } - // name is "vclap_version" because flags are sorted by name - let arg = FlagBuilder { - b: Base { - name: "vclap_version", - help: self.version_message.or(Some("Prints version information")), - ..Default::default() - }, - s: Switched { - short: self.version_short, - long: Some("version"), - ..Default::default() - }, - }; - self.flags.push(arg); - } - if !self.subcommands.is_empty() && !self.is_set(AS::DisableHelpSubcommand) - && self.is_set(AS::NeedsSubcommandHelp) - { - debugln!("Parser::create_help_and_version: Building help"); - self.subcommands.push( - App::new("help") - .about("Prints this message or the help of the given subcommand(s)"), - ); - } - } - - // Retrieves the names of all args the user has supplied thus far, except required ones - // because those will be listed in self.required - 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...", - arg.to_str().unwrap() - ); - if arg == "help" && self.is_set(AS::NeedsLongHelp) { - sdebugln!("Help"); - return Err(self._help(true)); - } - if arg == "version" && self.is_set(AS::NeedsLongVersion) { - sdebugln!("Version"); - return Err(self._version(true)); - } - sdebugln!("Neither"); - - Ok(()) - } - - fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> { - debugln!("Parser::check_for_help_and_version_char;"); - debug!( - "Parser::check_for_help_and_version_char: Checking if -{} is help or version...", - arg - ); - if let Some(h) = self.help_short { - if arg == h && self.is_set(AS::NeedsLongHelp) { - sdebugln!("Help"); - return Err(self._help(false)); - } - } - if let Some(v) = self.version_short { - if arg == v && self.is_set(AS::NeedsLongVersion) { - sdebugln!("Version"); - return Err(self._version(false)); - } - } - sdebugln!("Neither"); - Ok(()) - } - - fn use_long_help(&self) -> bool { - // In this case, both must be checked. This allows the retention of - // original formatting, but also ensures that the actual -h or --help - // specified by the user is sent through. If HiddenShortHelp is not included, - // then items specified with hidden_short_help will also be hidden. - let should_long = |v: &Base| { - v.long_help.is_some() || - v.is_set(ArgSettings::HiddenLongHelp) || - v.is_set(ArgSettings::HiddenShortHelp) - }; - - self.meta.long_about.is_some() - || self.flags.iter().any(|f| should_long(&f.b)) - || self.opts.iter().any(|o| should_long(&o.b)) - || self.positionals.values().any(|p| should_long(&p.b)) - || self.subcommands - .iter() - .any(|s| s.p.meta.long_about.is_some()) - } - - fn _help(&self, mut use_long: bool) -> Error { - debugln!("Parser::_help: use_long={:?}", use_long); - use_long = use_long && self.use_long_help(); - let mut buf = vec![]; - match Help::write_parser_help(&mut buf, self, use_long) { - Err(e) => e, - _ => Error { - message: String::from_utf8(buf).unwrap_or_default(), - kind: ErrorKind::HelpDisplayed, - info: None, - }, - } - } - - fn _version(&self, use_long: bool) -> Error { - debugln!("Parser::_version: "); - let out = io::stdout(); - let mut buf_w = BufWriter::new(out.lock()); - match self.print_version(&mut buf_w, use_long) { - Err(e) => e, - _ => Error { - message: String::new(), - kind: ErrorKind::VersionDisplayed, - info: None, - }, - } - } - - fn parse_long_arg( - &mut self, - matcher: &mut ArgMatcher<'a>, - full_arg: &OsStr, - it: &mut Peekable, - ) -> ClapResult> - where - I: Iterator, - T: Into + Clone, - { - // maybe here lifetime should be 'a - debugln!("Parser::parse_long_arg;"); - - // Update the current index - self.cur_idx.set(self.cur_idx.get() + 1); - - let mut val = None; - debug!("Parser::parse_long_arg: Does it contain '='..."); - let arg = if full_arg.contains_byte(b'=') { - let (p0, p1) = full_arg.trim_left_matches(b'-').split_at_byte(b'='); - sdebugln!("Yes '{:?}'", p1); - val = Some(p1); - p0 - } else { - sdebugln!("No"); - full_arg.trim_left_matches(b'-') - }; - - if let Some(opt) = find_opt_by_long!(@os self, arg) { - debugln!( - "Parser::parse_long_arg: Found valid opt '{}'", - opt.to_string() - ); - 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) { - self.cache = Some(opt.b.name); - } - - return Ok(ret); - } else if let Some(flag) = find_flag_by_long!(@os self, arg) { - debugln!( - "Parser::parse_long_arg: Found valid flag '{}'", - flag.to_string() - ); - self.settings.set(AS::ValidArgFound); - // Only flags could be help or version, and we need to check the raw long - // so this is the first point to check - self.check_for_help_and_version_str(arg)?; - - self.parse_flag(flag, matcher)?; - - // Handle conflicts, requirements, etc. - 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) { - return Ok(ParseResult::MaybeHyphenValue); - } else if self.is_set(AS::ValidNegNumFound) { - return Ok(ParseResult::MaybeNegNum); - } - - debugln!("Parser::parse_long_arg: Didn't match anything"); - - let args_rest: Vec<_> = it.map(|x| x.clone().into()).collect(); - let args_rest2: Vec<_> = args_rest.iter().map(|x| x.to_str().expect(INVALID_UTF8)).collect(); - self.did_you_mean_error( - arg.to_str().expect(INVALID_UTF8), - matcher, - &args_rest2[..] - ).map(|_| ParseResult::NotFound) - } - - #[cfg_attr(feature = "lints", allow(len_zero))] - fn parse_short_arg( - &mut self, - matcher: &mut ArgMatcher<'a>, - full_arg: &OsStr, - ) -> ClapResult> { - debugln!("Parser::parse_short_arg: full_arg={:?}", full_arg); - let arg_os = full_arg.trim_left_matches(b'-'); - let arg = arg_os.to_string_lossy(); - - // If AllowLeadingHyphen is set, we want to ensure `-val` gets parsed as `-val` and not - // `-v` `-a` `-l` assuming `v` `a` and `l` are all, or mostly, valid shorts. - if self.is_set(AS::AllowLeadingHyphen) { - if arg.chars().any(|c| !self.contains_short(c)) { - debugln!( - "Parser::parse_short_arg: LeadingHyphenAllowed yet -{} isn't valid", - arg - ); - return Ok(ParseResult::MaybeHyphenValue); - } - } else if self.is_set(AS::ValidNegNumFound) { - // TODO: Add docs about having AllowNegativeNumbers and `-2` as a valid short - // May be better to move this to *after* not finding a valid flag/opt? - debugln!("Parser::parse_short_arg: Valid negative num..."); - return Ok(ParseResult::MaybeNegNum); - } - - let mut ret = ParseResult::NotFound; - for c in arg.chars() { - debugln!("Parser::parse_short_arg:iter:{}", c); - - // update each index because `-abcd` is four indices to clap - self.cur_idx.set(self.cur_idx.get() + 1); - - // Check for matching short options, and return the name if there is no trailing - // concatenated value: -oval - // Option: -o - // Value: val - if let Some(opt) = find_opt_by_short!(self, c) { - debugln!("Parser::parse_short_arg:iter:{}: Found valid opt", c); - self.settings.set(AS::ValidArgFound); - // Check for trailing concatenated value - let p: Vec<_> = arg.splitn(2, c).collect(); - debugln!( - "Parser::parse_short_arg:iter:{}: p[0]={:?}, p[1]={:?}", - c, - p[0].as_bytes(), - p[1].as_bytes() - ); - let i = p[0].as_bytes().len() + 1; - let val = if p[1].as_bytes().len() > 0 { - debugln!( - "Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)", - c, - arg_os.split_at(i).1.as_bytes(), - arg_os.split_at(i).1 - ); - Some(arg_os.split_at(i).1) - } else { - None - }; - - // Default to "we're expecting a value later" - let ret = self.parse_opt(val, opt, false, matcher)?; - - if self.cache.map_or(true, |name| name != opt.b.name) { - self.cache = Some(opt.b.name); - } - - return Ok(ret); - } else if let Some(flag) = find_flag_by_short!(self, c) { - debugln!("Parser::parse_short_arg:iter:{}: Found valid flag", c); - self.settings.set(AS::ValidArgFound); - // Only flags can be help or version - self.check_for_help_and_version_char(c)?; - ret = self.parse_flag(flag, matcher)?; - - // Handle conflicts, requirements, overrides, etc. - // Must be called here due to mutabililty - if self.cache.map_or(true, |name| name != flag.b.name) { - self.cache = Some(flag.b.name); - } - } else { - let arg = format!("-{}", c); - return Err(Error::unknown_argument( - &*arg, - "", - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } - } - Ok(ret) - } - - fn parse_opt( - &self, - val: Option<&OsStr>, - opt: &OptBuilder<'a, 'b>, - had_eq: bool, - matcher: &mut ArgMatcher<'a>, - ) -> ClapResult> { - debugln!("Parser::parse_opt; opt={}, val={:?}", opt.b.name, val); - debugln!("Parser::parse_opt; opt.settings={:?}", opt.b.settings); - let mut has_eq = false; - let no_val = val.is_none(); - let empty_vals = opt.is_set(ArgSettings::EmptyValues); - let min_vals_zero = opt.v.min_vals.unwrap_or(1) == 0; - let needs_eq = opt.is_set(ArgSettings::RequireEquals); - - debug!("Parser::parse_opt; Checking for val..."); - if let Some(fv) = val { - has_eq = fv.starts_with(&[b'=']) || had_eq; - let v = fv.trim_left_matches(b'='); - if !empty_vals && (v.len() == 0 || (needs_eq && !has_eq)) { - sdebugln!("Found Empty - Error"); - return Err(Error::empty_value( - opt, - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } - sdebugln!("Found - {:?}, len: {}", v, v.len()); - debugln!( - "Parser::parse_opt: {:?} contains '='...{:?}", - fv, - fv.starts_with(&[b'=']) - ); - self.add_val_to_arg(opt, v, matcher)?; - } else if needs_eq && !(empty_vals || min_vals_zero) { - sdebugln!("None, but requires equals...Error"); - return Err(Error::empty_value( - opt, - &*usage::create_error_usage(self, matcher, None), - self.color(), - )); - } else { - sdebugln!("None"); - } - - matcher.inc_occurrence_of(opt.b.name); - // Increment or create the group "args" - self.groups_for_arg(opt.b.name) - .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); - - let needs_delim = opt.is_set(ArgSettings::RequireDelimiter); - let mult = opt.is_set(ArgSettings::Multiple); - if no_val && min_vals_zero && !has_eq && needs_eq { - debugln!("Parser::parse_opt: More arg vals not required..."); - return Ok(ParseResult::ValuesDone); - } else if no_val || (mult && !needs_delim) && !has_eq && matcher.needs_more_vals(opt) { - debugln!("Parser::parse_opt: More arg vals required..."); - return Ok(ParseResult::Opt(opt.b.name)); - } - debugln!("Parser::parse_opt: More arg vals not required..."); - Ok(ParseResult::ValuesDone) - } - - fn add_val_to_arg( - &self, - arg: &A, - val: &OsStr, - matcher: &mut ArgMatcher<'a>, - ) -> ClapResult> - where - A: AnyArg<'a, 'b> + Display, - { - debugln!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name(), val); - debugln!( - "Parser::add_val_to_arg; trailing_vals={:?}, DontDelimTrailingVals={:?}", - self.is_set(AS::TrailingValues), - self.is_set(AS::DontDelimitTrailingValues) - ); - if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) { - if let Some(delim) = arg.val_delim() { - if val.is_empty() { - Ok(self.add_single_val_to_arg(arg, val, matcher)?) - } else { - let mut iret = ParseResult::ValuesDone; - for v in val.split(delim as u32 as u8) { - iret = self.add_single_val_to_arg(arg, v, matcher)?; - } - // If there was a delimiter used, we're not looking for more values - if val.contains_byte(delim as u32 as u8) - || arg.is_set(ArgSettings::RequireDelimiter) - { - iret = ParseResult::ValuesDone; - } - Ok(iret) - } - } else { - self.add_single_val_to_arg(arg, val, matcher) - } - } else { - self.add_single_val_to_arg(arg, val, matcher) - } - } - - fn add_single_val_to_arg( - &self, - arg: &A, - v: &OsStr, - matcher: &mut ArgMatcher<'a>, - ) -> ClapResult> - where - A: AnyArg<'a, 'b> + Display, - { - debugln!("Parser::add_single_val_to_arg;"); - debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v); - - // update the current index because each value is a distinct index to clap - self.cur_idx.set(self.cur_idx.get() + 1); - - // @TODO @docs @p4: docs for indices should probably note that a terminator isn't a value - // and therefore not reported in indices - if let Some(t) = arg.val_terminator() { - if t == v { - return Ok(ParseResult::ValuesDone); - } - } - - matcher.add_val_to(arg.name(), v); - matcher.add_index_to(arg.name(), self.cur_idx.get()); - - // Increment or create the group "args" - if let Some(grps) = self.groups_for_arg(arg.name()) { - for grp in grps { - matcher.add_val_to(&*grp, v); - } - } - - if matcher.needs_more_vals(arg) { - return Ok(ParseResult::Opt(arg.name())); - } - Ok(ParseResult::ValuesDone) - } - - fn parse_flag( - &self, - flag: &FlagBuilder<'a, 'b>, - matcher: &mut ArgMatcher<'a>, - ) -> ClapResult> { - debugln!("Parser::parse_flag;"); - - matcher.inc_occurrence_of(flag.b.name); - matcher.add_index_to(flag.b.name, self.cur_idx.get()); - - // Increment or create the group "args" - self.groups_for_arg(flag.b.name) - .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); - - Ok(ParseResult::Flag) - } - - fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'a>, args_rest: &[&str]) -> ClapResult<()> { - // Didn't match a flag or option - let suffix = suggestions::did_you_mean_flag_suffix(arg, &args_rest, longs!(self), &self.subcommands); - - // Add the arg to the matches to build a proper usage string - if let Some(name) = suffix.1 { - if let Some(opt) = find_opt_by_long!(self, name) { - self.groups_for_arg(&*opt.b.name) - .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); - matcher.insert(&*opt.b.name); - } else if let Some(flg) = find_flag_by_long!(self, name) { - self.groups_for_arg(&*flg.b.name) - .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); - matcher.insert(&*flg.b.name); - } - } - - let used_arg = format!("--{}", arg); - Err(Error::unknown_argument( - &*used_arg, - &*suffix.0, - &*usage::create_error_usage(self, matcher, None), - self.color(), - )) - } - - // Prints the version to the user and exits if quit=true - fn print_version(&self, w: &mut W, use_long: bool) -> ClapResult<()> { - self.write_version(w, use_long)?; - w.flush().map_err(Error::from) - } - - pub fn write_version(&self, w: &mut W, use_long: bool) -> io::Result<()> { - let ver = if use_long { - self.meta - .long_version - .unwrap_or_else(|| self.meta.version.unwrap_or("")) - } else { - self.meta - .version - .unwrap_or_else(|| self.meta.long_version.unwrap_or("")) - }; - if let Some(bn) = self.meta.bin_name.as_ref() { - if bn.contains(' ') { - // Incase we're dealing with subcommands i.e. git mv is translated to git-mv - write!(w, "{} {}", bn.replace(" ", "-"), ver) - } else { - write!(w, "{} {}", &self.meta.name[..], ver) - } - } else { - write!(w, "{} {}", &self.meta.name[..], ver) - } - } - - pub fn print_help(&self) -> ClapResult<()> { - let out = io::stdout(); - let mut buf_w = BufWriter::new(out.lock()); - self.write_help(&mut buf_w) - } - - pub fn write_help(&self, w: &mut W) -> ClapResult<()> { - Help::write_parser_help(w, self, false) - } - - pub fn write_long_help(&self, w: &mut W) -> ClapResult<()> { - Help::write_parser_help(w, self, true) - } - - pub fn write_help_err(&self, w: &mut W) -> ClapResult<()> { - Help::write_parser_help_to_stderr(w, self) - } - - pub fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { - debugln!("Parser::add_defaults;"); - macro_rules! add_val { - (@default $_self:ident, $a:ident, $m:ident) => { - if let Some(ref val) = $a.v.default_val { - debugln!("Parser::add_defaults:iter:{}: has default vals", $a.b.name); - if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) { - debugln!("Parser::add_defaults:iter:{}: has no user defined vals", $a.b.name); - $_self.add_val_to_arg($a, OsStr::new(val), $m)?; - - if $_self.cache.map_or(true, |name| name != $a.name()) { - $_self.cache = Some($a.name()); - } - } else if $m.get($a.b.name).is_some() { - debugln!("Parser::add_defaults:iter:{}: has user defined vals", $a.b.name); - } else { - debugln!("Parser::add_defaults:iter:{}: wasn't used", $a.b.name); - - $_self.add_val_to_arg($a, OsStr::new(val), $m)?; - - if $_self.cache.map_or(true, |name| name != $a.name()) { - $_self.cache = Some($a.name()); - } - } - } else { - debugln!("Parser::add_defaults:iter:{}: doesn't have default vals", $a.b.name); - } - }; - ($_self:ident, $a:ident, $m:ident) => { - if let Some(ref vm) = $a.v.default_vals_ifs { - sdebugln!(" has conditional defaults"); - let mut done = false; - if $m.get($a.b.name).is_none() { - for &(arg, val, default) in vm.values() { - let add = if let Some(a) = $m.get(arg) { - if let Some(v) = val { - a.vals.iter().any(|value| v == value) - } else { - true - } - } else { - false - }; - if add { - $_self.add_val_to_arg($a, OsStr::new(default), $m)?; - if $_self.cache.map_or(true, |name| name != $a.name()) { - $_self.cache = Some($a.name()); - } - done = true; - break; - } - } - } - - if done { - continue; // outer loop (outside macro) - } - } else { - sdebugln!(" doesn't have conditional defaults"); - } - add_val!(@default $_self, $a, $m) - }; - } - - for o in &self.opts { - debug!("Parser::add_defaults:iter:{}:", o.b.name); - add_val!(self, o, matcher); - } - for p in self.positionals.values() { - debug!("Parser::add_defaults:iter:{}:", p.b.name); - add_val!(self, p, matcher); - } - Ok(()) - } - - pub fn add_env(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { - macro_rules! add_val { - ($_self:ident, $a:ident, $m:ident) => { - if let Some(ref val) = $a.v.env { - if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) { - if let Some(ref val) = val.1 { - $_self.add_val_to_arg($a, OsStr::new(val), $m)?; - - if $_self.cache.map_or(true, |name| name != $a.name()) { - $_self.cache = Some($a.name()); - } - } - } else { - if let Some(ref val) = val.1 { - $_self.add_val_to_arg($a, OsStr::new(val), $m)?; - - if $_self.cache.map_or(true, |name| name != $a.name()) { - $_self.cache = Some($a.name()); - } - } - } - } - }; - } - - for o in &self.opts { - add_val!(self, o, matcher); - } - for p in self.positionals.values() { - add_val!(self, p, matcher); - } - Ok(()) - } - - pub fn flags(&self) -> Iter> { self.flags.iter() } - - pub fn opts(&self) -> Iter> { self.opts.iter() } - - pub fn positionals(&self) -> map::Values> { self.positionals.values() } - - pub fn subcommands(&self) -> Iter { self.subcommands.iter() } - - // Should we color the output? None=determined by output location, true=yes, false=no - #[doc(hidden)] - pub fn color(&self) -> ColorWhen { - debugln!("Parser::color;"); - debug!("Parser::color: Color setting..."); - if self.is_set(AS::ColorNever) { - sdebugln!("Never"); - ColorWhen::Never - } else if self.is_set(AS::ColorAlways) { - sdebugln!("Always"); - ColorWhen::Always - } else { - sdebugln!("Auto"); - ColorWhen::Auto - } - } - - 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); - } - if let Some(o) = find_by_name!(self, name, opts, iter) { - return Some(o); - } - if let Some(p) = find_by_name!(self, name, positionals, values) { - return Some(p); - } - None - } - - /// Check is a given string matches the binary name for this parser - fn is_bin_name(&self, value: &str) -> bool { - self.meta - .bin_name - .as_ref() - .and_then(|name| Some(value == name)) - .unwrap_or(false) - } - - /// Check is a given string is an alias for this parser - fn is_alias(&self, value: &str) -> bool { - self.meta - .aliases - .as_ref() - .and_then(|aliases| { - for alias in aliases { - if alias.0 == value { - return Some(true); - } - } - Some(false) - }) - .unwrap_or(false) - } - - // Only used for completion scripts due to bin_name messiness - #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))] - pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> { - debugln!("Parser::find_subcommand: sc={}", sc); - debugln!( - "Parser::find_subcommand: Currently in Parser...{}", - self.meta.bin_name.as_ref().unwrap() - ); - for s in &self.subcommands { - if s.p.is_bin_name(sc) { - return Some(s); - } - // XXX: why do we split here? - // isn't `sc` supposed to be single word already? - let last = sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG); - if s.p.is_alias(last) { - return Some(s); - } - - if let Some(app) = s.p.find_subcommand(sc) { - return Some(app); - } - } - None - } - - #[inline] - fn contains_long(&self, l: &str) -> bool { longs!(self).any(|al| al == &l) } - - #[inline] - fn contains_short(&self, s: char) -> bool { shorts!(self).any(|arg_s| arg_s == &s) } -} diff --git a/src/app/usage.rs b/src/app/usage.rs deleted file mode 100644 index 609058843c84..000000000000 --- a/src/app/usage.rs +++ /dev/null @@ -1,479 +0,0 @@ -// std -use std::collections::{BTreeMap, VecDeque}; - -// Internal -use INTERNAL_ERROR_MSG; -use args::{AnyArg, ArgMatcher, PosBuilder}; -use args::settings::ArgSettings; -use app::settings::AppSettings as AS; -use app::parser::Parser; - -// Creates a usage string for display. This happens just after all arguments were parsed, but before -// any subcommands have been parsed (so as to give subcommands their own usage recursively) -pub fn create_usage_with_title(p: &Parser, used: &[&str]) -> String { - debugln!("usage::create_usage_with_title;"); - let mut usage = String::with_capacity(75); - usage.push_str("USAGE:\n "); - usage.push_str(&*create_usage_no_title(p, used)); - usage -} - -// Creates a usage string to be used in error message (i.e. one with currently used args) -pub fn create_error_usage<'a, 'b>( - p: &Parser<'a, 'b>, - 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!(p, **n, opts, iter) { - !o.b.is_set(ArgSettings::Required) && !o.b.is_set(ArgSettings::Hidden) - } else if let Some(p) = find_by_name!(p, **n, positionals, values) { - !p.b.is_set(ArgSettings::Required) && p.b.is_set(ArgSettings::Hidden) - } else { - true // flags can't be required, so they're always true - } - }) - .map(|&n| n) - .collect(); - if let Some(r) = extra { - args.push(r); - } - create_usage_with_title(p, &*args) -} - -// Creates a usage string (*without title*) if one was not provided by the user manually. -pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String { - debugln!("usage::create_usage_no_title;"); - if let Some(u) = p.meta.usage_str { - String::from(&*u) - } else if used.is_empty() { - create_help_usage(p, true) - } else { - create_smart_usage(p, used) - } -} - -// Creates a usage string for display in help messages (i.e. not for errors) -pub fn create_help_usage(p: &Parser, incl_reqs: bool) -> String { - let mut usage = String::with_capacity(75); - let name = p.meta - .usage - .as_ref() - .unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name)); - usage.push_str(&*name); - let req_string = if incl_reqs { - let mut reqs: Vec<&str> = p.required().map(|r| &**r).collect(); - reqs.sort(); - reqs.dedup(); - get_required_usage_from(p, &reqs, None, None, false) - .iter() - .fold(String::new(), |a, s| a + &format!(" {}", s)[..]) - } else { - String::new() - }; - - let flags = needs_flags_tag(p); - if flags && !p.is_set(AS::UnifiedHelpMessage) { - usage.push_str(" [FLAGS]"); - } else if flags { - usage.push_str(" [OPTIONS]"); - } - if !p.is_set(AS::UnifiedHelpMessage) && p.opts.iter().any(|o| { - !o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden) - }) { - usage.push_str(" [OPTIONS]"); - } - - usage.push_str(&req_string[..]); - - let has_last = p.positionals.values().any(|p| p.is_set(ArgSettings::Last)); - // places a '--' in the usage string if there are args and options - // supporting multiple values - if p.opts.iter().any(|o| o.is_set(ArgSettings::Multiple)) - && p.positionals - .values() - .any(|p| !p.is_set(ArgSettings::Required)) - && !(p.has_visible_subcommands() || p.is_set(AS::AllowExternalSubcommands)) - && !has_last - { - usage.push_str(" [--]"); - } - let not_req_or_hidden = |p: &PosBuilder| { - (!p.is_set(ArgSettings::Required) || p.is_set(ArgSettings::Last)) - && !p.is_set(ArgSettings::Hidden) - }; - if p.has_positionals() && p.positionals.values().any(not_req_or_hidden) { - if let Some(args_tag) = get_args_tag(p, incl_reqs) { - usage.push_str(&*args_tag); - } else { - usage.push_str(" [ARGS]"); - } - if has_last && incl_reqs { - let pos = p.positionals - .values() - .find(|p| p.b.is_set(ArgSettings::Last)) - .expect(INTERNAL_ERROR_MSG); - debugln!("usage::create_help_usage: '{}' has .last(true)", pos.name()); - let req = pos.is_set(ArgSettings::Required); - if req - && p.positionals - .values() - .any(|p| !p.is_set(ArgSettings::Required)) - { - usage.push_str(" -- <"); - } else if req { - usage.push_str(" [--] <"); - } else { - usage.push_str(" [-- <"); - } - usage.push_str(&*pos.name_no_brackets()); - usage.push_str(">"); - usage.push_str(pos.multiple_str()); - if !req { - usage.push_str("]"); - } - } - } - - // incl_reqs is only false when this function is called recursively - if p.has_visible_subcommands() && incl_reqs || p.is_set(AS::AllowExternalSubcommands) { - if p.is_set(AS::SubcommandsNegateReqs) || p.is_set(AS::ArgsNegateSubcommands) { - if !p.is_set(AS::ArgsNegateSubcommands) { - usage.push_str("\n "); - usage.push_str(&*create_help_usage(p, false)); - usage.push_str(" "); - } else { - usage.push_str("\n "); - usage.push_str(&*name); - usage.push_str(" "); - } - } else if p.is_set(AS::SubcommandRequired) || p.is_set(AS::SubcommandRequiredElseHelp) { - usage.push_str(" "); - } else { - usage.push_str(" [SUBCOMMAND]"); - } - } - usage.shrink_to_fit(); - debugln!("usage::create_help_usage: usage={}", usage); - usage -} - -// Creates a context aware usage string, or "smart usage" from currently used -// args, and requirements -fn create_smart_usage(p: &Parser, used: &[&str]) -> String { - debugln!("usage::smart_usage;"); - let mut usage = String::with_capacity(75); - let mut hs: Vec<&str> = p.required().map(|s| &**s).collect(); - hs.extend_from_slice(used); - - let r_string = get_required_usage_from(p, &hs, None, None, false) - .iter() - .fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]); - - usage.push_str( - &p.meta - .usage - .as_ref() - .unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name))[..], - ); - usage.push_str(&*r_string); - if p.is_set(AS::SubcommandRequired) { - usage.push_str(" "); - } - usage.shrink_to_fit(); - usage -} - -// Gets the `[ARGS]` tag for the usage string -fn get_args_tag(p: &Parser, incl_reqs: bool) -> Option { - debugln!("usage::get_args_tag;"); - let mut count = 0; - 'outer: for pos in p.positionals - .values() - .filter(|pos| !pos.is_set(ArgSettings::Required)) - .filter(|pos| !pos.is_set(ArgSettings::Hidden)) - .filter(|pos| !pos.is_set(ArgSettings::Last)) - { - debugln!("usage::get_args_tag:iter:{}:", pos.b.name); - if let Some(g_vec) = p.groups_for_arg(pos.b.name) { - for grp_s in &g_vec { - debugln!("usage::get_args_tag:iter:{}:iter:{};", pos.b.name, grp_s); - // if it's part of a required group we don't want to count it - if p.groups.iter().any(|g| g.required && (&g.name == grp_s)) { - continue 'outer; - } - } - } - count += 1; - debugln!( - "usage::get_args_tag:iter: {} Args not required or hidden", - count - ); - } - if !p.is_set(AS::DontCollapseArgsInUsage) && count > 1 { - debugln!("usage::get_args_tag:iter: More than one, returning [ARGS]"); - return None; // [ARGS] - } else if count == 1 && incl_reqs { - let pos = p.positionals - .values() - .find(|pos| { - !pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Hidden) - && !pos.is_set(ArgSettings::Last) - }) - .expect(INTERNAL_ERROR_MSG); - debugln!( - "usage::get_args_tag:iter: Exactly one, returning '{}'", - pos.name() - ); - return Some(format!( - " [{}]{}", - pos.name_no_brackets(), - pos.multiple_str() - )); - } else if p.is_set(AS::DontCollapseArgsInUsage) && !p.positionals.is_empty() && incl_reqs { - debugln!("usage::get_args_tag:iter: Don't collapse returning all"); - return Some( - p.positionals - .values() - .filter(|pos| !pos.is_set(ArgSettings::Required)) - .filter(|pos| !pos.is_set(ArgSettings::Hidden)) - .filter(|pos| !pos.is_set(ArgSettings::Last)) - .map(|pos| { - format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()) - }) - .collect::>() - .join(""), - ); - } else if !incl_reqs { - debugln!("usage::get_args_tag:iter: incl_reqs=false, building secondary usage string"); - let highest_req_pos = p.positionals - .iter() - .filter_map(|(idx, pos)| { - if pos.b.is_set(ArgSettings::Required) && !pos.b.is_set(ArgSettings::Last) { - Some(idx) - } else { - None - } - }) - .max() - .unwrap_or_else(|| p.positionals.len()); - return Some( - p.positionals - .iter() - .filter_map(|(idx, pos)| { - if idx <= highest_req_pos { - Some(pos) - } else { - None - } - }) - .filter(|pos| !pos.is_set(ArgSettings::Required)) - .filter(|pos| !pos.is_set(ArgSettings::Hidden)) - .filter(|pos| !pos.is_set(ArgSettings::Last)) - .map(|pos| { - format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()) - }) - .collect::>() - .join(""), - ); - } - Some("".into()) -} - -// Determines if we need the `[FLAGS]` tag in the usage string -fn needs_flags_tag(p: &Parser) -> bool { - debugln!("usage::needs_flags_tag;"); - 'outer: for f in &p.flags { - debugln!("usage::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) = p.groups_for_arg(f.b.name) { - for grp_s in &g_vec { - debugln!("usage::needs_flags_tag:iter:iter: grp_s={};", grp_s); - if p.groups.iter().any(|g| &g.name == grp_s && g.required) { - debugln!("usage::needs_flags_tag:iter:iter: Group is required"); - continue 'outer; - } - } - } - if f.is_set(ArgSettings::Hidden) { - continue; - } - debugln!("usage::needs_flags_tag:iter: [FLAGS] required"); - return true; - } - - debugln!("usage::needs_flags_tag: [FLAGS] not required"); - false -} - -// Returns the required args in usage string form by fully unrolling all groups -pub fn get_required_usage_from<'a, 'b>( - p: &Parser<'a, 'b>, - reqs: &[&'a str], - matcher: Option<&ArgMatcher<'a>>, - extra: Option<&str>, - incl_last: bool, -) -> VecDeque { - debugln!( - "usage::get_required_usage_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) = p.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!("usage::get_required_usage_from:iter:{}: adding group req={:?}", - $a, r); - $v.push(r); - } - } - } - }}; - ($a:ident, $what:ident, $how:ident, $v:ident, $p:ident) => {{ - if let Some(rl) = p.$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!("usage::get_required_usage_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!( - "usage::get_required_usage_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!("usage::get_required_usage_from: no more children"); - break; - } else { - debugln!("usage::get_required_usage_from: after iter tmp={:?}", tmp); - debugln!( - "usage::get_required_usage_from: after iter new_reqs={:?}", - new_reqs - ); - desc_reqs.extend_from_slice(&*new_reqs); - new_reqs.clear(); - new_reqs.extend_from_slice(&*tmp); - debugln!( - "usage::get_required_usage_from: after iter desc_reqs={:?}", - desc_reqs - ); - } - } - desc_reqs.extend_from_slice(reqs); - desc_reqs.sort(); - desc_reqs.dedup(); - debugln!( - "usage::get_required_usage_from: final desc_reqs={:?}", - desc_reqs - ); - let mut ret_val = VecDeque::new(); - let args_in_groups = p.groups - .iter() - .filter(|gn| desc_reqs.contains(&gn.name)) - .flat_map(|g| p.arg_names_in_group(g.name)) - .collect::>(); - - let pmap = if let Some(m) = matcher { - desc_reqs - .iter() - .filter(|a| p.positionals.values().any(|p| &&p.b.name == a)) - .filter(|&pos| !m.contains(pos)) - .filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos)) - .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last)) - .filter(|pos| !args_in_groups.contains(&pos.b.name)) - .map(|pos| (pos.index, pos)) - .collect::>() // sort by index - } else { - desc_reqs - .iter() - .filter(|a| p.positionals.values().any(|pos| &&pos.b.name == a)) - .filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos)) - .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last)) - .filter(|pos| !args_in_groups.contains(&pos.b.name)) - .map(|pos| (pos.index, pos)) - .collect::>() // sort by index - }; - debugln!( - "usage::get_required_usage_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| !p.positionals.values().any(|p| &&p.b.name == name)) - .filter(|name| !p.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!("usage::get_required_usage_from:iter:{}:", a); - let arg = find_by_name!(p, *a, flags, iter) - .map(|f| f.to_string()) - .unwrap_or_else(|| { - find_by_name!(p, *a, opts, iter) - .map(|o| o.to_string()) - .expect(INTERNAL_ERROR_MSG) - }); - ret_val.push_back(arg); - } - let mut g_vec: Vec = vec![]; - for g in desc_reqs - .iter() - .filter(|n| p.groups.iter().any(|g| &&g.name == n)) - { - let g_string = p.args_in_group(g).join("|"); - let elem = format!("<{}>", &g_string[..g_string.len()]); - if !g_vec.contains(&elem) { - g_vec.push(elem); - } - } - for g in g_vec { - ret_val.push_back(g); - } - - ret_val -} diff --git a/src/app/validator.rs b/src/app/validator.rs deleted file mode 100644 index 181b831d0b3c..000000000000 --- a/src/app/validator.rs +++ /dev/null @@ -1,573 +0,0 @@ -// std -use std::fmt::Display; -#[allow(deprecated, unused_imports)] -use std::ascii::AsciiExt; - -// Internal -use INTERNAL_ERROR_MSG; -use INVALID_UTF8; -use args::{AnyArg, ArgMatcher, MatchedArg}; -use args::settings::ArgSettings; -use errors::{Error, ErrorKind}; -use errors::Result as ClapResult; -use app::settings::AppSettings as AS; -use app::parser::{ParseResult, Parser}; -use fmt::{Colorizer, ColorizerOption}; -use app::usage; - -pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>) -where - 'a: 'b, - 'b: 'z; - -impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { - pub fn new(p: &'z mut Parser<'a, 'b>) -> Self { Validator(p) } - - pub fn validate( - &mut self, - needs_val_of: ParseResult<'a>, - subcmd_name: Option, - matcher: &mut ArgMatcher<'a>, - ) -> ClapResult<()> { - debugln!("Validator::validate;"); - let mut reqs_validated = false; - self.0.add_env(matcher)?; - self.0.add_defaults(matcher)?; - if let ParseResult::Opt(a) = needs_val_of { - debugln!("Validator::validate: needs_val_of={:?}", a); - let o = { - self.0 - .opts - .iter() - .find(|o| o.b.name == a) - .expect(INTERNAL_ERROR_MSG) - .clone() - }; - 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 { - true - }; - if should_err { - return Err(Error::empty_value( - &o, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - } - - if matcher.is_empty() && matcher.subcommand_name().is_none() - && self.0.is_set(AS::ArgRequiredElseHelp) - { - let mut out = vec![]; - self.0.write_help_err(&mut out)?; - return Err(Error { - message: String::from_utf8_lossy(&*out).into_owned(), - kind: ErrorKind::MissingArgumentOrSubcommand, - info: None, - }); - } - self.validate_blacklist(matcher)?; - if !(self.0.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated { - self.validate_required(matcher)?; - } - self.validate_matched_args(matcher)?; - matcher.usage(usage::create_usage_with_title(self.0, &[])); - - Ok(()) - } - - fn validate_arg_values( - &self, - arg: &A, - ma: &MatchedArg, - matcher: &ArgMatcher<'a>, - ) -> ClapResult<()> - where - A: AnyArg<'a, 'b> + Display, - { - 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_arg_values: invalid UTF-8 found in val {:?}", - val - ); - return Err(Error::invalid_utf8( - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - if let Some(p_vals) = arg.possible_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)) - } else { - p_vals.contains(&&*val_str) - }; - if !ok { - return Err(Error::invalid_value( - val_str, - p_vals, - arg, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - } - if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty() - && matcher.contains(&*arg.name()) - { - debugln!("Validator::validate_arg_values: illegal empty val found"); - return Err(Error::empty_value( - arg, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - if let Some(vtor) = arg.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())); - } else { - sdebugln!("good"); - } - } - if let Some(vtor) = arg.validator_os() { - debug!("Validator::validate_arg_values: checking validator_os..."); - if let Err(e) = vtor(val) { - sdebugln!("error"); - return Err(Error::value_validation( - Some(arg), - (*e).to_string_lossy().to_string(), - self.0.color(), - )); - } else { - sdebugln!("good"); - } - } - } - Ok(()) - } - - 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))) - ); - 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 &conflicts { - debugln!( - "Validator::validate_blacklist:iter:{}: Checking blacklisted arg", - name - ); - let mut should_err = false; - if self.0.groups.iter().any(|g| &g.name == name) { - debugln!( - "Validator::validate_blacklist:iter:{}: groups contains it...", - name - ); - for n in self.0.arg_names_in_group(name) { - debugln!( - "Validator::validate_blacklist:iter:{}:iter:{}: looking in group...", - name, - n - ); - if matcher.contains(n) { - debugln!( - "Validator::validate_blacklist:iter:{}:iter:{}: matcher contains it...", - name, - n - ); - return self.build_err(n, matcher); - } - } - } else if let Some(ma) = matcher.get(name) { - debugln!( - "Validator::validate_blacklist:iter:{}: matcher contains it...", - name - ); - should_err = ma.occurs > 0; - } - if should_err { - return self.build_err(*name, matcher); - } - } - Ok(()) - } - - fn validate_matched_args(&self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { - debugln!("Validator::validate_matched_args;"); - for (name, ma) in matcher.iter() { - debugln!( - "Validator::validate_matched_args:iter:{}: vals={:#?}", - name, - ma.vals - ); - if let Some(opt) = find_by_name!(self.0, *name, opts, iter) { - self.validate_arg_num_vals(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) { - self.validate_arg_requires(flag, ma, matcher)?; - self.validate_arg_num_occurs(flag, ma, matcher)?; - } 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_arg_values(pos, ma, matcher)?; - self.validate_arg_requires(pos, ma, matcher)?; - } else { - let grp = self.0 - .groups - .iter() - .find(|g| &g.name == name) - .expect(INTERNAL_ERROR_MSG); - if let Some(ref g_reqs) = grp.requires { - if g_reqs.iter().any(|&n| !matcher.contains(n)) { - return self.missing_required_error(matcher, None); - } - } - } - } - Ok(()) - } - - fn validate_arg_num_occurs( - &self, - a: &A, - ma: &MatchedArg, - matcher: &ArgMatcher, - ) -> ClapResult<()> - where - A: AnyArg<'a, 'b> + Display, - { - debugln!("Validator::validate_arg_num_occurs: a={};", a.name()); - if ma.occurs > 1 && !a.is_set(ArgSettings::Multiple) { - // Not the first time, and we don't allow multiples - return Err(Error::unexpected_multiple_usage( - a, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - Ok(()) - } - - fn validate_arg_num_vals( - &self, - a: &A, - ma: &MatchedArg, - matcher: &ArgMatcher, - ) -> ClapResult<()> - where - A: AnyArg<'a, 'b> + Display, - { - debugln!("Validator::validate_arg_num_vals:{}", a.name()); - if let Some(num) = a.num_vals() { - debugln!("Validator::validate_arg_num_vals: num_vals set...{}", num); - let should_err = if a.is_set(ArgSettings::Multiple) { - ((ma.vals.len() as u64) % num) != 0 - } else { - num != (ma.vals.len() as u64) - }; - if should_err { - debugln!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues"); - return Err(Error::wrong_number_of_values( - a, - num, - if a.is_set(ArgSettings::Multiple) { - (ma.vals.len() % num as usize) - } else { - ma.vals.len() - }, - if ma.vals.len() == 1 - || (a.is_set(ArgSettings::Multiple) && (ma.vals.len() % num as usize) == 1) - { - "as" - } else { - "ere" - }, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - } - if let Some(num) = a.max_vals() { - debugln!("Validator::validate_arg_num_vals: max_vals set...{}", num); - if (ma.vals.len() as u64) > num { - debugln!("Validator::validate_arg_num_vals: Sending error TooManyValues"); - return Err(Error::too_many_values( - ma.vals - .iter() - .last() - .expect(INTERNAL_ERROR_MSG) - .to_str() - .expect(INVALID_UTF8), - a, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - } - let min_vals_zero = if let Some(num) = a.min_vals() { - debugln!("Validator::validate_arg_num_vals: min_vals set: {}", num); - if (ma.vals.len() as u64) < num && num != 0 { - debugln!("Validator::validate_arg_num_vals: Sending error TooFewValues"); - return Err(Error::too_few_values( - a, - num, - ma.vals.len(), - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - num == 0 - } else { - false - }; - // Issue 665 (https://github.com/clap-rs/clap/issues/665) - // Issue 1105 (https://github.com/clap-rs/clap/issues/1105) - if a.takes_value() && !min_vals_zero && ma.vals.is_empty() { - return Err(Error::empty_value( - a, - &*usage::create_error_usage(self.0, matcher, None), - self.0.color(), - )); - } - Ok(()) - } - - fn validate_arg_requires( - &self, - a: &A, - ma: &MatchedArg, - matcher: &ArgMatcher, - ) -> ClapResult<()> - where - A: AnyArg<'a, 'b> + Display, - { - 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 = - |v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name); - if ma.vals.iter().any(missing_req) { - 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(()) - } - - fn validate_required(&mut self, matcher: &ArgMatcher) -> ClapResult<()> { - debugln!( - "Validator::validate_required: required={:?};", - self.0.required - ); - - let mut should_err = false; - let mut to_rem = Vec::new(); - for name in &self.0.required { - debugln!("Validator::validate_required:iter:{}:", name); - if matcher.contains(name) { - continue; - } - if to_rem.contains(name) { - continue; - } else if let Some(a) = find_any_by_name!(self.0, *name) { - if self.is_missing_required_ok(a, matcher) { - to_rem.push(a.name()); - if let Some(reqs) = a.requires() { - for r in reqs - .iter() - .filter(|&&(val, _)| val.is_none()) - .map(|&(_, name)| name) - { - to_rem.push(r); - } - } - continue; - } - } - should_err = true; - break; - } - if should_err { - for r in &to_rem { - 'inner: for i in (0 .. self.0.required.len()).rev() { - if &self.0.required[i] == r { - self.0.required.swap_remove(i); - break 'inner; - } - } - } - return self.missing_required_error(matcher, None); - } - - // Validate the conditionally required args - for &(a, v, r) in &self.0.r_ifs { - if let Some(ma) = matcher.get(a) { - if matcher.get(r).is_none() && ma.vals.iter().any(|val| val == v) { - return self.missing_required_error(matcher, Some(r)); - } - } - } - Ok(()) - } - - 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) - || self.0 - .groups - .iter() - .find(|g| &g.name == conf) - .map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg))) - }) - }) - } - - 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) => {{ - $a.required_unless().map(|ru| { - ru.iter().$how(|n| { - $m.contains(n) || { - if let Some(grp) = $_self.groups.iter().find(|g| &g.name == n) { - grp.args.iter().any(|arg| $m.contains(arg)) - } else { - false - } - } - }) - }) - }}; - } - if a.is_set(ArgSettings::RequiredUnlessAll) { - check!(all, self.0, a, matcher) - } else { - check!(any, self.0, a, matcher) - } - } - - fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> { - debugln!("Validator::missing_required_error: extra={:?}", extra); - let c = Colorizer::new(ColorizerOption { - use_stderr: true, - when: self.0.color(), - }); - let mut reqs = self.0.required.iter().map(|&r| &*r).collect::>(); - if let Some(r) = extra { - reqs.push(r); - } - reqs.retain(|n| !matcher.contains(n)); - reqs.dedup(); - debugln!("Validator::missing_required_error: reqs={:#?}", reqs); - let req_args = - usage::get_required_usage_from(self.0, &reqs[..], Some(matcher), extra, true) - .iter() - .fold(String::new(), |acc, s| { - acc + &format!("\n {}", c.error(s))[..] - }); - debugln!( - "Validator::missing_required_error: req_args={:#?}", - req_args - ); - Err(Error::missing_required_argument( - &*req_args, - &*usage::create_error_usage(self.0, matcher, extra), - self.0.color(), - )) - } - - #[inline] - fn is_missing_required_ok(&self, a: &AnyArg, matcher: &ArgMatcher) -> bool { - debugln!("Validator::is_missing_required_ok: a={}", a.name()); - 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 deleted file mode 100644 index eee522833286..000000000000 --- a/src/args/any_arg.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Std -use std::rc::Rc; -use std::fmt as std_fmt; -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 { - fn name(&self) -> &'n str; - fn overrides(&self) -> Option<&[&'e str]>; - fn aliases(&self) -> Option>; - fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]>; - fn blacklist(&self) -> Option<&[&'e str]>; - fn required_unless(&self) -> Option<&[&'e str]>; - fn is_set(&self, ArgSettings) -> bool; - fn set(&mut self, ArgSettings); - fn has_switch(&self) -> bool; - fn max_vals(&self) -> Option; - fn min_vals(&self) -> Option; - fn num_vals(&self) -> Option; - fn possible_vals(&self) -> Option<&[&'e str]>; - fn validator(&self) -> Option<&Rc Result<(), String>>>; - fn validator_os(&self) -> Option<&Rc Result<(), OsString>>>; - fn short(&self) -> Option; - fn long(&self) -> Option<&'e str>; - fn val_delim(&self) -> Option; - fn takes_value(&self) -> bool; - fn val_names(&self) -> Option<&VecMap<&'e str>>; - fn help(&self) -> Option<&'e str>; - fn long_help(&self) -> Option<&'e str>; - fn default_val(&self) -> Option<&'e OsStr>; - fn default_vals_ifs(&self) -> Option, &'e OsStr)>>; - fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)>; - fn longest_filter(&self) -> bool; - fn val_terminator(&self) -> Option<&'e str>; -} - -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_builder/base.rs b/src/args/arg_builder/base.rs deleted file mode 100644 index fef9d8ab979d..000000000000 --- a/src/args/arg_builder/base.rs +++ /dev/null @@ -1,38 +0,0 @@ -use args::{Arg, ArgFlags, ArgSettings}; - -#[derive(Debug, Clone, Default)] -pub struct Base<'a, 'b> -where - 'a: 'b, -{ - pub name: &'a str, - pub help: Option<&'b str>, - pub long_help: Option<&'b str>, - pub blacklist: Option>, - pub settings: ArgFlags, - pub r_unless: Option>, - pub overrides: Option>, - pub groups: Option>, - pub requires: Option, &'a str)>>, -} - -impl<'n, 'e> Base<'n, 'e> { - pub fn new(name: &'n str) -> Self { - Base { - name: name, - ..Default::default() - } - } - - pub fn set(&mut self, s: ArgSettings) { self.settings.set(s); } - pub fn unset(&mut self, s: ArgSettings) { self.settings.unset(s); } - pub fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) } -} - -impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Base<'n, 'e> { - fn from(a: &'z Arg<'n, 'e>) -> Self { a.b.clone() } -} - -impl<'n, 'e> PartialEq for Base<'n, 'e> { - fn eq(&self, other: &Base<'n, 'e>) -> bool { self.name == other.name } -} diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs deleted file mode 100644 index 641e7777e00d..000000000000 --- a/src/args/arg_builder/flag.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Std -use std::convert::From; -use std::fmt::{Display, Formatter, Result}; -use std::rc::Rc; -use std::result::Result as StdResult; -use std::ffi::{OsStr, OsString}; -use std::mem; - -// Internal -use Arg; -use args::{AnyArg, ArgSettings, Base, DispOrder, Switched}; -use map::{self, VecMap}; - -#[derive(Default, Clone, Debug)] -#[doc(hidden)] -pub struct FlagBuilder<'n, 'e> -where - 'n: 'e, -{ - pub b: Base<'n, 'e>, - pub s: Switched<'e>, -} - -impl<'n, 'e> FlagBuilder<'n, 'e> { - pub fn new(name: &'n str) -> Self { - FlagBuilder { - b: Base::new(name), - ..Default::default() - } - } -} - -impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> { - fn from(a: &'z Arg<'a, 'b>) -> Self { - FlagBuilder { - b: Base::from(a), - s: Switched::from(a), - } - } -} - -impl<'a, 'b> From> for FlagBuilder<'a, 'b> { - fn from(mut a: Arg<'a, 'b>) -> Self { - FlagBuilder { - b: mem::replace(&mut a.b, Base::default()), - s: mem::replace(&mut a.s, Switched::default()), - } - } -} - -impl<'n, 'e> Display for FlagBuilder<'n, 'e> { - fn fmt(&self, f: &mut Formatter) -> Result { - if let Some(l) = self.s.long { - write!(f, "--{}", l)?; - } else { - write!(f, "-{}", self.s.short.unwrap())?; - } - - Ok(()) - } -} - -impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { - fn name(&self) -> &'n str { self.b.name } - fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) } - fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> { - self.b.requires.as_ref().map(|o| &o[..]) - } - fn blacklist(&self) -> Option<&[&'e str]> { self.b.blacklist.as_ref().map(|o| &o[..]) } - fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) } - fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) } - fn has_switch(&self) -> bool { true } - fn takes_value(&self) -> bool { false } - fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) } - fn max_vals(&self) -> Option { None } - fn val_names(&self) -> Option<&VecMap<&'e str>> { None } - fn num_vals(&self) -> Option { None } - fn possible_vals(&self) -> Option<&[&'e str]> { None } - fn validator(&self) -> Option<&Rc StdResult<(), String>>> { None } - fn validator_os(&self) -> Option<&Rc StdResult<(), OsString>>> { None } - fn min_vals(&self) -> Option { None } - fn short(&self) -> Option { self.s.short } - fn long(&self) -> Option<&'e str> { self.s.long } - fn val_delim(&self) -> Option { None } - fn help(&self) -> Option<&'e str> { self.b.help } - fn long_help(&self) -> Option<&'e str> { self.b.long_help } - fn val_terminator(&self) -> Option<&'e str> { None } - fn default_val(&self) -> Option<&'e OsStr> { None } - fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { - None - } - fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> { None } - fn longest_filter(&self) -> bool { self.s.long.is_some() } - fn aliases(&self) -> Option> { - if let Some(ref aliases) = self.s.aliases { - let vis_aliases: Vec<_> = aliases - .iter() - .filter_map(|&(n, v)| if v { Some(n) } else { None }) - .collect(); - if vis_aliases.is_empty() { - None - } else { - Some(vis_aliases) - } - } else { - None - } - } -} - -impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> { - fn disp_ord(&self) -> usize { self.s.disp_ord } -} - -impl<'n, 'e> PartialEq for FlagBuilder<'n, 'e> { - fn eq(&self, other: &FlagBuilder<'n, 'e>) -> bool { self.b == other.b } -} - -#[cfg(test)] -mod test { - use args::settings::ArgSettings; - use super::FlagBuilder; - - #[test] - fn flagbuilder_display() { - let mut f = FlagBuilder::new("flg"); - f.b.settings.set(ArgSettings::Multiple); - f.s.long = Some("flag"); - - assert_eq!(&*format!("{}", f), "--flag"); - - let mut f2 = FlagBuilder::new("flg"); - f2.s.short = Some('f'); - - assert_eq!(&*format!("{}", f2), "-f"); - } - - #[test] - fn flagbuilder_display_single_alias() { - let mut f = FlagBuilder::new("flg"); - f.s.long = Some("flag"); - f.s.aliases = Some(vec![("als", true)]); - - assert_eq!(&*format!("{}", f), "--flag"); - } - - #[test] - fn flagbuilder_display_multiple_aliases() { - let mut f = FlagBuilder::new("flg"); - f.s.short = Some('f'); - f.s.aliases = Some(vec![ - ("alias_not_visible", false), - ("f2", true), - ("f3", true), - ("f4", true), - ]); - assert_eq!(&*format!("{}", f), "-f"); - } -} diff --git a/src/args/arg_builder/mod.rs b/src/args/arg_builder/mod.rs deleted file mode 100644 index d1a7a6608668..000000000000 --- a/src/args/arg_builder/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub use self::flag::FlagBuilder; -pub use self::option::OptBuilder; -pub use self::positional::PosBuilder; -pub use self::base::Base; -pub use self::switched::Switched; -pub use self::valued::Valued; - -mod flag; -mod positional; -mod option; -mod base; -mod valued; -mod switched; diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs deleted file mode 100644 index 4bb147a7d2d0..000000000000 --- a/src/args/arg_builder/option.rs +++ /dev/null @@ -1,244 +0,0 @@ -// Std -use std::fmt::{Display, Formatter, Result}; -use std::rc::Rc; -use std::result::Result as StdResult; -use std::ffi::{OsStr, OsString}; -use std::mem; - -// Internal -use args::{AnyArg, Arg, ArgSettings, Base, DispOrder, Switched, Valued}; -use map::{self, VecMap}; -use INTERNAL_ERROR_MSG; - -#[allow(missing_debug_implementations)] -#[doc(hidden)] -#[derive(Default, Clone)] -pub struct OptBuilder<'n, 'e> -where - 'n: 'e, -{ - pub b: Base<'n, 'e>, - pub s: Switched<'e>, - pub v: Valued<'n, 'e>, -} - -impl<'n, 'e> OptBuilder<'n, 'e> { - pub fn new(name: &'n str) -> Self { - OptBuilder { - b: Base::new(name), - ..Default::default() - } - } -} - -impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for OptBuilder<'n, 'e> { - fn from(a: &'z Arg<'n, 'e>) -> Self { - OptBuilder { - b: Base::from(a), - s: Switched::from(a), - v: Valued::from(a), - } - } -} - -impl<'n, 'e> From> for OptBuilder<'n, 'e> { - fn from(mut a: Arg<'n, 'e>) -> Self { - a.v.fill_in(); - OptBuilder { - b: mem::replace(&mut a.b, Base::default()), - s: mem::replace(&mut a.s, Switched::default()), - v: mem::replace(&mut a.v, Valued::default()), - } - } -} - -impl<'n, 'e> Display for OptBuilder<'n, 'e> { - fn fmt(&self, f: &mut Formatter) -> Result { - debugln!("OptBuilder::fmt:{}", self.b.name); - let sep = if self.b.is_set(ArgSettings::RequireEquals) { - "=" - } else { - " " - }; - // Write the name such --long or -l - if let Some(l) = self.s.long { - write!(f, "--{}{}", l, sep)?; - } else { - write!(f, "-{}{}", self.s.short.unwrap(), sep)?; - } - let delim = if self.is_set(ArgSettings::RequireDelimiter) { - self.v.val_delim.expect(INTERNAL_ERROR_MSG) - } else { - ' ' - }; - - // Write the values such as - if let Some(ref vec) = self.v.val_names { - let mut it = vec.iter().peekable(); - while let Some((_, val)) = it.next() { - write!(f, "<{}>", val)?; - if it.peek().is_some() { - write!(f, "{}", delim)?; - } - } - let num = vec.len(); - if self.is_set(ArgSettings::Multiple) && num == 1 { - write!(f, "...")?; - } - } else if let Some(num) = self.v.num_vals { - let mut it = (0..num).peekable(); - while let Some(_) = it.next() { - write!(f, "<{}>", self.b.name)?; - if it.peek().is_some() { - write!(f, "{}", delim)?; - } - } - if self.is_set(ArgSettings::Multiple) && num == 1 { - write!(f, "...")?; - } - } else { - write!( - f, - "<{}>{}", - self.b.name, - if self.is_set(ArgSettings::Multiple) { - "..." - } else { - "" - } - )?; - } - - Ok(()) - } -} - -impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> { - fn name(&self) -> &'n str { self.b.name } - fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) } - fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> { - self.b.requires.as_ref().map(|o| &o[..]) - } - fn blacklist(&self) -> Option<&[&'e str]> { self.b.blacklist.as_ref().map(|o| &o[..]) } - fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) } - fn val_names(&self) -> Option<&VecMap<&'e str>> { self.v.val_names.as_ref() } - fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) } - fn has_switch(&self) -> bool { true } - fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) } - fn max_vals(&self) -> Option { self.v.max_vals } - fn val_terminator(&self) -> Option<&'e str> { self.v.terminator } - fn num_vals(&self) -> Option { self.v.num_vals } - fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) } - fn validator(&self) -> Option<&Rc StdResult<(), String>>> { - self.v.validator.as_ref() - } - fn validator_os(&self) -> Option<&Rc StdResult<(), OsString>>> { - self.v.validator_os.as_ref() - } - fn min_vals(&self) -> Option { self.v.min_vals } - fn short(&self) -> Option { self.s.short } - fn long(&self) -> Option<&'e str> { self.s.long } - fn val_delim(&self) -> Option { self.v.val_delim } - fn takes_value(&self) -> bool { true } - fn help(&self) -> Option<&'e str> { self.b.help } - fn long_help(&self) -> Option<&'e str> { self.b.long_help } - fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val } - fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { - self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) - } - fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> { - self.v - .env - .as_ref() - .map(|&(key, ref value)| (key, value.as_ref())) - } - fn longest_filter(&self) -> bool { true } - fn aliases(&self) -> Option> { - if let Some(ref aliases) = self.s.aliases { - let vis_aliases: Vec<_> = aliases - .iter() - .filter_map(|&(n, v)| if v { Some(n) } else { None }) - .collect(); - if vis_aliases.is_empty() { - None - } else { - Some(vis_aliases) - } - } else { - None - } - } -} - -impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> { - fn disp_ord(&self) -> usize { self.s.disp_ord } -} - -impl<'n, 'e> PartialEq for OptBuilder<'n, 'e> { - fn eq(&self, other: &OptBuilder<'n, 'e>) -> bool { self.b == other.b } -} - -#[cfg(test)] -mod test { - use args::settings::ArgSettings; - use super::OptBuilder; - use map::VecMap; - - #[test] - fn optbuilder_display1() { - let mut o = OptBuilder::new("opt"); - o.s.long = Some("option"); - o.b.settings.set(ArgSettings::Multiple); - - assert_eq!(&*format!("{}", o), "--option ..."); - } - - #[test] - fn optbuilder_display2() { - let mut v_names = VecMap::new(); - v_names.insert(0, "file"); - v_names.insert(1, "name"); - - let mut o2 = OptBuilder::new("opt"); - o2.s.short = Some('o'); - o2.v.val_names = Some(v_names); - - assert_eq!(&*format!("{}", o2), "-o "); - } - - #[test] - fn optbuilder_display3() { - let mut v_names = VecMap::new(); - v_names.insert(0, "file"); - v_names.insert(1, "name"); - - let mut o2 = OptBuilder::new("opt"); - o2.s.short = Some('o'); - o2.v.val_names = Some(v_names); - o2.b.settings.set(ArgSettings::Multiple); - - assert_eq!(&*format!("{}", o2), "-o "); - } - - #[test] - fn optbuilder_display_single_alias() { - let mut o = OptBuilder::new("opt"); - o.s.long = Some("option"); - o.s.aliases = Some(vec![("als", true)]); - - assert_eq!(&*format!("{}", o), "--option "); - } - - #[test] - fn optbuilder_display_multiple_aliases() { - let mut o = OptBuilder::new("opt"); - o.s.long = Some("option"); - o.s.aliases = Some(vec![ - ("als_not_visible", false), - ("als2", true), - ("als3", true), - ("als4", true), - ]); - assert_eq!(&*format!("{}", o), "--option "); - } -} diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs deleted file mode 100644 index 43fdca4c5809..000000000000 --- a/src/args/arg_builder/positional.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Std -use std::borrow::Cow; -use std::fmt::{Display, Formatter, Result}; -use std::rc::Rc; -use std::result::Result as StdResult; -use std::ffi::{OsStr, OsString}; -use std::mem; - -// Internal -use Arg; -use args::{AnyArg, ArgSettings, Base, DispOrder, Valued}; -use INTERNAL_ERROR_MSG; -use map::{self, VecMap}; - -#[allow(missing_debug_implementations)] -#[doc(hidden)] -#[derive(Clone, Default)] -pub struct PosBuilder<'n, 'e> -where - 'n: 'e, -{ - pub b: Base<'n, 'e>, - pub v: Valued<'n, 'e>, - pub index: u64, -} - -impl<'n, 'e> PosBuilder<'n, 'e> { - pub fn new(name: &'n str, idx: u64) -> Self { - PosBuilder { - b: Base::new(name), - index: idx, - ..Default::default() - } - } - - pub fn from_arg_ref(a: &Arg<'n, 'e>, idx: u64) -> Self { - let mut pb = PosBuilder { - b: Base::from(a), - v: Valued::from(a), - index: idx, - }; - if a.v.max_vals.is_some() || a.v.min_vals.is_some() - || (a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1) - { - pb.b.settings.set(ArgSettings::Multiple); - } - pb - } - - pub fn from_arg(mut a: Arg<'n, 'e>, idx: u64) -> Self { - if a.v.max_vals.is_some() || a.v.min_vals.is_some() - || (a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1) - { - a.b.settings.set(ArgSettings::Multiple); - } - PosBuilder { - b: mem::replace(&mut a.b, Base::default()), - v: mem::replace(&mut a.v, Valued::default()), - index: idx, - } - } - - pub fn multiple_str(&self) -> &str { - let mult_vals = self.v - .val_names - .as_ref() - .map_or(true, |names| names.len() < 2); - if self.is_set(ArgSettings::Multiple) && mult_vals { - "..." - } else { - "" - } - } - - pub fn name_no_brackets(&self) -> Cow { - debugln!("PosBuilder::name_no_brackets;"); - let mut delim = String::new(); - delim.push(if self.is_set(ArgSettings::RequireDelimiter) { - self.v.val_delim.expect(INTERNAL_ERROR_MSG) - } else { - ' ' - }); - if let Some(ref names) = self.v.val_names { - debugln!("PosBuilder:name_no_brackets: val_names={:#?}", names); - if names.len() > 1 { - Cow::Owned( - names - .values() - .map(|n| format!("<{}>", n)) - .collect::>() - .join(&*delim), - ) - } else { - Cow::Borrowed(names.values().next().expect(INTERNAL_ERROR_MSG)) - } - } else { - debugln!("PosBuilder:name_no_brackets: just name"); - Cow::Borrowed(self.b.name) - } - } -} - -impl<'n, 'e> Display for PosBuilder<'n, 'e> { - fn fmt(&self, f: &mut Formatter) -> Result { - let mut delim = String::new(); - delim.push(if self.is_set(ArgSettings::RequireDelimiter) { - self.v.val_delim.expect(INTERNAL_ERROR_MSG) - } else { - ' ' - }); - if let Some(ref names) = self.v.val_names { - write!( - f, - "{}", - names - .values() - .map(|n| format!("<{}>", n)) - .collect::>() - .join(&*delim) - )?; - } else { - write!(f, "<{}>", self.b.name)?; - } - if self.b.settings.is_set(ArgSettings::Multiple) - && (self.v.val_names.is_none() || self.v.val_names.as_ref().unwrap().len() == 1) - { - write!(f, "...")?; - } - - Ok(()) - } -} - -impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> { - fn name(&self) -> &'n str { self.b.name } - fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) } - fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> { - self.b.requires.as_ref().map(|o| &o[..]) - } - fn blacklist(&self) -> Option<&[&'e str]> { self.b.blacklist.as_ref().map(|o| &o[..]) } - fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) } - fn val_names(&self) -> Option<&VecMap<&'e str>> { self.v.val_names.as_ref() } - fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) } - fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) } - fn has_switch(&self) -> bool { false } - fn max_vals(&self) -> Option { self.v.max_vals } - fn val_terminator(&self) -> Option<&'e str> { self.v.terminator } - fn num_vals(&self) -> Option { self.v.num_vals } - fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) } - fn validator(&self) -> Option<&Rc StdResult<(), String>>> { - self.v.validator.as_ref() - } - fn validator_os(&self) -> Option<&Rc StdResult<(), OsString>>> { - self.v.validator_os.as_ref() - } - fn min_vals(&self) -> Option { self.v.min_vals } - fn short(&self) -> Option { None } - fn long(&self) -> Option<&'e str> { None } - fn val_delim(&self) -> Option { self.v.val_delim } - fn takes_value(&self) -> bool { true } - fn help(&self) -> Option<&'e str> { self.b.help } - fn long_help(&self) -> Option<&'e str> { self.b.long_help } - fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { - self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) - } - fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val } - fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> { - self.v - .env - .as_ref() - .map(|&(key, ref value)| (key, value.as_ref())) - } - fn longest_filter(&self) -> bool { true } - fn aliases(&self) -> Option> { None } -} - -impl<'n, 'e> DispOrder for PosBuilder<'n, 'e> { - fn disp_ord(&self) -> usize { self.index as usize } -} - -impl<'n, 'e> PartialEq for PosBuilder<'n, 'e> { - fn eq(&self, other: &PosBuilder<'n, 'e>) -> bool { self.b == other.b } -} - -#[cfg(test)] -mod test { - use args::settings::ArgSettings; - use super::PosBuilder; - use map::VecMap; - - #[test] - fn display_mult() { - let mut p = PosBuilder::new("pos", 1); - p.b.settings.set(ArgSettings::Multiple); - - assert_eq!(&*format!("{}", p), "..."); - } - - #[test] - fn display_required() { - let mut p2 = PosBuilder::new("pos", 1); - p2.b.settings.set(ArgSettings::Required); - - assert_eq!(&*format!("{}", p2), ""); - } - - #[test] - fn display_val_names() { - let mut p2 = PosBuilder::new("pos", 1); - let mut vm = VecMap::new(); - vm.insert(0, "file1"); - vm.insert(1, "file2"); - p2.v.val_names = Some(vm); - - assert_eq!(&*format!("{}", p2), " "); - } - - #[test] - fn display_val_names_req() { - let mut p2 = PosBuilder::new("pos", 1); - p2.b.settings.set(ArgSettings::Required); - let mut vm = VecMap::new(); - vm.insert(0, "file1"); - vm.insert(1, "file2"); - p2.v.val_names = Some(vm); - - assert_eq!(&*format!("{}", p2), " "); - } -} diff --git a/src/args/arg_builder/switched.rs b/src/args/arg_builder/switched.rs deleted file mode 100644 index 224b2f2b24b5..000000000000 --- a/src/args/arg_builder/switched.rs +++ /dev/null @@ -1,38 +0,0 @@ -use Arg; - -#[derive(Debug)] -pub struct Switched<'b> { - pub short: Option, - pub long: Option<&'b str>, - pub aliases: Option>, // (name, visible) - pub disp_ord: usize, - pub unified_ord: usize, -} - -impl<'e> Default for Switched<'e> { - fn default() -> Self { - Switched { - short: None, - long: None, - aliases: None, - disp_ord: 999, - unified_ord: 999, - } - } -} - -impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Switched<'e> { - fn from(a: &'z Arg<'n, 'e>) -> Self { a.s.clone() } -} - -impl<'e> Clone for Switched<'e> { - fn clone(&self) -> Self { - Switched { - short: self.short, - long: self.long, - aliases: self.aliases.clone(), - disp_ord: self.disp_ord, - unified_ord: self.unified_ord, - } - } -} diff --git a/src/args/arg_builder/valued.rs b/src/args/arg_builder/valued.rs deleted file mode 100644 index d70854dc89ac..000000000000 --- a/src/args/arg_builder/valued.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::rc::Rc; -use std::ffi::{OsStr, OsString}; - -use map::VecMap; - -use Arg; - -#[allow(missing_debug_implementations)] -#[derive(Clone)] -pub struct Valued<'a, 'b> -where - 'a: 'b, -{ - pub possible_vals: Option>, - pub val_names: Option>, - pub num_vals: Option, - pub max_vals: Option, - pub min_vals: Option, - pub validator: Option Result<(), String>>>, - pub validator_os: Option Result<(), OsString>>>, - pub val_delim: Option, - pub default_val: Option<&'b OsStr>, - pub default_vals_ifs: Option, &'b OsStr)>>, - pub env: Option<(&'a OsStr, Option)>, - pub terminator: Option<&'b str>, -} - -impl<'n, 'e> Default for Valued<'n, 'e> { - fn default() -> Self { - Valued { - possible_vals: None, - num_vals: None, - min_vals: None, - max_vals: None, - val_names: None, - validator: None, - validator_os: None, - val_delim: None, - default_val: None, - default_vals_ifs: None, - env: None, - terminator: None, - } - } -} - -impl<'n, 'e> Valued<'n, 'e> { - pub fn fill_in(&mut self) { - if let Some(ref vec) = self.val_names { - if vec.len() > 1 { - self.num_vals = Some(vec.len() as u64); - } - } - } -} - -impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Valued<'n, 'e> { - fn from(a: &'z Arg<'n, 'e>) -> Self { - let mut v = a.v.clone(); - if let Some(ref vec) = a.v.val_names { - if vec.len() > 1 { - v.num_vals = Some(vec.len() as u64); - } - } - v - } -} diff --git a/src/args/arg_matcher.rs b/src/args/arg_matcher.rs deleted file mode 100644 index e1d8067e69ad..000000000000 --- a/src/args/arg_matcher.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Std -use std::collections::hash_map::{Entry, Iter}; -use std::collections::HashMap; -use std::ffi::OsStr; -use std::ops::Deref; -use std::mem; - -// Internal -use args::{ArgMatches, MatchedArg, SubCommand}; -use args::AnyArg; -use args::settings::ArgSettings; - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ArgMatcher<'a>(pub ArgMatches<'a>); - -impl<'a> Default for ArgMatcher<'a> { - fn default() -> Self { ArgMatcher(ArgMatches::default()) } -} - -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>, check_all: bool) { - debugln!("ArgMatcher::process_arg_overrides:{:?};", a.map_or(None, |a| Some(a.name()))); - if let Some(aa) = a { - let mut self_done = false; - if let Some(a_overrides) = aa.overrides() { - for overr in a_overrides { - debugln!("ArgMatcher::process_arg_overrides:iter:{};", overr); - if overr == &aa.name() { - self_done = true; - self.handle_self_overrides(a); - } else 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; - } - } - overrides.push((overr, aa.name())); - } else { - overrides.push((overr, aa.name())); - } - } - } - if check_all && !self_done { - self.handle_self_overrides(a); - } - } - } - - pub fn handle_self_overrides<'b>(&mut self, a: Option<&AnyArg<'a, 'b>>) { - debugln!("ArgMatcher::handle_self_overrides:{:?};", a.map_or(None, |a| Some(a.name()))); - if let Some(aa) = a { - if !aa.has_switch() || aa.is_set(ArgSettings::Multiple) { - // positional args can't override self or else we would never advance to the next - - // Also flags with --multiple set are ignored otherwise we could never have more - // than one - return; - } - if let Some(ma) = self.get_mut(aa.name()) { - if ma.vals.len() > 1 { - // swap_remove(0) would be O(1) but does not preserve order, which - // we need - ma.vals.remove(0); - ma.occurs = 1; - } else if !aa.takes_value() && ma.occurs > 1 { - ma.occurs = 1; - } - } - } - } - - 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 ); - let mut vals_map = HashMap::new(); - self.fill_in_global_values(global_arg_vec, &mut vals_map); - } - - fn fill_in_global_values( - &mut self, - global_arg_vec: &[&'a str], - vals_map: &mut HashMap<&'a str, MatchedArg>, - ) { - for global_arg in global_arg_vec { - if let Some(ma) = self.get(global_arg) { - // We have to check if the parent's global arg wasn't used but still exists - // such as from a default value. - // - // For example, `myprog subcommand --global-arg=value` where --global-arg defines - // a default value of `other` myprog would have an existing MatchedArg for - // --global-arg where the value is `other`, however the occurs will be 0. - let to_update = if let Some(parent_ma) = vals_map.get(global_arg) { - if parent_ma.occurs > 0 && ma.occurs == 0 { - parent_ma.clone() - } else { - ma.clone() - } - } else { - ma.clone() - }; - vals_map.insert(global_arg, to_update); - } - } - if let Some(ref mut sc) = self.0.subcommand { - let mut am = ArgMatcher(mem::replace(&mut sc.matches, ArgMatches::new())); - am.fill_in_global_values(global_arg_vec, vals_map); - mem::swap(&mut am.0, &mut sc.matches); - } - - for (name, matched_arg) in vals_map.into_iter() { - self.0.args.insert(name, matched_arg.clone()); - } - } - - pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> { self.0.args.get_mut(arg) } - - pub fn get(&self, arg: &str) -> Option<&MatchedArg> { self.0.args.get(arg) } - - pub fn remove(&mut self, arg: &str) { self.0.args.remove(arg); } - - pub fn remove_all(&mut self, args: &[&str]) { - for &arg in args { - self.0.args.remove(arg); - } - } - - pub fn insert(&mut self, name: &'a str) { self.0.args.insert(name, MatchedArg::new()); } - - pub fn contains(&self, arg: &str) -> bool { self.0.args.contains_key(arg) } - - pub fn is_empty(&self) -> bool { self.0.args.is_empty() } - - pub fn usage(&mut self, usage: String) { self.0.usage = Some(usage); } - - pub fn arg_names(&'a self) -> Vec<&'a str> { self.0.args.keys().map(Deref::deref).collect() } - - pub fn entry(&mut self, arg: &'a str) -> Entry<&'a str, MatchedArg> { self.0.args.entry(arg) } - - pub fn subcommand(&mut self, sc: SubCommand<'a>) { self.0.subcommand = Some(Box::new(sc)); } - - pub fn subcommand_name(&self) -> Option<&str> { self.0.subcommand_name() } - - pub fn iter(&self) -> Iter<&str, MatchedArg> { self.0.args.iter() } - - pub fn inc_occurrence_of(&mut self, arg: &'a str) { - debugln!("ArgMatcher::inc_occurrence_of: arg={}", arg); - if let Some(a) = self.get_mut(arg) { - a.occurs += 1; - return; - } - debugln!("ArgMatcher::inc_occurrence_of: first instance"); - self.insert(arg); - } - - pub fn inc_occurrences_of(&mut self, args: &[&'a str]) { - debugln!("ArgMatcher::inc_occurrences_of: args={:?}", args); - for arg in args { - self.inc_occurrence_of(arg); - } - } - - pub fn add_val_to(&mut self, arg: &'a str, val: &OsStr) { - let ma = self.entry(arg).or_insert(MatchedArg { - occurs: 0, - indices: Vec::with_capacity(1), - vals: Vec::with_capacity(1), - }); - ma.vals.push(val.to_owned()); - } - - pub fn add_index_to(&mut self, arg: &'a str, idx: usize) { - let ma = self.entry(arg).or_insert(MatchedArg { - occurs: 0, - indices: Vec::with_capacity(1), - vals: Vec::new(), - }); - ma.indices.push(idx); - } - - pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool - where - A: AnyArg<'a, 'b>, - { - debugln!("ArgMatcher::needs_more_vals: o={}", o.name()); - if let Some(ma) = self.get(o.name()) { - if let Some(num) = o.num_vals() { - debugln!("ArgMatcher::needs_more_vals: num_vals...{}", num); - return if o.is_set(ArgSettings::Multiple) { - ((ma.vals.len() as u64) % num) != 0 - } else { - num != (ma.vals.len() as u64) - }; - } else if let Some(num) = o.max_vals() { - debugln!("ArgMatcher::needs_more_vals: max_vals...{}", num); - return !((ma.vals.len() as u64) > num); - } else if o.min_vals().is_some() { - debugln!("ArgMatcher::needs_more_vals: min_vals...true"); - return true; - } - return o.is_set(ArgSettings::Multiple); - } - true - } -} - -impl<'a> Into> for ArgMatcher<'a> { - fn into(self) -> ArgMatches<'a> { self.0 } -} diff --git a/src/args/macros.rs b/src/args/macros.rs deleted file mode 100644 index 1de12f4ec20a..000000000000 --- a/src/args/macros.rs +++ /dev/null @@ -1,109 +0,0 @@ -#[cfg(feature = "yaml")] -macro_rules! yaml_tuple2 { - ($a:ident, $v:ident, $c:ident) => {{ - if let Some(vec) = $v.as_vec() { - for ys in vec { - if let Some(tup) = ys.as_vec() { - debug_assert_eq!(2, tup.len()); - $a = $a.$c(yaml_str!(tup[0]), yaml_str!(tup[1])); - } else { - panic!("Failed to convert YAML value to vec"); - } - } - } else { - panic!("Failed to convert YAML value to vec"); - } - $a - } - }; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_tuple3 { - ($a:ident, $v:ident, $c:ident) => {{ - if let Some(vec) = $v.as_vec() { - for ys in vec { - if let Some(tup) = ys.as_vec() { - debug_assert_eq!(3, tup.len()); - $a = $a.$c(yaml_str!(tup[0]), yaml_opt_str!(tup[1]), yaml_str!(tup[2])); - } else { - panic!("Failed to convert YAML value to vec"); - } - } - } else { - panic!("Failed to convert YAML value to vec"); - } - $a - } - }; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_vec_or_str { - ($v:ident, $a:ident, $c:ident) => {{ - let maybe_vec = $v.as_vec(); - if let Some(vec) = maybe_vec { - for ys in vec { - if let Some(s) = ys.as_str() { - $a = $a.$c(s); - } else { - panic!("Failed to convert YAML value {:?} to a string", ys); - } - } - } else { - if let Some(s) = $v.as_str() { - $a = $a.$c(s); - } else { - panic!("Failed to convert YAML value {:?} to either a vec or string", $v); - } - } - $a - } - }; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_opt_str { - ($v:expr) => {{ - if $v.is_null() { - Some($v.as_str().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))) - } else { - None - } - }}; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_str { - ($v:expr) => {{ - $v.as_str().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) - }}; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_to_str { - ($a:ident, $v:ident, $c:ident) => {{ - $a.$c(yaml_str!($v)) - }}; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_to_bool { - ($a:ident, $v:ident, $c:ident) => {{ - $a.$c($v.as_bool().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))) - }}; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_to_u64 { - ($a:ident, $v:ident, $c:ident) => {{ - $a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as u64) - }}; -} - -#[cfg(feature = "yaml")] -macro_rules! yaml_to_usize { - ($a:ident, $v:ident, $c:ident) => {{ - $a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as usize) - }}; -} diff --git a/src/args/matched_arg.rs b/src/args/matched_arg.rs deleted file mode 100644 index eeda2611ca9f..000000000000 --- a/src/args/matched_arg.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Std -use std::ffi::OsString; - -#[doc(hidden)] -#[derive(Debug, Clone)] -pub struct MatchedArg { - #[doc(hidden)] pub occurs: u64, - #[doc(hidden)] pub indices: Vec, - #[doc(hidden)] pub vals: Vec, -} - -impl Default for MatchedArg { - fn default() -> Self { - MatchedArg { - occurs: 1, - indices: Vec::new(), - vals: Vec::new(), - } - } -} - -impl MatchedArg { - pub fn new() -> Self { MatchedArg::default() } -} diff --git a/src/args/mod.rs b/src/args/mod.rs deleted file mode 100644 index 21f9b850d5f6..000000000000 --- a/src/args/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub use self::any_arg::{AnyArg, DispOrder}; -pub use self::arg::Arg; -pub use self::arg_builder::{Base, FlagBuilder, OptBuilder, PosBuilder, Switched, Valued}; -pub use self::arg_matcher::ArgMatcher; -pub use self::arg_matches::{ArgMatches, OsValues, Values}; -pub use self::group::ArgGroup; -pub use self::matched_arg::MatchedArg; -pub use self::settings::{ArgFlags, ArgSettings}; -pub use self::subcommand::SubCommand; - -#[macro_use] -mod macros; -mod arg; -pub mod any_arg; -mod arg_matches; -mod arg_matcher; -mod subcommand; -mod arg_builder; -mod matched_arg; -mod group; -pub mod settings; diff --git a/src/args/subcommand.rs b/src/args/subcommand.rs deleted file mode 100644 index eebbf827aec7..000000000000 --- a/src/args/subcommand.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Third Party -#[cfg(feature = "yaml")] -use yaml_rust::Yaml; - -// Internal -use App; -use ArgMatches; - -/// The abstract representation of a command line subcommand. -/// -/// This struct describes all the valid options of the subcommand for the program. Subcommands are -/// essentially "sub-[`App`]s" and contain all the same possibilities (such as their own -/// [arguments], subcommands, and settings). -/// -/// # Examples -/// -/// ```rust -/// # use clap::{App, Arg, SubCommand}; -/// App::new("myprog") -/// .subcommand( -/// SubCommand::with_name("config") -/// .about("Used for configuration") -/// .arg(Arg::with_name("config_file") -/// .help("The configuration file to use") -/// .index(1))) -/// # ; -/// ``` -/// [`App`]: ./struct.App.html -/// [arguments]: ./struct.Arg.html -#[derive(Debug, Clone)] -pub struct SubCommand<'a> { - #[doc(hidden)] pub name: String, - #[doc(hidden)] pub matches: ArgMatches<'a>, -} - -impl<'a> SubCommand<'a> { - /// Creates a new instance of a subcommand requiring a name. The name will be displayed - /// to the user when they print version or help and usage information. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, SubCommand}; - /// App::new("myprog") - /// .subcommand( - /// SubCommand::with_name("config")) - /// # ; - /// ``` - pub fn with_name<'b>(name: &str) -> App<'a, 'b> { App::new(name) } - - /// Creates a new instance of a subcommand from a YAML (.yml) document - /// - /// # Examples - /// - /// ```ignore - /// # #[macro_use] - /// # extern crate clap; - /// # use clap::Subcommand; - /// # fn main() { - /// let sc_yaml = load_yaml!("test_subcommand.yml"); - /// let sc = SubCommand::from_yaml(sc_yaml); - /// # } - /// ``` - #[cfg(feature = "yaml")] - pub fn from_yaml(yaml: &Yaml) -> App { App::from_yaml(yaml) } -} diff --git a/src/app/mod.rs b/src/build/app/mod.rs similarity index 57% rename from src/app/mod.rs rename to src/build/app/mod.rs index 3a1a3831e399..b2537fc50c97 100644 --- a/src/app/mod.rs +++ b/src/build/app/mod.rs @@ -1,32 +1,39 @@ mod settings; -pub mod parser; -mod meta; -mod help; -mod validator; -mod usage; +pub use self::settings::{AppFlags, AppSettings}; // Std use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::fmt; use std::io::{self, BufRead, BufWriter, Write}; +use std::iter::Peekable; use std::path::Path; use std::process; -use std::rc::Rc; -use std::result::Result as StdResult; // Third Party #[cfg(feature = "yaml")] use yaml_rust::Yaml; // Internal -use app::help::Help; -use app::parser::Parser; -use args::{AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings}; -use errors::Result as ClapResult; -pub use self::settings::AppSettings; -use completions::Shell; -use map::{self, VecMap}; +use crate::build::{Arg, ArgGroup, ArgSettings}; +use crate::mkeymap::MKeyMap; +use crate::output::fmt::ColorWhen; +use crate::output::{Help, Usage}; +use crate::parse::errors::Result as ClapResult; +use crate::parse::{ArgMatcher, ArgMatches, Parser}; +use crate::util::{Key, HELP_HASH, VERSION_HASH}; +use crate::INTERNAL_ERROR_MSG; + +type Id = u64; + +#[doc(hidden)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Propagation { + To(Id), + Full, + NextLevel, + None, +} /// Used to create a representation of a command line program and all possible command line /// arguments. Application settings are set using the "builder pattern" with the @@ -56,16 +63,59 @@ use map::{self, VecMap}; /// // Your program logic starts here... /// ``` /// [`App::get_matches`]: ./struct.App.html#method.get_matches -#[allow(missing_debug_implementations)] -pub struct App<'a, 'b> -where - 'a: 'b, -{ - #[doc(hidden)] pub p: Parser<'a, 'b>, +#[derive(Default, Debug, Clone)] +pub struct App<'b> { + #[doc(hidden)] + pub id: Id, + #[doc(hidden)] + pub name: String, + #[doc(hidden)] + pub bin_name: Option, + #[doc(hidden)] + pub author: Option<&'b str>, + #[doc(hidden)] + pub version: Option<&'b str>, + #[doc(hidden)] + pub long_version: Option<&'b str>, + #[doc(hidden)] + pub about: Option<&'b str>, + #[doc(hidden)] + pub long_about: Option<&'b str>, + #[doc(hidden)] + pub more_help: Option<&'b str>, + #[doc(hidden)] + pub pre_help: Option<&'b str>, + #[doc(hidden)] + pub aliases: Option>, // (name, visible) + #[doc(hidden)] + pub usage_str: Option<&'b str>, + #[doc(hidden)] + pub usage: Option, + #[doc(hidden)] + pub help_str: Option<&'b str>, + #[doc(hidden)] + pub disp_ord: usize, + #[doc(hidden)] + pub term_w: Option, + #[doc(hidden)] + pub max_w: Option, + #[doc(hidden)] + pub template: Option<&'b str>, + #[doc(hidden)] + pub settings: AppFlags, + #[doc(hidden)] + pub g_settings: AppFlags, + #[doc(hidden)] + pub args: MKeyMap<'b>, + #[doc(hidden)] + pub subcommands: Vec>, + #[doc(hidden)] + pub groups: Vec>, + #[doc(hidden)] + pub help_headings: Vec>, } - -impl<'a, 'b> App<'a, 'b> { +impl<'b> App<'b> { /// Creates a new instance of an application requiring a name. The name may be, but doesn't /// have to be same as the binary. The name will be displayed to the user when they request to /// print version or help and usage information. @@ -78,82 +128,20 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn new>(n: S) -> Self { + let name = n.into(); + let id = name.key(); App { - p: Parser::with_name(n.into()), + id, + name, + ..Default::default() } } /// Get the name of the app - pub fn get_name(&self) -> &str { &self.p.meta.name } + pub fn get_name(&self) -> &str { &self.name } /// Get the name of the binary - pub fn get_bin_name(&self) -> Option<&str> { self.p.meta.bin_name.as_ref().map(|s| s.as_str()) } - - /// Creates a new instance of an application requiring a name, but uses the [`crate_authors!`] - /// and [`crate_version!`] macros to fill in the [`App::author`] and [`App::version`] fields. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg}; - /// let prog = App::with_defaults("My Program") - /// # ; - /// ``` - /// [`crate_authors!`]: ./macro.crate_authors!.html - /// [`crate_version!`]: ./macro.crate_version!.html - /// [`App::author`]: ./struct.App.html#method.author - /// [`App::version`]: ./struct.App.html#method.author - #[deprecated(since="2.14.1", note="Can never work; use explicit App::author() and App::version() calls instead")] - pub fn with_defaults>(n: S) -> Self { - let mut a = App { - p: Parser::with_name(n.into()), - }; - a.p.meta.author = Some("Kevin K. "); - a.p.meta.version = Some("2.19.2"); - a - } - - /// Creates a new instance of [`App`] from a .yml (YAML) file. A full example of supported YAML - /// objects can be found in [`examples/17_yaml.rs`] and [`examples/17_yaml.yml`]. One great use - /// for using YAML is when supporting multiple languages and dialects, as each language could - /// be a distinct YAML file and determined at compiletime via `cargo` "features" in your - /// `Cargo.toml` - /// - /// In order to use this function you must compile `clap` with the `features = ["yaml"]` in - /// your settings for the `[dependencies.clap]` table of your `Cargo.toml` - /// - /// **NOTE:** Due to how the YAML objects are built there is a convenience macro for loading - /// the YAML file at compile time (relative to the current file, like modules work). That YAML - /// object can then be passed to this function. - /// - /// # Panics - /// - /// The YAML file must be properly formatted or this function will [`panic!`]. A good way to - /// ensure this doesn't happen is to run your program with the `--help` switch. If this passes - /// without error, you needn't worry because the YAML is properly formatted. - /// - /// # Examples - /// - /// The following example shows how to load a properly formatted YAML file to build an instance - /// of an [`App`] struct. - /// - /// ```ignore - /// # #[macro_use] - /// # extern crate clap; - /// # use clap::App; - /// # fn main() { - /// let yml = load_yaml!("app.yml"); - /// let app = App::from_yaml(yml); - /// - /// // continued logic goes here, such as `app.get_matches()` etc. - /// # } - /// ``` - /// [`App`]: ./struct.App.html - /// [`examples/17_yaml.rs`]: https://github.com/clap-rs/clap/blob/master/examples/17_yaml.rs - /// [`examples/17_yaml.yml`]: https://github.com/clap-rs/clap/blob/master/examples/17_yaml.yml - /// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html - #[cfg(feature = "yaml")] - pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) } + pub fn get_bin_name(&self) -> Option<&str> { self.bin_name.as_ref().map(String::as_str) } /// Sets a string of author(s) that will be displayed to the user when they /// request the help information with `--help` or `-h`. @@ -174,9 +162,9 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` /// [`crate_authors!`]: ./macro.crate_authors!.html - /// [`examples/`]: https://github.com/clap-rs/clap/tree/master/examples + /// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples pub fn author>(mut self, author: S) -> Self { - self.p.meta.author = Some(author.into()); + self.author = Some(author.into()); self } @@ -187,7 +175,7 @@ impl<'a, 'b> App<'a, 'b> { /// **Pro-tip:** When building things such as third party `cargo` subcommands, this setting /// **should** be used! /// - /// **NOTE:** This command **should not** be used for [`SubCommand`]s. + /// **NOTE:** This command **should not** be used for [``]s. /// /// # Examples /// @@ -197,9 +185,9 @@ impl<'a, 'b> App<'a, 'b> { /// .bin_name("my_binary") /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html pub fn bin_name>(mut self, name: S) -> Self { - self.p.meta.bin_name = Some(name.into()); + self.bin_name = Some(name.into()); self } @@ -222,7 +210,7 @@ impl<'a, 'b> App<'a, 'b> { /// ``` /// [`App::long_about`]: ./struct.App.html#method.long_about pub fn about>(mut self, about: S) -> Self { - self.p.meta.about = Some(about.into()); + self.about = Some(about.into()); self } @@ -248,7 +236,7 @@ impl<'a, 'b> App<'a, 'b> { /// ``` /// [`App::about`]: ./struct.App.html#method.about pub fn long_about>(mut self, about: S) -> Self { - self.p.meta.long_about = Some(about.into()); + self.long_about = Some(about.into()); self } @@ -275,7 +263,7 @@ impl<'a, 'b> App<'a, 'b> { /// [`App::from_yaml`]: ./struct.App.html#method.from_yaml /// [`crate_name!`]: ./macro.crate_name.html pub fn name>(mut self, name: S) -> Self { - self.p.meta.name = name.into(); + self.name = name.into(); self } @@ -292,7 +280,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn after_help>(mut self, help: S) -> Self { - self.p.meta.more_help = Some(help.into()); + self.more_help = Some(help.into()); self } @@ -309,7 +297,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn before_help>(mut self, help: S) -> Self { - self.p.meta.pre_help = Some(help.into()); + self.pre_help = Some(help.into()); self } @@ -332,10 +320,10 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` /// [`crate_version!`]: ./macro.crate_version!.html - /// [`examples/`]: https://github.com/clap-rs/clap/tree/master/examples + /// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples /// [`App::long_version`]: ./struct.App.html#method.long_version pub fn version>(mut self, ver: S) -> Self { - self.p.meta.version = Some(ver.into()); + self.version = Some(ver.into()); self } @@ -363,25 +351,20 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` /// [`crate_version!`]: ./macro.crate_version!.html - /// [`examples/`]: https://github.com/clap-rs/clap/tree/master/examples + /// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples /// [`App::version`]: ./struct.App.html#method.version pub fn long_version>(mut self, ver: S) -> Self { - self.p.meta.long_version = Some(ver.into()); + self.long_version = Some(ver.into()); self } - /// Sets a custom usage string to override the auto-generated usage string. + /// Overrides the `clap` generated usage string. /// - /// This will be displayed to the user when errors are found in argument parsing, or when you - /// call [`ArgMatches::usage`] + /// This will be displayed to the user when errors are found in argument parsing. /// /// **CAUTION:** Using this setting disables `clap`s "context-aware" usage strings. After this /// setting is set, this will be the only usage string displayed to the user! /// - /// **NOTE:** You do not need to specify the "USAGE: \n\t" portion, as that will - /// still be applied by `clap`, you only need to specify the portion starting - /// with the binary name. - /// /// **NOTE:** This will not replace the entire help message, *only* the portion /// showing the usage. /// @@ -390,16 +373,16 @@ impl<'a, 'b> App<'a, 'b> { /// ```no_run /// # use clap::{App, Arg}; /// App::new("myprog") - /// .usage("myapp [-clDas] ") + /// .override_usage("myapp [-clDas] ") /// # ; /// ``` /// [`ArgMatches::usage`]: ./struct.ArgMatches.html#method.usage - pub fn usage>(mut self, usage: S) -> Self { - self.p.meta.usage_str = Some(usage.into()); + pub fn override_usage>(mut self, usage: S) -> Self { + self.usage_str = Some(usage.into()); self } - /// Sets a custom help message and overrides the auto-generated one. This should only be used + /// Overrides the `clap` generated help message. This should only be used /// when the auto-generated message does not suffice. /// /// This will be displayed to the user when they use `--help` or `-h` @@ -408,21 +391,21 @@ impl<'a, 'b> App<'a, 'b> { /// /// **NOTE:** This **only** replaces the help message for the current command, meaning if you /// are using subcommands, those help messages will still be auto-generated unless you - /// specify a [`Arg::help`] for them as well. + /// specify a [`Arg::override_help`] for them as well. /// /// # Examples /// /// ```no_run /// # use clap::{App, Arg}; /// App::new("myapp") - /// .help("myapp v1.0\n\ + /// .override_help("myapp v1.0\n\ /// Does awesome things\n\ /// (C) me@mail.com\n\n\ /// - /// USAGE: myapp \n\n\ + /// USAGE: myapp \n\n\ /// /// Options:\n\ - /// -h, --help Display this message\n\ + /// -h, --helpe Dispay this message\n\ /// -V, --version Display version info\n\ /// -s Do something with stuff\n\ /// -v Be verbose\n\n\ @@ -432,99 +415,9 @@ impl<'a, 'b> App<'a, 'b> { /// work Do some work") /// # ; /// ``` - /// [`Arg::help`]: ./struct.Arg.html#method.help - pub fn help>(mut self, help: S) -> Self { - self.p.meta.help_str = Some(help.into()); - self - } - - /// Sets the [`short`] for the auto-generated `help` argument. - /// - /// By default `clap` automatically assigns `h`, but this can be overridden if you have a - /// different argument which you'd prefer to use the `-h` short with. This can be done by - /// defining your own argument with a lowercase `h` as the [`short`]. - /// - /// `clap` lazily generates these `help` arguments **after** you've defined any arguments of - /// your own. - /// - /// **NOTE:** Any leading `-` characters will be stripped, and only the first - /// non `-` character will be used as the [`short`] version - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .help_short("H") // Using an uppercase `H` instead of the default lowercase `h` - /// # ; - /// ``` - /// [`short`]: ./struct.Arg.html#method.short - pub fn help_short + 'b>(mut self, s: S) -> Self { - self.p.help_short(s.as_ref()); - self - } - - /// Sets the [`short`] for the auto-generated `version` argument. - /// - /// By default `clap` automatically assigns `V`, but this can be overridden if you have a - /// different argument which you'd prefer to use the `-V` short with. This can be done by - /// defining your own argument with an uppercase `V` as the [`short`]. - /// - /// `clap` lazily generates these `version` arguments **after** you've defined any arguments of - /// your own. - /// - /// **NOTE:** Any leading `-` characters will be stripped, and only the first - /// non `-` character will be used as the `short` version - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .version_short("v") // Using a lowercase `v` instead of the default capital `V` - /// # ; - /// ``` - /// [`short`]: ./struct.Arg.html#method.short - pub fn version_short>(mut self, s: S) -> Self { - self.p.version_short(s.as_ref()); - self - } - - /// Sets the help text for the auto-generated `help` argument. - /// - /// By default `clap` sets this to `"Prints help information"`, but if you're using a - /// different convention for your help messages and would prefer a different phrasing you can - /// override it. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .help_message("Print help information") // Perhaps you want imperative help messages - /// - /// # ; - /// ``` - pub fn help_message>(mut self, s: S) -> Self { - self.p.help_message = Some(s.into()); - self - } - - /// Sets the help text for the auto-generated `version` argument. - /// - /// By default `clap` sets this to `"Prints version information"`, but if you're using a - /// different convention for your help messages and would prefer a different phrasing then you - /// can change it. - /// - /// # Examples - /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .version_message("Print version information") // Perhaps you want imperative help messages - /// # ; - /// ``` - pub fn version_message>(mut self, s: S) -> Self { - self.p.version_message = Some(s.into()); + /// [`Arg::override_help`]: ./struct.Arg.html#method.override_help + pub fn override_help>(mut self, help: S) -> Self { + self.help_str = Some(help.into()); self } @@ -557,22 +450,21 @@ impl<'a, 'b> App<'a, 'b> { /// # use clap::{App, Arg}; /// App::new("myprog") /// .version("1.0") - /// .template("{bin} ({version}) - {usage}") + /// .help_template("{bin} ({version}) - {usage}") /// # ; /// ``` - /// **NOTE:** The template system is, on purpose, very simple. Therefore the tags have to be - /// written in lowercase and without spacing. - /// + /// **NOTE:**The template system is, on purpose, very simple. Therefore the tags have to writen + /// in the lowercase and without spacing. /// [`App::about`]: ./struct.App.html#method.about /// [`App::after_help`]: ./struct.App.html#method.after_help /// [`App::before_help`]: ./struct.App.html#method.before_help /// [`AppSettings::UnifiedHelpMessage`]: ./enum.AppSettings.html#variant.UnifiedHelpMessage - pub fn template>(mut self, s: S) -> Self { - self.p.meta.template = Some(s.into()); + pub fn help_template>(mut self, s: S) -> Self { + self.template = Some(s.into()); self } - /// Enables a single command, or [`SubCommand`], level settings. + /// Enables a single command, or [``], level settings. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// @@ -585,36 +477,35 @@ impl<'a, 'b> App<'a, 'b> { /// .setting(AppSettings::WaitOnError) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`AppSettings`]: ./enum.AppSettings.html pub fn setting(mut self, setting: AppSettings) -> Self { - self.p.set(setting); + self.settings.set(setting); self } - /// Enables multiple command, or [`SubCommand`], level settings + /// Disables a single command, or [``], level setting. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, AppSettings}; + /// # use clap::{App, AppSettings}; /// App::new("myprog") - /// .settings(&[AppSettings::SubcommandRequired, - /// AppSettings::WaitOnError]) + /// .unset_setting(AppSettings::ColorAuto) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`AppSettings`]: ./enum.AppSettings.html - pub fn settings(mut self, settings: &[AppSettings]) -> Self { - for s in settings { - self.p.set(*s); - } + /// [global]: ./struct.App.html#method.global_setting + pub fn unset_setting(mut self, setting: AppSettings) -> Self { + self.settings.unset(setting); + self.g_settings.unset(setting); self } - /// Enables a single setting that is propagated down through all child [`SubCommand`]s. + /// Enables a single setting that is propagated down through all child subcommands. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// @@ -628,84 +519,39 @@ impl<'a, 'b> App<'a, 'b> { /// .global_setting(AppSettings::SubcommandRequired) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html /// [`AppSettings`]: ./enum.AppSettings.html pub fn global_setting(mut self, setting: AppSettings) -> Self { - self.p.set(setting); - self.p.g_settings.set(setting); + self.settings.set(setting); + self.g_settings.set(setting); self } - /// Enables multiple settings which are propagated *down* through all child [`SubCommand`]s. + /// Disables a global setting, and stops propagating down to child subcommands. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// - /// **NOTE**: The setting is *only* propagated *down* and not up through parent commands. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_settings(&[AppSettings::SubcommandRequired, - /// AppSettings::ColoredHelp]) - /// # ; - /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html - /// [`AppSettings`]: ./enum.AppSettings.html - pub fn global_settings(mut self, settings: &[AppSettings]) -> Self { - for s in settings { - self.p.set(*s); - self.p.g_settings.set(*s) - } - self - } - - /// Disables a single command, or [`SubCommand`], level setting. - /// - /// See [`AppSettings`] for a full list of possibilities and examples. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .unset_setting(AppSettings::ColorAuto) - /// # ; - /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html - /// [`AppSettings`]: ./enum.AppSettings.html - pub fn unset_setting(mut self, setting: AppSettings) -> Self { - self.p.unset(setting); - self - } - - /// Disables multiple command, or [`SubCommand`], level settings. - /// - /// See [`AppSettings`] for a full list of possibilities and examples. + /// **NOTE:** The setting being unset will be unset from both local and [global] settings /// /// # Examples /// /// ```no_run /// # use clap::{App, AppSettings}; /// App::new("myprog") - /// .unset_settings(&[AppSettings::ColorAuto, - /// AppSettings::AllowInvalidUtf8]) + /// .unset_global_setting(AppSettings::ColorAuto) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html /// [`AppSettings`]: ./enum.AppSettings.html - pub fn unset_settings(mut self, settings: &[AppSettings]) -> Self { - for s in settings { - self.p.unset(*s); - } + /// [global]: ./struct.App.html#method.global_setting + pub fn unset_global_setting(mut self, setting: AppSettings) -> Self { + self.settings.unset(setting); + self.g_settings.unset(setting); self } /// Sets the terminal width at which to wrap help messages. Defaults to `120`. Using `0` will /// ignore terminal widths and use source formatting. /// - /// `clap` automatically tries to determine the terminal width on Unix, Linux, macOS and Windows + /// `clap` automatically tries to determine the terminal width on Unix, Linux, OSX and Windows /// if the `wrap_help` cargo "feature" has been used while compiling. If the terminal width /// cannot be determined, `clap` defaults to `120`. /// @@ -715,7 +561,7 @@ impl<'a, 'b> App<'a, 'b> { /// /// # Platform Specific /// - /// Only Unix, Linux, macOS and Windows support automatic determination of terminal width. + /// Only Unix, Linux, OSX and Windows support automatic determination of terminal width. /// Even on those platforms, this setting is useful if for any reason the terminal width /// cannot be determined. /// @@ -728,14 +574,14 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn set_term_width(mut self, width: usize) -> Self { - self.p.meta.term_w = Some(width); + self.term_w = Some(width); self } /// Sets the max terminal width at which to wrap help messages. Using `0` will ignore terminal /// widths and use source formatting. /// - /// `clap` automatically tries to determine the terminal width on Unix, Linux, macOS and Windows + /// `clap` automatically tries to determine the terminal width on Unix, Linux, OSX and Windows /// if the `wrap_help` cargo "feature" has been used while compiling, but one might want to /// limit the size (e.g. when the terminal is running fullscreen). /// @@ -745,7 +591,7 @@ impl<'a, 'b> App<'a, 'b> { /// /// # Platform Specific /// - /// Only Unix, Linux, macOS and Windows support automatic determination of terminal width. + /// Only Unix, Linux, OSX and Windows support automatic determination of terminal width. /// /// # Examples /// @@ -756,7 +602,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn max_term_width(mut self, w: usize) -> Self { - self.p.meta.max_w = Some(w); + self.max_w = Some(w); self } @@ -770,99 +616,70 @@ impl<'a, 'b> App<'a, 'b> { /// // Adding a single "flag" argument with a short and help text, using Arg::with_name() /// .arg( /// Arg::with_name("debug") - /// .short("d") + /// .short('d') /// .help("turns on debugging mode") /// ) /// // Adding a single "option" argument with a short, a long, and help text using the less - /// // verbose Arg::from_usage() + /// // verbose Arg::from() /// .arg( - /// Arg::from_usage("-c --config=[CONFIG] 'Optionally sets a config file to use'") + /// Arg::from("-c --config=[CONFIG] 'Optionally sets a config file to use'") /// ) /// # ; /// ``` /// [argument]: ./struct.Arg.html - pub fn arg>>(mut self, a: A) -> Self { - self.p.add_arg(a.into()); + pub fn arg>>(mut self, a: A) -> Self { + let help_heading: Option<&'b str> = if let Some(option_str) = self.help_headings.last() { + *option_str + } else { + None + }; + let arg = a.into().help_heading(help_heading); + self.args.push(arg); self } - /// Adds multiple [arguments] to the list of valid possibilities - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .args( - /// &[Arg::from_usage("[debug] -d 'turns on debugging info'"), - /// Arg::with_name("input").index(1).help("the input file to use")] - /// ) - /// # ; - /// ``` - /// [arguments]: ./struct.Arg.html - pub fn args(mut self, args: &[Arg<'a, 'b>]) -> Self { - for arg in args { - self.p.add_arg_ref(arg); - } + /// Set a custom section heading for future args. Every call to arg will + /// have this header (instead of its default header) until a subsequent + /// call to help_heading + pub fn help_heading(mut self, heading: &'b str) -> Self { + self.help_headings.push(Some(heading)); self } - /// A convenience method for adding a single [argument] from a usage type string. The string - /// used follows the same rules and syntax as [`Arg::from_usage`] - /// - /// **NOTE:** The downside to using this method is that you can not set any additional - /// properties of the [`Arg`] other than what [`Arg::from_usage`] supports. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .arg_from_usage("-c --config= 'Sets a configuration file to use'") - /// # ; - /// ``` - /// [argument]: ./struct.Arg.html - /// [`Arg`]: ./struct.Arg.html - /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage - pub fn arg_from_usage(mut self, usage: &'a str) -> Self { - self.p.add_arg(Arg::from_usage(usage)); + /// Stop using custom section headings. + pub fn stop_custom_headings(mut self) -> Self { + self.help_headings.push(None); self } - /// Adds multiple [arguments] at once from a usage string, one per line. See - /// [`Arg::from_usage`] for details on the syntax and rules supported. - /// - /// **NOTE:** Like [`App::arg_from_usage`] the downside is you only set properties for the - /// [`Arg`]s which [`Arg::from_usage`] supports. + /// Adds multiple [arguments] to the list of valid possibilties /// /// # Examples /// /// ```no_run /// # use clap::{App, Arg}; /// App::new("myprog") - /// .args_from_usage( - /// "-c --config=[FILE] 'Sets a configuration file to use' - /// [debug]... -d 'Sets the debugging level' - /// 'The input file to use'" - /// ) + /// .args(&[ + /// Arg::from("[debug] -d 'turns on debugging info'"), + /// Arg::with_name("input").index(1).help("the input file to use") + /// ]) /// # ; /// ``` /// [arguments]: ./struct.Arg.html - /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage - /// [`App::arg_from_usage`]: ./struct.App.html#method.arg_from_usage - /// [`Arg`]: ./struct.Arg.html - pub fn args_from_usage(mut self, usage: &'a str) -> Self { - for line in usage.lines() { - let l = line.trim(); - if l.is_empty() { - continue; - } - self.p.add_arg(Arg::from_usage(l)); + pub fn args(mut self, args: I) -> Self + where + I: IntoIterator, + T: Into>, + { + // @TODO @perf @p4 @v3-beta: maybe extend_from_slice would be possible and perform better? + // But that may also not let us do `&["-a 'some'", "-b 'other']` because of not Into + for arg in args.into_iter() { + self.args.push(arg.into()); } self } - /// Allows adding a [`SubCommand`] alias, which function as "hidden" subcommands that + /// Allows adding a [``] alias, which function as "hidden" subcommands that /// automatically dispatch as if this subcommand was used. This is more efficient, and easier /// than creating multiple hidden subcommands as one only needs to check for the existence of /// this command, and not all variants. @@ -870,24 +687,24 @@ impl<'a, 'b> App<'a, 'b> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand}; + /// # use clap::{App, Arg, }; /// let m = App::new("myprog") - /// .subcommand(SubCommand::with_name("test") + /// .subcommand(App::new("test") /// .alias("do-stuff")) /// .get_matches_from(vec!["myprog", "do-stuff"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html pub fn alias>(mut self, name: S) -> Self { - if let Some(ref mut als) = self.p.meta.aliases { + if let Some(ref mut als) = self.aliases { als.push((name.into(), false)); } else { - self.p.meta.aliases = Some(vec![(name.into(), false)]); + self.aliases = Some(vec![(name.into(), false)]); } self } - /// Allows adding [`SubCommand`] aliases, which function as "hidden" subcommands that + /// Allows adding [``] aliases, which function as "hidden" subcommands that /// automatically dispatch as if this subcommand was used. This is more efficient, and easier /// than creating multiple hidden subcommands as one only needs to check for the existence of /// this command, and not all variants. @@ -895,9 +712,9 @@ impl<'a, 'b> App<'a, 'b> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, SubCommand}; + /// # use clap::{App, Arg, }; /// let m = App::new("myprog") - /// .subcommand(SubCommand::with_name("test") + /// .subcommand(App::new("test") /// .aliases(&["do-stuff", "do-tests", "tests"])) /// .arg(Arg::with_name("input") /// .help("the file to add") @@ -906,64 +723,64 @@ impl<'a, 'b> App<'a, 'b> { /// .get_matches_from(vec!["myprog", "do-tests"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html pub fn aliases(mut self, names: &[&'b str]) -> Self { - if let Some(ref mut als) = self.p.meta.aliases { + if let Some(ref mut als) = self.aliases { for n in names { als.push((n, false)); } } else { - self.p.meta.aliases = Some(names.iter().map(|n| (*n, false)).collect::>()); + self.aliases = Some(names.iter().map(|n| (*n, false)).collect::>()); } self } - /// Allows adding a [`SubCommand`] alias that functions exactly like those defined with + /// Allows adding a [``] alias that functions exactly like those defined with /// [`App::alias`], except that they are visible inside the help message. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand}; + /// # use clap::{App, Arg, }; /// let m = App::new("myprog") - /// .subcommand(SubCommand::with_name("test") + /// .subcommand(App::new("test") /// .visible_alias("do-stuff")) /// .get_matches_from(vec!["myprog", "do-stuff"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`App::alias`]: ./struct.App.html#method.alias pub fn visible_alias>(mut self, name: S) -> Self { - if let Some(ref mut als) = self.p.meta.aliases { + if let Some(ref mut als) = self.aliases { als.push((name.into(), true)); } else { - self.p.meta.aliases = Some(vec![(name.into(), true)]); + self.aliases = Some(vec![(name.into(), true)]); } self } - /// Allows adding multiple [`SubCommand`] aliases that functions exactly like those defined + /// Allows adding multiple [``] aliases that functions exactly like those defined /// with [`App::aliases`], except that they are visible inside the help message. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand}; + /// # use clap::{App, Arg, }; /// let m = App::new("myprog") - /// .subcommand(SubCommand::with_name("test") + /// .subcommand(App::new("test") /// .visible_aliases(&["do-stuff", "tests"])) /// .get_matches_from(vec!["myprog", "do-stuff"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`App::aliases`]: ./struct.App.html#method.aliases pub fn visible_aliases(mut self, names: &[&'b str]) -> Self { - if let Some(ref mut als) = self.p.meta.aliases { + if let Some(ref mut als) = self.aliases { for n in names { als.push((n, true)); } } else { - self.p.meta.aliases = Some(names.iter().map(|n| (*n, true)).collect::>()); + self.aliases = Some(names.iter().map(|n| (*n, true)).collect::>()); } self } @@ -991,19 +808,18 @@ impl<'a, 'b> App<'a, 'b> { /// ```no_run /// # use clap::{App, ArgGroup}; /// App::new("app") - /// .args_from_usage( - /// "--set-ver [ver] 'set the version manually' - /// --major 'auto increase major' - /// --minor 'auto increase minor' - /// --patch 'auto increase patch'") + /// .arg("--set-ver [ver] 'set the version manually'") + /// .arg("--major 'auto increase major'") + /// .arg("--minor 'auto increase minor'") + /// .arg("--patch 'auto increase patch'") /// .group(ArgGroup::with_name("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) /// # ; /// ``` /// [`ArgGroup`]: ./struct.ArgGroup.html - pub fn group(mut self, group: ArgGroup<'a>) -> Self { - self.p.add_group(group); + pub fn group(mut self, group: ArgGroup<'b>) -> Self { + self.groups.push(group); self } @@ -1014,13 +830,12 @@ impl<'a, 'b> App<'a, 'b> { /// ```no_run /// # use clap::{App, ArgGroup}; /// App::new("app") - /// .args_from_usage( - /// "--set-ver [ver] 'set the version manually' - /// --major 'auto increase major' - /// --minor 'auto increase minor' - /// --patch 'auto increase patch' - /// -c [FILE] 'a config file' - /// -i [IFACE] 'an interface'") + /// .arg("--set-ver [ver] 'set the version manually'") + /// .arg("--major 'auto increase major'") + /// .arg("--minor 'auto increase minor'") + /// .arg("--patch 'auto increase patch'") + /// .arg("-c [FILE] 'a config file'") + /// .arg("-i [IFACE] 'an interface'") /// .groups(&[ /// ArgGroup::with_name("vers") /// .args(&["set-ver", "major", "minor","patch"]) @@ -1032,14 +847,14 @@ impl<'a, 'b> App<'a, 'b> { /// ``` /// [`ArgGroup`]: ./struct.ArgGroup.html /// [`App`]: ./struct.App.html - pub fn groups(mut self, groups: &[ArgGroup<'a>]) -> Self { + pub fn groups(mut self, groups: &[ArgGroup<'b>]) -> Self { for g in groups { self = self.group(g.into()); } self } - /// Adds a [`SubCommand`] to the list of valid possibilities. Subcommands are effectively + /// Adds a [``] to the list of valid possibilities. Subcommands are effectively /// sub-[`App`]s, because they can contain their own arguments, subcommands, version, usage, /// etc. They also function just like [`App`]s, in that they get their own auto generated help, /// version, and usage. @@ -1047,47 +862,47 @@ impl<'a, 'b> App<'a, 'b> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand}; + /// # use clap::{App, Arg, }; /// App::new("myprog") - /// .subcommand(SubCommand::with_name("config") + /// .subcommand(App::new("config") /// .about("Controls configuration features") - /// .arg_from_usage(" 'Required configuration file to use'")) + /// .arg(" 'Required configuration file to use'")) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`App`]: ./struct.App.html - pub fn subcommand(mut self, subcmd: App<'a, 'b>) -> Self { - self.p.add_subcommand(subcmd); + pub fn subcommand(mut self, subcmd: App<'b>) -> Self { + self.subcommands.push(subcmd); self } /// Adds multiple subcommands to the list of valid possibilities by iterating over an - /// [`IntoIterator`] of [`SubCommand`]s + /// [`IntoIterator`] of [``]s /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, SubCommand}; + /// # use clap::{App, Arg, }; /// # App::new("myprog") /// .subcommands( vec![ - /// SubCommand::with_name("config").about("Controls configuration functionality") + /// App::new("config").about("Controls configuration functionality") /// .arg(Arg::with_name("config_file").index(1)), - /// SubCommand::with_name("debug").about("Controls debug functionality")]) + /// App::new("debug").about("Controls debug functionality")]) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html pub fn subcommands(mut self, subcmds: I) -> Self where - I: IntoIterator>, + I: IntoIterator>, { for subcmd in subcmds { - self.p.add_subcommand(subcmd); + self.subcommands.push(subcmd); } self } - /// Allows custom ordering of [`SubCommand`]s within the help message. Subcommands with a lower + /// Allows custom ordering of [``]s within the help message. Subcommands with a lower /// value will be displayed first in the help message. This is helpful when one would like to /// emphasise frequently used subcommands, or prioritize those towards the top of the list. /// Duplicate values **are** allowed. Subcommands with duplicate display orders will be @@ -1098,15 +913,15 @@ impl<'a, 'b> App<'a, 'b> { /// # Examples /// /// ```rust - /// # use clap::{App, SubCommand}; + /// # use clap::{App, }; /// let m = App::new("cust-ord") - /// .subcommand(SubCommand::with_name("alpha") // typically subcommands are grouped + /// .subcommand(App::new("alpha") // typically subcommands are grouped /// // alphabetically by name. Subcommands /// // without a display_order have a value of /// // 999 and are displayed alphabetically with /// // all other 999 subcommands /// .about("Some help and text")) - /// .subcommand(SubCommand::with_name("beta") + /// .subcommand(App::new("beta") /// .display_order(1) // In order to force this subcommand to appear *first* /// // all we have to do is give it a value lower than 999. /// // Any other subcommands with a value of 1 will be displayed @@ -1133,9 +948,48 @@ impl<'a, 'b> App<'a, 'b> { /// beta I should be first! /// alpha Some help and text /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html pub fn display_order(mut self, ord: usize) -> Self { - self.p.meta.disp_ord = ord; + self.disp_ord = ord; + self + } + + /// Allows one to mutate an [`Arg`] after it's been added to an `App`. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg}; + /// + /// let mut app = App::new("foo") + /// .arg(Arg::with_name("bar") + /// .short('b')) + /// .mut_arg("bar", |a| a.short('B')); + /// + /// let res = app.try_get_matches_from_mut(vec!["foo", "-b"]); + /// + /// // Since we changed `bar`'s short to "B" this should err as there + /// // is no `-b` anymore, only `-B` + /// + /// assert!(res.is_err()); + /// + /// let res = app.try_get_matches_from_mut(vec!["foo", "-B"]); + /// assert!(res.is_ok()); + /// ``` + /// [`Arg`]: ./struct.Arg.html + pub fn mut_arg(mut self, arg_id: T, f: F) -> Self + where + F: FnOnce(Arg<'b>) -> Arg<'b>, + T: Key + Into<&'b str>, + { + let id = arg_id.key(); + let a = self.args.remove_by_name(id).unwrap_or_else(|| Arg { + id, + name: arg_id.into(), + ..Arg::default() + }); + self.args.push(f(a)); + self } @@ -1159,11 +1013,8 @@ impl<'a, 'b> App<'a, 'b> { pub fn print_help(&mut self) -> ClapResult<()> { // If there are global arguments, or settings we need to propagate them down to subcommands // before parsing incase we run into a subcommand - self.p.propagate_globals(); - self.p.propagate_settings(); - self.p.derive_display_order(); + self._build(); - self.p.create_help_and_version(); let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); self.write_help(&mut buf_w) @@ -1187,6 +1038,10 @@ impl<'a, 'b> App<'a, 'b> { /// [`-h` (short)]: ./struct.Arg.html#method.help /// [`--help` (long)]: ./struct.Arg.html#method.long_help pub fn print_long_help(&mut self) -> ClapResult<()> { + // If there are global arguments, or settings we need to propagate them down to subcommands + // before parsing incase we run into a subcommand + self._build(); + let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); self.write_long_help(&mut buf_w) @@ -1198,10 +1053,6 @@ impl<'a, 'b> App<'a, 'b> { /// **NOTE:** clap has the ability to distinguish between "short" and "long" help messages /// depending on if the user ran [`-h` (short)] or [`--help` (long)] /// - /// **NOTE:** There is a known bug where this method does not write propagated global arguments - /// or autogenerated arguments (i.e. the default help/version args). Prefer - /// [`App::write_long_help`] instead if possible! - /// /// # Examples /// /// ```rust @@ -1214,17 +1065,11 @@ impl<'a, 'b> App<'a, 'b> { /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html /// [`-h` (short)]: ./struct.Arg.html#method.help /// [`--help` (long)]: ./struct.Arg.html#method.long_help - pub fn write_help(&self, w: &mut W) -> ClapResult<()> { - // PENDING ISSUE: 808 - // https://github.com/clap-rs/clap/issues/808 - // If there are global arguments, or settings we need to propagate them down to subcommands - // before parsing incase we run into a subcommand - // self.p.propagate_globals(); - // self.p.propagate_settings(); - // self.p.derive_display_order(); - // self.p.create_help_and_version(); + pub fn write_help(&mut self, w: &mut W) -> ClapResult<()> { + self._build(); - Help::write_app_help(w, self, false) + let p = Parser::new(self); + Help::new(w, &p, false, false).write_help() } /// Writes the full help message to the user to a [`io::Write`] object in the same method as if @@ -1246,14 +1091,10 @@ impl<'a, 'b> App<'a, 'b> { /// [`-h` (short)]: ./struct.Arg.html#method.help /// [`--help` (long)]: ./struct.Arg.html#method.long_help pub fn write_long_help(&mut self, w: &mut W) -> ClapResult<()> { - // If there are global arguments, or settings we need to propagate them down to subcommands - // before parsing incase we run into a subcommand - self.p.propagate_globals(); - self.p.propagate_settings(); - self.p.derive_display_order(); - self.p.create_help_and_version(); + self._build(); - Help::write_app_help(w, self, true) + let p = Parser::new(self); + Help::new(w, &p, true, false).write_help() } /// Writes the version message to the user to a [`io::Write`] object as if the user ran `-V`. @@ -1274,7 +1115,7 @@ impl<'a, 'b> App<'a, 'b> { /// [`-V` (short)]: ./struct.App.html#method.version /// [`--version` (long)]: ./struct.App.html#method.long_version pub fn write_version(&self, w: &mut W) -> ClapResult<()> { - self.p.write_version(w, false).map_err(From::from) + self._write_version(w, false).map_err(From::from) } /// Writes the version message to the user to a [`io::Write`] object @@ -1295,143 +1136,20 @@ impl<'a, 'b> App<'a, 'b> { /// [`-V` (short)]: ./struct.App.html#method.version /// [`--version` (long)]: ./struct.App.html#method.long_version pub fn write_long_version(&self, w: &mut W) -> ClapResult<()> { - self.p.write_version(w, true).map_err(From::from) - } - - /// Generate a completions file for a specified shell at compile time. - /// - /// **NOTE:** to generate the file at compile time you must use a `build.rs` "Build Script" - /// - /// # Examples - /// - /// The following example generates a bash completion script via a `build.rs` script. In this - /// simple example, we'll demo a very small application with only a single subcommand and two - /// args. Real applications could be many multiple levels deep in subcommands, and have tens or - /// potentially hundreds of arguments. - /// - /// First, it helps if we separate out our `App` definition into a separate file. Whether you - /// do this as a function, or bare App definition is a matter of personal preference. - /// - /// ``` - /// // src/cli.rs - /// - /// use clap::{App, Arg, SubCommand}; - /// - /// pub fn build_cli() -> App<'static, 'static> { - /// App::new("compl") - /// .about("Tests completions") - /// .arg(Arg::with_name("file") - /// .help("some input file")) - /// .subcommand(SubCommand::with_name("test") - /// .about("tests things") - /// .arg(Arg::with_name("case") - /// .long("case") - /// .takes_value(true) - /// .help("the case to test"))) - /// } - /// ``` - /// - /// In our regular code, we can simply call this `build_cli()` function, then call - /// `get_matches()`, or any of the other normal methods directly after. For example: - /// - /// ```ignore - /// // src/main.rs - /// - /// mod cli; - /// - /// fn main() { - /// let m = cli::build_cli().get_matches(); - /// - /// // normal logic continues... - /// } - /// ``` - /// - /// Next, we set up our `Cargo.toml` to use a `build.rs` build script. - /// - /// ```toml - /// # Cargo.toml - /// build = "build.rs" - /// - /// [build-dependencies] - /// clap = "2.23" - /// ``` - /// - /// Next, we place a `build.rs` in our project root. - /// - /// ```ignore - /// extern crate clap; - /// - /// use clap::Shell; - /// - /// include!("src/cli.rs"); - /// - /// fn main() { - /// let outdir = match env::var_os("OUT_DIR") { - /// None => return, - /// Some(outdir) => outdir, - /// }; - /// let mut app = build_cli(); - /// app.gen_completions("myapp", // We need to specify the bin name manually - /// Shell::Bash, // Then say which shell to build completions for - /// outdir); // Then say where write the completions to - /// } - /// ``` - /// Now, once we compile there will be a `{bin_name}.bash` file in the directory. - /// Assuming we compiled with debug mode, it would be somewhere similar to - /// `/target/debug/build/myapp-/out/myapp.bash`. - /// - /// Fish shell completions will use the file format `{bin_name}.fish` - pub fn gen_completions, S: Into>( - &mut self, - bin_name: S, - for_shell: Shell, - out_dir: T, - ) { - self.p.meta.bin_name = Some(bin_name.into()); - self.p.gen_completions(for_shell, out_dir.into()); + self._write_version(w, true).map_err(From::from) } + /// @TODO-v3-alpha @docs @p2: write docs + pub fn generate_usage(&mut self) -> String { + // If there are global arguments, or settings we need to propgate them down to subcommands + // before parsing incase we run into a subcommand + if !self.settings.is_set(AppSettings::Built) { + self._build(); + } - /// Generate a completions file for a specified shell at runtime. Until `cargo install` can - /// install extra files like a completion script, this may be used e.g. in a command that - /// outputs the contents of the completion script, to be redirected into a file by the user. - /// - /// # Examples - /// - /// Assuming a separate `cli.rs` like the [example above](./struct.App.html#method.gen_completions), - /// we can let users generate a completion script using a command: - /// - /// ```ignore - /// // src/main.rs - /// - /// mod cli; - /// use std::io; - /// - /// fn main() { - /// let matches = cli::build_cli().get_matches(); - /// - /// if matches.is_present("generate-bash-completions") { - /// cli::build_cli().gen_completions_to("myapp", Shell::Bash, &mut io::stdout()); - /// } - /// - /// // normal logic continues... - /// } - /// - /// ``` - /// - /// Usage: - /// - /// ```shell - /// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash - /// ``` - pub fn gen_completions_to>( - &mut self, - bin_name: S, - for_shell: Shell, - buf: &mut W, - ) { - self.p.meta.bin_name = Some(bin_name.into()); - self.p.gen_completions_to(for_shell, buf); + let mut parser = Parser::new(self); + parser._build(); + Usage::new(&parser).create_usage_with_title(&[]) } /// Starts the parsing process, upon a failed parse an error will be displayed to the user and @@ -1448,7 +1166,40 @@ impl<'a, 'b> App<'a, 'b> { /// .get_matches(); /// ``` /// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html - pub fn get_matches(self) -> ArgMatches<'a> { self.get_matches_from(&mut env::args_os()) } + pub fn get_matches(self) -> ArgMatches { self.get_matches_from(&mut env::args_os()) } + + /// Starts the parsing process, just like [`App::get_matches`] but doesn't consume the `App` + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// let mut app = App::new("myprog") + /// // Args and options go here... + /// ; + /// let matches = app.get_matches_mut(); + /// ``` + /// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html + /// [`App::get_matches`]: ./struct.App.html#method.get_matches + pub fn get_matches_mut(&mut self) -> ArgMatches { + self.try_get_matches_from_mut(&mut env::args_os()) + .unwrap_or_else(|e| { + // Otherwise, write to stderr and exit + if e.use_stderr() { + wlnerr!("{}", e.message); + if self.settings.is_set(AppSettings::WaitOnError) { + wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); + let mut s = String::new(); + let i = io::stdin(); + i.lock().read_line(&mut s).unwrap(); + } + drop(e); + process::exit(1); + } + + e.exit() + }) + } /// Starts the parsing process. This method will return a [`clap::Result`] type instead of exiting /// the process on failed parse. By default this method gets matches from [`env::args_os`] @@ -1464,7 +1215,7 @@ impl<'a, 'b> App<'a, 'b> { /// # use clap::{App, Arg}; /// let matches = App::new("myprog") /// // Args and options go here... - /// .get_matches_safe() + /// .try_get_matches() /// .unwrap_or_else( |e| e.exit() ); /// ``` /// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html @@ -1475,9 +1226,9 @@ impl<'a, 'b> App<'a, 'b> { /// [`clap::Result`]: ./type.Result.html /// [`clap::Error`]: ./struct.Error.html /// [`kind`]: ./struct.Error.html - pub fn get_matches_safe(self) -> ClapResult> { + pub fn try_get_matches(self) -> ClapResult { // Start the parsing - self.get_matches_from_safe(&mut env::args_os()) + self.try_get_matches_from(&mut env::args_os()) } /// Starts the parsing process. Like [`App::get_matches`] this method does not return a [`clap::Result`] @@ -1501,16 +1252,16 @@ impl<'a, 'b> App<'a, 'b> { /// [`clap::Result`]: ./type.Result.html /// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html /// [`AppSettings::NoBinaryName`]: ./enum.AppSettings.html#variant.NoBinaryName - pub fn get_matches_from(mut self, itr: I) -> ArgMatches<'a> + pub fn get_matches_from(mut self, itr: I) -> ArgMatches where I: IntoIterator, T: Into + Clone, { - self.get_matches_from_safe_borrow(itr).unwrap_or_else(|e| { + self.try_get_matches_from_mut(itr).unwrap_or_else(|e| { // Otherwise, write to stderr and exit if e.use_stderr() { wlnerr!("{}", e.message); - if self.p.is_set(AppSettings::WaitOnError) { + if self.settings.is_set(AppSettings::WaitOnError) { wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); let mut s = String::new(); let i = io::stdin(); @@ -1527,7 +1278,7 @@ impl<'a, 'b> App<'a, 'b> { } /// Starts the parsing process. A combination of [`App::get_matches_from`], and - /// [`App::get_matches_safe`] + /// [`App::try_get_matches`] /// /// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are /// used. It will return a [`clap::Error`], where the [`kind`] is a [`ErrorKind::HelpDisplayed`] @@ -1545,11 +1296,11 @@ impl<'a, 'b> App<'a, 'b> { /// /// let matches = App::new("myprog") /// // Args and options go here... - /// .get_matches_from_safe(arg_vec) + /// .try_get_matches_from(arg_vec) /// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) }); /// ``` /// [`App::get_matches_from`]: ./struct.App.html#method.get_matches_from - /// [`App::get_matches_safe`]: ./struct.App.html#method.get_matches_safe + /// [`App::try_get_matches`]: ./struct.App.html#method.try_get_matches /// [`ErrorKind::HelpDisplayed`]: ./enum.ErrorKind.html#variant.HelpDisplayed /// [`ErrorKind::VersionDisplayed`]: ./enum.ErrorKind.html#variant.VersionDisplayed /// [`Error::exit`]: ./struct.Error.html#method.exit @@ -1558,16 +1309,16 @@ impl<'a, 'b> App<'a, 'b> { /// [`Error::exit`]: ./struct.Error.html#method.exit /// [`kind`]: ./struct.Error.html /// [`AppSettings::NoBinaryName`]: ./enum.AppSettings.html#variant.NoBinaryName - pub fn get_matches_from_safe(mut self, itr: I) -> ClapResult> + pub fn try_get_matches_from(mut self, itr: I) -> ClapResult where I: IntoIterator, T: Into + Clone, { - self.get_matches_from_safe_borrow(itr) + self.try_get_matches_from_mut(itr) } /// Starts the parsing process without consuming the [`App`] struct `self`. This is normally not - /// the desired functionality, instead prefer [`App::get_matches_from_safe`] which *does* + /// the desired functionality, instead prefer [`App::try_get_matches_from`] which *does* /// consume `self`. /// /// **NOTE:** The first argument will be parsed as the binary name unless @@ -1581,28 +1332,17 @@ impl<'a, 'b> App<'a, 'b> { /// /// let mut app = App::new("myprog"); /// // Args and options go here... - /// let matches = app.get_matches_from_safe_borrow(arg_vec) + /// let matches = app.try_get_matches_from_mut(arg_vec) /// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) }); /// ``` /// [`App`]: ./struct.App.html - /// [`App::get_matches_from_safe`]: ./struct.App.html#method.get_matches_from_safe + /// [`App::try_get_matches_from`]: ./struct.App.html#method.try_get_matches_from /// [`AppSettings::NoBinaryName`]: ./enum.AppSettings.html#variant.NoBinaryName - pub fn get_matches_from_safe_borrow(&mut self, itr: I) -> ClapResult> + pub fn try_get_matches_from_mut(&mut self, itr: I) -> ClapResult where I: IntoIterator, T: Into + Clone, { - // If there are global arguments, or settings we need to propagate them down to subcommands - // before parsing incase we run into a subcommand - if !self.p.is_set(AppSettings::Propagated) { - self.p.propagate_globals(); - self.p.propagate_settings(); - self.p.derive_display_order(); - self.p.set(AppSettings::Propagated); - } - - let mut matcher = ArgMatcher::new(); - let mut it = itr.into_iter(); // Get the name of the program (argument 1 of env::args()) and determine the // actual file @@ -1611,36 +1351,535 @@ impl<'a, 'b> App<'a, 'b> { // will have two arguments, './target/release/my_prog', '-a' but we don't want // to display // the full path when displaying help messages and such - if !self.p.is_set(AppSettings::NoBinaryName) { + if !self.settings.is_set(AppSettings::NoBinaryName) { if let Some(name) = it.next() { let bn_os = name.into(); let p = Path::new(&*bn_os); if let Some(f) = p.file_name() { if let Some(s) = f.to_os_string().to_str() { - if self.p.meta.bin_name.is_none() { - self.p.meta.bin_name = Some(s.to_owned()); + if self.bin_name.is_none() { + self.bin_name = Some(s.to_owned()); } } } } } - // do the real parsing - if let Err(e) = self.p.get_matches_with(&mut matcher, &mut it.peekable()) { - return Err(e); + self._do_parse(&mut it.peekable()) + } +} + +// Internally used only +#[doc(hidden)] +impl<'b> App<'b> { + #[doc(hidden)] + fn _do_parse(&mut self, it: &mut Peekable) -> ClapResult + where + I: Iterator, + T: Into + Clone, + { + debugln!("App::_do_parse;"); + let mut matcher = ArgMatcher::new(); + + // If there are global arguments, or settings we need to propgate them down to subcommands + // before parsing incase we run into a subcommand + if !self.settings.is_set(AppSettings::Built) { + self._build(); + } + + { + let mut parser = Parser::new(self); + + // do the real parsing + parser.get_matches_with(&mut matcher, it)?; } - let global_arg_vec: Vec<&str> = (&self).p.global_args.iter().map(|ga| ga.b.name).collect(); + let global_arg_vec: Vec = self + .args + .args + .iter() + .filter(|a| a.global) + .map(|ga| ga.id) + .collect(); + matcher.propagate_globals(&global_arg_vec); - Ok(matcher.into()) + Ok(matcher.into_inner()) + } + + // used in clap_generate (https://github.com/clap-rs/clap_generate) + #[doc(hidden)] + pub fn _build(&mut self) { + debugln!("App::_build;"); + // Make sure all the globally set flags apply to us as well + self.settings = self.settings | self.g_settings; + + self._derive_display_order(); + self._create_help_and_version(); + // Perform expensive debug assertions + debug_assert!({ + for a in self.args.args.iter() { + self._arg_debug_asserts(a); + } + true + }); + + let mut pos_counter = 1; + for a in self.args.args.iter_mut() { + // Fill in the groups + if let Some(ref grps) = a.groups { + for &g in grps { + let mut found = false; + if let Some(ag) = self.groups.iter_mut().find(|grp| grp.id == g) { + ag.args.push(a.id); + found = true; + } + if !found { + let mut ag = ArgGroup::_with_id(g); + ag.args.push(a.id); + self.groups.push(ag); + } + } + } + + // Figure out implied settings + 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.settings.set(AppSettings::DontCollapseArgsInUsage); + self.settings.set(AppSettings::ContainsLast); + } + a._build(); + if a.short.is_none() && a.long.is_none() && a.index.is_none() { + a.index = Some(pos_counter); + pos_counter += 1; + } + } + + debug_assert!(self._app_debug_asserts()); + self.args._build(); + self.settings.set(AppSettings::Built); + } + + // Perform some expensive assertions on the Parser itself + fn _app_debug_asserts(&mut self) -> bool { + debugln!("App::_app_debug_asserts;"); + for name in self.args.args.iter().map(|x| x.id) { + if self.args.args.iter().filter(|x| x.id == name).count() > 1 { + panic!(format!( + "Arg names must be unique, found {} more than once", + name + )); + } + } + // * Args listed inside groups should exist + // * Groups should not have naming conflicts with Args + + // * Will be removed as a part of removing String types + // let g = groups!(self).find(|g| { + // g.args + // .iter() + // .any(|arg| !(find!(self, arg).is_some() || groups!(self).any(|g| &g.name == arg))) + // }); + // assert!( + // g.is_none(), + // "The group '{}' contains an arg that doesn't exist or has a naming conflict with a group.", + // g.unwrap().name + // ); + true + } + + pub fn _propagate(&mut self, prop: Propagation) { + macro_rules! propagate_subcmd { + ($_self:ident, $sc:expr) => {{ + // We have to create a new scope in order to tell rustc the borrow of `sc` is + // done and to recursively call this method + { + let vsc = $_self.settings.is_set(AppSettings::VersionlessSubcommands); + let gv = $_self.settings.is_set(AppSettings::GlobalVersion); + + if vsc { + $sc.set(AppSettings::DisableVersion); + } + if gv && $sc.version.is_none() && $_self.version.is_some() { + $sc.set(AppSettings::GlobalVersion); + $sc.version = Some($_self.version.unwrap()); + } + $sc.settings = $sc.settings | $_self.g_settings; + $sc.g_settings = $sc.g_settings | $_self.g_settings; + $sc.term_w = $_self.term_w; + $sc.max_w = $_self.max_w; + } + { + for a in $_self + .args + .args + .iter() + .filter(|a| a.global) + { + $sc.args.push(a.clone()); + } + } + }} + } + + debugln!("App::_propagate:{}", self.name); + match prop { + Propagation::NextLevel | Propagation::Full => { + for sc in &mut self.subcommands { + propagate_subcmd!(self, sc); + if prop == Propagation::Full { + sc._propagate(prop); + } + } + }, + Propagation::To(id) => { + let mut sc = self.subcommands.iter_mut().find(|sc| sc.id == id).expect(INTERNAL_ERROR_MSG); + propagate_subcmd!(self, sc); + }, + Propagation::None => { + return; + }, + } + } + + pub(crate) fn _create_help_and_version(&mut self) { + debugln!("App::_create_help_and_version;"); + if !(self + .args + .args + .iter() + .any(|x| x.long == Some("help") || x.id == HELP_HASH)) + { + debugln!("App::_create_help_and_version: Building --help"); + let mut help = Arg::with_name("help") + .long("help") + .help("Prints help information"); + if !self.args.args.iter().any(|x| x.short == Some('h')) { + help = help.short('h'); + } + + self.args.push(help); + } + if !(self + .args + .args + .iter() + .any(|x| x.long == Some("version") || x.id == VERSION_HASH) + || self.is_set(AppSettings::DisableVersion)) + { + debugln!("App::_create_help_and_version: Building --version"); + let mut version = Arg::with_name("version") + .long("version") + .help("Prints version information"); + if !self.args.args.iter().any(|x| x.short == Some('V')) { + version = version.short('V'); + } + + self.args.push(version); + } + if self.has_subcommands() + && !self.is_set(AppSettings::DisableHelpSubcommand) + && !subcommands!(self).any(|s| s.id == HELP_HASH) + { + debugln!("App::_create_help_and_version: Building help"); + self.subcommands.push( + App::new("help") + .about("Prints this message or the help of the given subcommand(s)"), + ); + } + } + + pub(crate) fn _derive_display_order(&mut self) { + debugln!("App::_derive_display_order:{}", self.name); + if self.settings.is_set(AppSettings::DeriveDisplayOrder) { + for (i, a) in self + .args + .args + .iter_mut() + .filter(|a| a.has_switch()) + .filter(|a| a.disp_ord == 999) + .enumerate() + { + a.disp_ord = i; + } + for (i, mut sc) in &mut subcommands_mut!(self) + .enumerate() + .filter(|&(_, ref sc)| sc.disp_ord == 999) + { + sc.disp_ord = i; + } + } + for sc in subcommands_mut!(self) { + sc._derive_display_order(); + } + } + + // Perform expensive assertions on the Arg instance + fn _arg_debug_asserts(&self, a: &Arg) -> bool { + debugln!("App::_arg_debug_asserts:{}", a.name); + + // Long conflicts + if let Some(l) = a.long { + assert!( + self.args.args.iter().filter(|x| x.long == Some(l)).count() < 2, + "Argument long must be unique\n\n\t--{} is already in use", + l + ); + } + + // Short conflicts + if let Some(s) = a.short { + assert!( + self.args.args.iter().filter(|x| x.short == Some(s)).count() < 2, + "Argument short must be unique\n\n\t-{} is already in use", + s + ); + } + + if let Some(idx) = a.index { + // No index conflicts + assert!( + positionals!(self).fold(0, |acc, p| if p.index == Some(idx) { + acc + 1 + } else { + acc + }) < 2, + "Argument '{}' has the same index as another positional \ + argument\n\n\tUse Arg::setting(ArgSettings::MultipleValues) to allow one \ + positional argument to take multiple values", + a.name + ); + } + if a.is_set(ArgSettings::Last) { + assert!( + a.long.is_none(), + "Flags or Options may not have last(true) set. {} has both a long and \ + last(true) set.", + a.name + ); + assert!( + a.short.is_none(), + "Flags or Options may not have last(true) set. {} has both a short and \ + last(true) set.", + a.name + ); + } + assert!( + !(a.is_set(ArgSettings::Required) && a.global), + "Global arguments cannot be required.\n\n\t'{}' is marked as \ + global and required", + a.name + ); + + true + } + + // used in clap_generate (https://github.com/clap-rs/clap_generate) + #[doc(hidden)] + pub fn _build_bin_names(&mut self) { + debugln!("App::_build_bin_names;"); + for mut sc in subcommands_mut!(self) { + debug!("Parser::build_bin_names:iter: bin_name set..."); + if sc.bin_name.is_none() { + sdebugln!("No"); + let bin_name = format!( + "{}{}{}", + self.bin_name.as_ref().unwrap_or(&self.name.clone()), + if self.bin_name.is_some() { " " } else { "" }, + &*sc.name + ); + debugln!( + "Parser::build_bin_names:iter: Setting bin_name of {} to {}", + self.name, + bin_name + ); + sc.bin_name = Some(bin_name); + } else { + sdebugln!("yes ({:?})", sc.bin_name); + } + debugln!( + "Parser::build_bin_names:iter: Calling build_bin_names from...{}", + sc.name + ); + sc._build_bin_names(); + } + } + + pub(crate) fn _write_version(&self, w: &mut W, use_long: bool) -> io::Result<()> { + debugln!("App::_write_version;"); + let ver = if use_long { + self.long_version + .unwrap_or_else(|| self.version.unwrap_or("")) + } else { + self.version + .unwrap_or_else(|| self.long_version.unwrap_or("")) + }; + if let Some(bn) = self.bin_name.as_ref() { + if bn.contains(' ') { + // Incase we're dealing with subcommands i.e. git mv is translated to git-mv + write!(w, "{} {}", bn.replace(" ", "-"), ver) + } else { + write!(w, "{} {}", &self.name[..], ver) + } + } else { + write!(w, "{} {}", &self.name[..], ver) + } + } + + pub(crate) fn format_group(&self, g: Id) -> String { + let g_string = self + .unroll_args_in_group(g) + .iter() + .filter_map(|&x| self.find(x)) + .map(|x| { + if x.index.is_some() { + x.name.to_owned() + } else { + x.to_string() + } + }) + .collect::>() + .join("|"); + format!("<{}>", &*g_string) + } +} + +// Internal Query Methods +#[doc(hidden)] +impl<'b> App<'b> { + pub(crate) fn find(&self, arg_id: Id) -> Option<&Arg<'b>> { + self.args.args.iter().find(|a| a.id == arg_id) + } + + // Should we color the output? None=determined by output location, true=yes, false=no + #[doc(hidden)] + pub fn color(&self) -> ColorWhen { + debugln!("App::color;"); + debug!("App::color: Color setting..."); + if self.is_set(AppSettings::ColorNever) { + sdebugln!("Never"); + ColorWhen::Never + } else if self.is_set(AppSettings::ColorAlways) { + sdebugln!("Always"); + ColorWhen::Always + } else { + sdebugln!("Auto"); + ColorWhen::Auto + } + } + + pub(crate) fn contains_short(&self, s: char) -> bool { + if !self.is_set(AppSettings::Built) { + panic!("If App::_build hasn't been called, manually search through Arg shorts"); + } + self.args.contains_short(s) + } + + pub fn is_set(&self, s: AppSettings) -> bool { + self.settings.is_set(s) || self.g_settings.is_set(s) + } + + pub fn set(&mut self, s: AppSettings) { self.settings.set(s) } + + pub fn set_global(&mut self, s: AppSettings) { self.g_settings.set(s) } + + pub fn unset_global(&mut self, s: AppSettings) { self.g_settings.unset(s) } + + pub fn unset(&mut self, s: AppSettings) { self.settings.unset(s) } + + pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } + + pub fn has_args(&self) -> bool { !self.args.is_empty() } + + pub fn has_opts(&self) -> bool { opts!(self).count() > 0 } + + pub fn has_flags(&self) -> bool { flags!(self).count() > 0 } + + pub fn has_positionals(&self) -> bool { positionals!(self).count() > 0 } + + pub fn has_visible_opts(&self) -> bool { opts!(self).any(|o| !o.is_set(ArgSettings::Hidden)) } + + pub fn has_visible_flags(&self) -> bool { flags!(self).any(|o| !o.is_set(ArgSettings::Hidden)) } + + pub fn has_visible_positionals(&self) -> bool { + positionals!(self).any(|o| !o.is_set(ArgSettings::Hidden)) + } + + pub fn has_visible_subcommands(&self) -> bool { + subcommands!(self) + .filter(|sc| sc.name != "help") + .any(|sc| !sc.is_set(AppSettings::Hidden)) + } + + pub(crate) fn unroll_args_in_group(&self, group: Id) -> Vec { + let mut g_vec = vec![group]; + let mut args = vec![]; + + while let Some(ref g) = g_vec.pop() { + for &n in self + .groups + .iter() + .find(|grp| &grp.id == g) + .expect(INTERNAL_ERROR_MSG) + .args + .iter() + { + if !args.contains(&n) { + if self.find(n).is_some() { + args.push(n) + } else { + g_vec.push(n); + } + } + } + } + + args + } + + pub(crate) fn unroll_requirements_for_arg(&self, arg: Id, matcher: &ArgMatcher) -> Vec { + let requires_if_or_not = |&(val, req_arg)| { + if let Some(v) = val { + if matcher + .get(arg) + .and_then(|ma| Some(ma.contains_val(v))) + .unwrap_or(false) + { + Some(req_arg) + } else { + None + } + } else { + Some(req_arg) + } + }; + + let mut r_vec = vec![arg]; + let mut args = vec![]; + + while let Some(a) = r_vec.pop() { + if let Some(arg) = self.find(a) { + if let Some(ref reqs) = arg.requires { + for r in reqs.iter().filter_map(requires_if_or_not) { + if let Some(req) = self.find(r) { + if req.requires.is_some() { + r_vec.push(req.id) + } + } + args.push(r); + } + } + } + } + + args } } #[cfg(feature = "yaml")] -impl<'a> From<&'a Yaml> for App<'a, 'a> { +impl<'a> From<&'a Yaml> for App<'a> { fn from(mut yaml: &'a Yaml) -> Self { - use args::SubCommand; // We WANT this to panic on error...so expect() is good. let mut is_sc = None; let mut a = if let Some(name) = yaml["name"].as_str() { @@ -1658,26 +1897,20 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> { if let Some(v) = $y[stringify!($i)].as_str() { $a = $a.$i(v); } else if $y[stringify!($i)] != Yaml::BadValue { - panic!("Failed to convert YAML value {:?} to a string", $y[stringify!($i)]); + panic!( + "Failed to convert YAML value {:?} to a string", + $y[stringify!($i)] + ); } }; } yaml_str!(a, yaml, version); - yaml_str!(a, yaml, long_version); yaml_str!(a, yaml, author); yaml_str!(a, yaml, bin_name); yaml_str!(a, yaml, about); - yaml_str!(a, yaml, long_about); yaml_str!(a, yaml, before_help); yaml_str!(a, yaml, after_help); - yaml_str!(a, yaml, template); - yaml_str!(a, yaml, usage); - yaml_str!(a, yaml, help); - yaml_str!(a, yaml, help_short); - yaml_str!(a, yaml, version_short); - yaml_str!(a, yaml, help_message); - yaml_str!(a, yaml, version_message); yaml_str!(a, yaml, alias); yaml_str!(a, yaml, visible_alias); @@ -1736,25 +1969,27 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> { macro_rules! vec_or_str { ($a:ident, $y:ident, $as_vec:ident, $as_single:ident) => {{ - let maybe_vec = $y[stringify!($as_vec)].as_vec(); - if let Some(vec) = maybe_vec { - for ys in vec { - if let Some(s) = ys.as_str() { - $a = $a.$as_single(s); - } else { - panic!("Failed to convert YAML value {:?} to a string", ys); - } - } - } else { - if let Some(s) = $y[stringify!($as_vec)].as_str() { + let maybe_vec = $y[stringify!($as_vec)].as_vec(); + if let Some(vec) = maybe_vec { + for ys in vec { + if let Some(s) = ys.as_str() { $a = $a.$as_single(s); - } else if $y[stringify!($as_vec)] != Yaml::BadValue { - panic!("Failed to convert YAML value {:?} to either a vec or string", $y[stringify!($as_vec)]); + } else { + panic!("Failed to convert YAML value {:?} to a string", ys); } } - $a + } else { + if let Some(s) = $y[stringify!($as_vec)].as_str() { + $a = $a.$as_single(s); + } else if $y[stringify!($as_vec)] != Yaml::BadValue { + panic!( + "Failed to convert YAML value {:?} to either a vec or string", + $y[stringify!($as_vec)] + ); + } } - }; + $a + }}; } a = vec_or_str!(a, yaml, aliases, alias); @@ -1767,7 +2002,7 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> { } if let Some(v) = yaml["subcommands"].as_vec() { for sc_yaml in v { - a = a.subcommand(SubCommand::from_yaml(sc_yaml)); + a = a.subcommand(App::from(sc_yaml)); } } if let Some(v) = yaml["groups"].as_vec() { @@ -1780,60 +2015,6 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> { } } -impl<'a, 'b> Clone for App<'a, 'b> { - fn clone(&self) -> Self { App { p: self.p.clone() } } -} - -impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> { - fn name(&self) -> &'n str { - "" - } - fn overrides(&self) -> Option<&[&'e str]> { None } - fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> { None } - fn blacklist(&self) -> Option<&[&'e str]> { None } - fn required_unless(&self) -> Option<&[&'e str]> { None } - fn val_names(&self) -> Option<&VecMap<&'e str>> { None } - fn is_set(&self, _: ArgSettings) -> bool { false } - fn val_terminator(&self) -> Option<&'e str> { None } - fn set(&mut self, _: ArgSettings) { - unreachable!("App struct does not support AnyArg::set, this is a bug!") - } - fn has_switch(&self) -> bool { false } - fn max_vals(&self) -> Option { None } - fn num_vals(&self) -> Option { None } - fn possible_vals(&self) -> Option<&[&'e str]> { None } - fn validator(&self) -> Option<&Rc StdResult<(), String>>> { None } - fn validator_os(&self) -> Option<&Rc StdResult<(), OsString>>> { None } - fn min_vals(&self) -> Option { None } - fn short(&self) -> Option { None } - fn long(&self) -> Option<&'e str> { None } - fn val_delim(&self) -> Option { None } - fn takes_value(&self) -> bool { true } - fn help(&self) -> Option<&'e str> { self.p.meta.about } - fn long_help(&self) -> Option<&'e str> { self.p.meta.long_about } - fn default_val(&self) -> Option<&'e OsStr> { None } - fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { - None - } - fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> { None } - fn longest_filter(&self) -> bool { true } - fn aliases(&self) -> Option> { - if let Some(ref aliases) = self.p.meta.aliases { - let vis_aliases: Vec<_> = aliases - .iter() - .filter_map(|&(n, v)| if v { Some(n) } else { None }) - .collect(); - if vis_aliases.is_empty() { - None - } else { - Some(vis_aliases) - } - } else { - None - } - } -} - -impl<'n, 'e> fmt::Display for App<'n, 'e> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.p.meta.name) } +impl<'e> fmt::Display for App<'e> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) } } diff --git a/src/app/settings.rs b/src/build/app/settings.rs similarity index 77% rename from src/app/settings.rs rename to src/build/app/settings.rs index ec0399722bd0..cec618c1aa9c 100644 --- a/src/app/settings.rs +++ b/src/build/app/settings.rs @@ -1,8 +1,7 @@ // Std -#[allow(deprecated, unused_imports)] -use std::ascii::AsciiExt; -use std::str::FromStr; +#[allow(unused_imports)] use std::ops::BitOr; +use std::str::FromStr; bitflags! { struct Flags: u64 { @@ -14,40 +13,38 @@ bitflags! { const UNIFIED_HELP = 1 << 5; const WAIT_ON_ERROR = 1 << 6; const SC_REQUIRED_ELSE_HELP= 1 << 7; - const NEEDS_LONG_HELP = 1 << 8; - const NEEDS_LONG_VERSION = 1 << 9; - const NEEDS_SC_HELP = 1 << 10; - const DISABLE_VERSION = 1 << 11; - const HIDDEN = 1 << 12; - const TRAILING_VARARG = 1 << 13; - const NO_BIN_NAME = 1 << 14; - const ALLOW_UNK_SC = 1 << 15; - const UTF8_STRICT = 1 << 16; - const UTF8_NONE = 1 << 17; - const LEADING_HYPHEN = 1 << 18; - const NO_POS_VALUES = 1 << 19; - const NEXT_LINE_HELP = 1 << 20; - const DERIVE_DISP_ORDER = 1 << 21; - const COLORED_HELP = 1 << 22; - const COLOR_ALWAYS = 1 << 23; - const COLOR_AUTO = 1 << 24; - const COLOR_NEVER = 1 << 25; - const DONT_DELIM_TRAIL = 1 << 26; - const ALLOW_NEG_NUMS = 1 << 27; - const LOW_INDEX_MUL_POS = 1 << 28; - const DISABLE_HELP_SC = 1 << 29; - const DONT_COLLAPSE_ARGS = 1 << 30; - const ARGS_NEGATE_SCS = 1 << 31; - const PROPAGATE_VALS_DOWN = 1 << 32; - const ALLOW_MISSING_POS = 1 << 33; - const TRAILING_VALUES = 1 << 34; - const VALID_NEG_NUM_FOUND = 1 << 35; - const PROPAGATED = 1 << 36; - const VALID_ARG_FOUND = 1 << 37; - const INFER_SUBCOMMANDS = 1 << 38; - const CONTAINS_LAST = 1 << 39; - const ARGS_OVERRIDE_SELF = 1 << 40; - const DISABLE_HELP_FLAGS = 1 << 41; + const NO_AUTO_HELP = 1 << 8; + const NO_AUTO_VERSION = 1 << 9; + const DISABLE_VERSION = 1 << 10; + const HIDDEN = 1 << 11; + const TRAILING_VARARG = 1 << 12; + const NO_BIN_NAME = 1 << 13; + const ALLOW_UNK_SC = 1 << 14; + const UTF8_STRICT = 1 << 15; + const UTF8_NONE = 1 << 16; + const LEADING_HYPHEN = 1 << 17; + const NO_POS_VALUES = 1 << 18; + const NEXT_LINE_HELP = 1 << 19; + const DERIVE_DISP_ORDER = 1 << 20; + const COLORED_HELP = 1 << 21; + const COLOR_ALWAYS = 1 << 22; + const COLOR_AUTO = 1 << 23; + const COLOR_NEVER = 1 << 24; + const DONT_DELIM_TRAIL = 1 << 25; + const ALLOW_NEG_NUMS = 1 << 26; + const LOW_INDEX_MUL_POS = 1 << 27; + const DISABLE_HELP_SC = 1 << 28; + const DONT_COLLAPSE_ARGS = 1 << 29; + const ARGS_NEGATE_SCS = 1 << 30; + const PROPAGATE_VALS_DOWN = 1 << 31; + const ALLOW_MISSING_POS = 1 << 32; + const TRAILING_VALUES = 1 << 33; + const VALID_NEG_NUM_FOUND = 1 << 34; + const BUILT = 1 << 35; + const VALID_ARG_FOUND = 1 << 36; + const INFER_SUBCOMMANDS = 1 << 37; + const CONTAINS_LAST = 1 << 38; + const ARGS_OVERRIDE_SELF = 1 << 39; } } @@ -61,15 +58,9 @@ impl BitOr for AppFlags { } impl Default for AppFlags { - fn default() -> Self { - AppFlags( - Flags::NEEDS_LONG_VERSION | Flags::NEEDS_LONG_HELP | Flags::NEEDS_SC_HELP - | Flags::UTF8_NONE | Flags::COLOR_AUTO, - ) - } + fn default() -> Self { AppFlags(Flags::UTF8_NONE | Flags::COLOR_AUTO) } } -#[allow(deprecated)] impl AppFlags { pub fn new() -> Self { AppFlags::default() } pub fn zeroed() -> Self { AppFlags(Flags::empty()) } @@ -77,7 +68,6 @@ impl AppFlags { impl_settings! { AppSettings, ArgRequiredElseHelp => Flags::A_REQUIRED_ELSE_HELP, ArgsNegateSubcommands => Flags::ARGS_NEGATE_SCS, - AllArgsOverrideSelf => Flags::ARGS_OVERRIDE_SELF, AllowExternalSubcommands => Flags::ALLOW_UNK_SC, AllowInvalidUtf8 => Flags::UTF8_NONE, AllowLeadingHyphen => Flags::LEADING_HYPHEN, @@ -90,18 +80,15 @@ impl AppFlags { DontDelimitTrailingValues => Flags::DONT_DELIM_TRAIL, DontCollapseArgsInUsage => Flags::DONT_COLLAPSE_ARGS, DeriveDisplayOrder => Flags::DERIVE_DISP_ORDER, - DisableHelpFlags => Flags::DISABLE_HELP_FLAGS, DisableHelpSubcommand => Flags::DISABLE_HELP_SC, DisableVersion => Flags::DISABLE_VERSION, GlobalVersion => Flags::GLOBAL_VERSION, HidePossibleValuesInHelp => Flags::NO_POS_VALUES, Hidden => Flags::HIDDEN, LowIndexMultiplePositional => Flags::LOW_INDEX_MUL_POS, - NeedsLongHelp => Flags::NEEDS_LONG_HELP, - NeedsLongVersion => Flags::NEEDS_LONG_VERSION, - NeedsSubcommandHelp => Flags::NEEDS_SC_HELP, + NoAutoHelp => Flags::NO_AUTO_HELP, + NoAutoVersion => Flags::NO_AUTO_VERSION, NoBinaryName => Flags::NO_BIN_NAME, - PropagateGlobalValuesDown=> Flags::PROPAGATE_VALS_DOWN, StrictUtf8 => Flags::UTF8_STRICT, SubcommandsNegateReqs => Flags::SC_NEGATE_REQS, SubcommandRequired => Flags::SC_REQUIRED, @@ -113,9 +100,10 @@ impl AppFlags { WaitOnError => Flags::WAIT_ON_ERROR, TrailingValues => Flags::TRAILING_VALUES, ValidNegNumFound => Flags::VALID_NEG_NUM_FOUND, - Propagated => Flags::PROPAGATED, + Built => Flags::BUILT, ValidArgFound => Flags::VALID_ARG_FOUND, InferSubcommands => Flags::INFER_SUBCOMMANDS, + AllArgsOverrideSelf => Flags::ARGS_OVERRIDE_SELF, ContainsLast => Flags::CONTAINS_LAST } } @@ -137,7 +125,7 @@ pub enum AppSettings { /// UTF-8 values /// /// **NOTE:** This rule only applies to argument values, as flags, options, and - /// [`SubCommand`]s themselves only allow valid UTF-8 code points. + /// [``]s themselves only allow valid UTF-8 code points. /// /// # Platform Specific /// @@ -153,8 +141,8 @@ pub enum AppSettings { /// /// let r = App::new("myprog") /// //.setting(AppSettings::AllowInvalidUtf8) - /// .arg_from_usage(" 'some positional arg'") - /// .get_matches_from_safe( + /// .arg(" 'some positional arg'") + /// .try_get_matches_from( /// vec![ /// OsString::from("myprog"), /// OsString::from_vec(vec![0xe9])]); @@ -167,16 +155,9 @@ pub enum AppSettings { /// [`ArgMatches::os_values_of`]: ./struct.ArgMatches.html#method.os_values_of /// [`ArgMatches::lossy_value_of`]: ./struct.ArgMatches.html#method.lossy_value_of /// [`ArgMatches::lossy_values_of`]: ./struct.ArgMatches.html#method.lossy_values_of - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html AllowInvalidUtf8, - /// Essentially sets [`Arg::overrides_with("itself")`] for all arguments. - /// - /// **WARNING:** Positional arguments cannot override themselves (or we would never be able - /// to advance to the next positional). This setting ignores positional arguments. - /// [`Arg::overrides_with("itself")`]: ./struct.Arg.html#method.overrides_with - AllArgsOverrideSelf, - /// Specifies that leading hyphens are allowed in argument *values*, such as negative numbers /// like `-10`. (which would otherwise be parsed as another flag or option) /// @@ -202,6 +183,11 @@ pub enum AppSettings { /// [`Arg::allow_hyphen_values`]: ./struct.Arg.html#method.allow_hyphen_values AllowLeadingHyphen, + /// Specifies that all arguments override themselves. This is the equivolent to saying the `foo` + /// arg using [`Arg::overrides_with("foo")`] for all defined arguments. + /// [`Arg::overrides_with("foo")`]: ./struct.Arg.html#method.overrides_with + AllArgsOverrideSelf, + /// Allows negative numbers to pass as values. This is similar to /// `AllowLeadingHyphen` except that it only allows numbers, all /// other undefined leading hyphens will fail to parse. @@ -214,7 +200,7 @@ pub enum AppSettings { /// .version("v1.1") /// .setting(AppSettings::AllowNegativeNumbers) /// .arg(Arg::with_name("num")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "myprog", "-20" /// ]); /// assert!(res.is_ok()); @@ -247,7 +233,7 @@ pub enum AppSettings { /// For example, imagine a CLI which has three positional arguments `[foo] [bar] [baz]...` where /// `baz` accepts multiple values (similar to man `ARGS...` style training arguments). /// - /// With this setting the following invocations are possible: + /// With this setting the following invocations are posisble: /// /// * `$ prog foo bar baz1 baz2 baz3` /// * `$ prog foo -- baz1 baz2 baz3` @@ -310,7 +296,7 @@ pub enum AppSettings { /// assert_eq!(m.values_of("baz").unwrap().collect::>(), &["baz1", "baz2", "baz3"]); /// ``` /// - /// Now notice if we don't specify `foo` or `baz` but use the `--` operator. + /// Now nofice if we don't specifiy `foo` or `baz` but use the `--` operator. /// /// ```rust /// # use clap::{App, Arg, AppSettings}; @@ -333,7 +319,7 @@ pub enum AppSettings { /// Specifies that an unexpected positional argument, /// which would otherwise cause a [`ErrorKind::UnknownArgument`] error, - /// should instead be treated as a [`SubCommand`] within the [`ArgMatches`] struct. + /// should instead be treated as a [``] within the [`ArgMatches`] struct. /// /// **NOTE:** Use this setting with caution, /// as a truly unexpected argument (i.e. one that is *NOT* an external subcommand) @@ -363,11 +349,11 @@ pub enum AppSettings { /// } /// ``` /// [`ErrorKind::UnknownArgument`]: ./enum.ErrorKind.html#variant.UnknownArgument - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`ArgMatches`]: ./struct.ArgMatches.html AllowExternalSubcommands, - /// Specifies that use of a valid [argument] negates [subcommands] being used after. By default + /// Specifies that use of a valid [argument] negates [subcomands] being used after. By default /// `clap` allows arguments between subcommands such as /// ` [cmd_args] [cmd2_args] [cmd3_args]`. This setting disables that /// functionality and says that arguments can only follow the *final* subcommand. For instance @@ -385,14 +371,14 @@ pub enum AppSettings { /// .setting(AppSettings::ArgsNegateSubcommands) /// # ; /// ``` - /// [subcommands]: ./struct.SubCommand.html + /// [subcommands]: ./struct..html /// [argument]: ./struct.Arg.html ArgsNegateSubcommands, /// Specifies that the help text should be displayed (and then exit gracefully), /// if no arguments are present at runtime (i.e. an empty run such as, `$ myprog`. /// - /// **NOTE:** [`SubCommand`]s count as arguments + /// **NOTE:** [``]s count as arguments /// /// **NOTE:** Setting [`Arg::default_value`] effectively disables this option as it will /// ensure that some argument is always present. @@ -405,7 +391,7 @@ pub enum AppSettings { /// .setting(AppSettings::ArgRequiredElseHelp) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value ArgRequiredElseHelp, @@ -415,12 +401,12 @@ pub enum AppSettings { /// /// # Platform Specific /// - /// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms) + /// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms) /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::ColoredHelp) /// .get_matches(); @@ -435,12 +421,12 @@ pub enum AppSettings { /// /// # Platform Specific /// - /// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms). + /// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms). /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::ColorAuto) /// .get_matches(); @@ -453,12 +439,12 @@ pub enum AppSettings { /// /// # Platform Specific /// - /// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms). + /// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms). /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::ColorAlways) /// .get_matches(); @@ -471,12 +457,12 @@ pub enum AppSettings { /// /// # Platform Specific /// - /// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms) + /// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms) /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::ColorNever) /// .get_matches(); @@ -488,7 +474,7 @@ pub enum AppSettings { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::DontCollapseArgsInUsage) /// .get_matches(); @@ -505,7 +491,7 @@ pub enum AppSettings { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::DontDelimitTrailingValues) /// .get_matches(); @@ -514,59 +500,28 @@ pub enum AppSettings { /// [`Arg::use_delimiter(false)`]: ./struct.Arg.html#method.use_delimiter DontDelimitTrailingValues, - /// Disables `-h` and `--help` [`App`] without affecting any of the [`SubCommand`]s - /// (Defaults to `false`; application *does* have help flags) - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind}; - /// let res = App::new("myprog") - /// .setting(AppSettings::DisableHelpFlags) - /// .get_matches_from_safe(vec![ - /// "myprog", "-h" - /// ]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - /// - /// ```rust - /// # use clap::{App, SubCommand, AppSettings, ErrorKind}; - /// let res = App::new("myprog") - /// .setting(AppSettings::DisableHelpFlags) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ - /// "myprog", "test", "-h" - /// ]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::HelpDisplayed); - /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html - /// [`App`]: ./struct.App.html - DisableHelpFlags, - /// Disables the `help` subcommand /// /// # Examples /// /// ```rust - /// # use clap::{App, AppSettings, ErrorKind, SubCommand}; + /// # use clap::{App, AppSettings, ErrorKind, }; /// let res = App::new("myprog") /// .version("v1.1") /// .setting(AppSettings::DisableHelpSubcommand) - /// // Normally, creating a subcommand causes a `help` subcommand to automatically + /// // Normally, creating a subcommand causes a `help` subcommand to automaticaly /// // be generated as well - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ + /// .subcommand(App::new("test")) + /// .try_get_matches_from(vec![ /// "myprog", "help" /// ]); /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html DisableHelpSubcommand, - /// Disables `-V` and `--version` [`App`] without affecting any of the [`SubCommand`]s + /// Disables `-V` and `--version` [`App`] without affecting any of the [``]s /// (Defaults to `false`; application *does* have a version flag) /// /// # Examples @@ -576,7 +531,7 @@ pub enum AppSettings { /// let res = App::new("myprog") /// .version("v1.1") /// .setting(AppSettings::DisableVersion) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "myprog", "-V" /// ]); /// assert!(res.is_err()); @@ -584,36 +539,36 @@ pub enum AppSettings { /// ``` /// /// ```rust - /// # use clap::{App, SubCommand, AppSettings, ErrorKind}; + /// # use clap::{App, AppSettings, ErrorKind}; /// let res = App::new("myprog") /// .version("v1.1") /// .setting(AppSettings::DisableVersion) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ + /// .subcommand(App::new("test")) + /// .try_get_matches_from(vec![ /// "myprog", "test", "-V" /// ]); /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::VersionDisplayed); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`App`]: ./struct.App.html DisableVersion, - /// Displays the arguments and [`SubCommand`]s in the help message in the order that they were + /// Displays the arguments and [``]s in the help message in the order that they were /// declared in, and not alphabetically which is the default. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::DeriveDisplayOrder) /// .get_matches(); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html DeriveDisplayOrder, - /// Specifies to use the version of the current command for all child [`SubCommand`]s. + /// Specifies to use the version of the current command for all child [``]s. /// (Defaults to `false`; subcommands have independent version strings from their parents.) /// /// **NOTE:** The version for the current command **and** this setting must be set **prior** to @@ -622,30 +577,30 @@ pub enum AppSettings { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .version("v1.1") /// .setting(AppSettings::GlobalVersion) - /// .subcommand(SubCommand::with_name("test")) + /// .subcommand(App::new("test")) /// .get_matches(); /// // running `$ myprog test --version` will display /// // "myprog-test v1.1" /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html GlobalVersion, - /// Specifies that this [`SubCommand`] should be hidden from help messages + /// Specifies that this [``] should be hidden from help messages /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, AppSettings, SubCommand}; + /// # use clap::{App, Arg, AppSettings, }; /// App::new("myprog") - /// .subcommand(SubCommand::with_name("test") + /// .subcommand(App::new("test") /// .setting(AppSettings::Hidden)) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html Hidden, /// Tells `clap` *not* to print possible values when displaying help information. @@ -667,16 +622,16 @@ pub enum AppSettings { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// let m = App::new("prog") /// .setting(AppSettings::InferSubcommands) - /// .subcommand(SubCommand::with_name("test")) + /// .subcommand(App::new("test")) /// .get_matches_from(vec![ /// "prog", "te" /// ]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`subcommands`]: ./struct.SubCommand.html + /// [`subcommands`]: ./struct..html /// [positional/free arguments]: ./struct.Arg.html#method.index /// [aliases]: ./struct.App.html#method.alias /// [`AppSeettings::ArgsNegateSubcommands`]: ./enum.AppSettings.html#variant.ArgsNegateSubcommands @@ -692,7 +647,7 @@ pub enum AppSettings { /// # use clap::{App, Arg, AppSettings}; /// let m = App::new("myprog") /// .setting(AppSettings::NoBinaryName) - /// .arg(Arg::from_usage("... 'commands to run'")) + /// .arg(Arg::from("... 'commands to run'")) /// .get_matches_from(vec!["command", "set"]); /// /// let cmds: Vec<&str> = m.values_of("cmd").unwrap().collect(); @@ -705,53 +660,14 @@ pub enum AppSettings { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::NextLineHelp) /// .get_matches(); /// ``` NextLineHelp, - /// **DEPRECATED**: This setting is no longer required in order to propagate values up or down - /// - /// Specifies that the parser should propagate global arg's values down or up through any *used* - /// child subcommands. Meaning, if a subcommand wasn't used, the values won't be propagated to - /// said subcommand. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings, SubCommand}; - /// let m = App::new("myprog") - /// .arg(Arg::from_usage("[cmd] 'command to run'") - /// .global(true)) - /// .subcommand(SubCommand::with_name("foo")) - /// .get_matches_from(vec!["myprog", "set", "foo"]); - /// - /// assert_eq!(m.value_of("cmd"), Some("set")); - /// - /// let sub_m = m.subcommand_matches("foo").unwrap(); - /// assert_eq!(sub_m.value_of("cmd"), Some("set")); - /// ``` - /// Now doing the same thing, but *not* using any subcommands will result in the value not being - /// propagated down. - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings, SubCommand}; - /// let m = App::new("myprog") - /// .arg(Arg::from_usage("[cmd] 'command to run'") - /// .global(true)) - /// .subcommand(SubCommand::with_name("foo")) - /// .get_matches_from(vec!["myprog", "set"]); - /// - /// assert_eq!(m.value_of("cmd"), Some("set")); - /// - /// assert!(m.subcommand_matches("foo").is_none()); - /// ``` - #[deprecated(since = "2.27.0", note = "No longer required to propagate values")] - PropagateGlobalValuesDown, - - /// Allows [`SubCommand`]s to override all requirements of the parent command. + /// Allows [``]s to override all requirements of the parent command. /// For example if you had a subcommand or top level application with a required argument /// that is only required as long as there is no subcommand present, /// using this setting would allow you to set those arguments to [`Arg::required(true)`] @@ -764,12 +680,12 @@ pub enum AppSettings { /// This first example shows that it is an error to not use a required argument /// /// ```rust - /// # use clap::{App, Arg, AppSettings, SubCommand, ErrorKind}; + /// # use clap::{App, Arg, AppSettings, ErrorKind}; /// let err = App::new("myprog") /// .setting(AppSettings::SubcommandsNegateReqs) /// .arg(Arg::with_name("opt").required(true)) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ + /// .subcommand(App::new("test")) + /// .try_get_matches_from(vec![ /// "myprog" /// ]); /// assert!(err.is_err()); @@ -781,23 +697,23 @@ pub enum AppSettings { /// valid subcommand is used. /// /// ```rust - /// # use clap::{App, Arg, AppSettings, SubCommand, ErrorKind}; + /// # use clap::{App, Arg, AppSettings, ErrorKind}; /// let noerr = App::new("myprog") /// .setting(AppSettings::SubcommandsNegateReqs) /// .arg(Arg::with_name("opt").required(true)) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ + /// .subcommand(App::new("test")) + /// .try_get_matches_from(vec![ /// "myprog", "test" /// ]); /// assert!(noerr.is_ok()); /// # ; /// ``` /// [`Arg::required(true)`]: ./struct.Arg.html#method.required - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html SubcommandsNegateReqs, /// Specifies that the help text should be displayed (before exiting gracefully) if no - /// [`SubCommand`]s are present at runtime (i.e. an empty run such as `$ myprog`). + /// [``]s are present at runtime (i.e. an empty run such as `$ myprog`). /// /// **NOTE:** This should *not* be used with [`AppSettings::SubcommandRequired`] as they do /// nearly same thing; this prints the help text, and the other prints an error. @@ -814,7 +730,7 @@ pub enum AppSettings { /// .setting(AppSettings::SubcommandRequiredElseHelp) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`AppSettings::SubcommandRequired`]: ./enum.AppSettings.html#variant.SubcommandRequired /// [`AppSettings::ArgRequiredElseHelp`]: ./enum.AppSettings.html#variant.ArgRequiredElseHelp SubcommandRequiredElseHelp, @@ -823,7 +739,7 @@ pub enum AppSettings { /// with a [`ErrorKind::InvalidUtf8`] error. /// /// **NOTE:** This rule only applies to argument values; Things such as flags, options, and - /// [`SubCommand`]s themselves only allow valid UTF-8 code points. + /// [``]s themselves only allow valid UTF-8 code points. /// /// # Platform Specific /// @@ -839,8 +755,8 @@ pub enum AppSettings { /// /// let m = App::new("myprog") /// .setting(AppSettings::StrictUtf8) - /// .arg_from_usage(" 'some positional arg'") - /// .get_matches_from_safe( + /// .arg(" 'some positional arg'") + /// .try_get_matches_from( /// vec![ /// OsString::from("myprog"), /// OsString::from_vec(vec![0xe9])]); @@ -848,11 +764,11 @@ pub enum AppSettings { /// assert!(m.is_err()); /// assert_eq!(m.unwrap_err().kind, ErrorKind::InvalidUtf8); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`ErrorKind::InvalidUtf8`]: ./enum.ErrorKind.html#variant.InvalidUtf8 StrictUtf8, - /// Allows specifying that if no [`SubCommand`] is present at runtime, + /// Allows specifying that if no [``] is present at runtime, /// error and exit gracefully. /// /// **NOTE:** This defaults to `false` (subcommands do *not* need to be present) @@ -860,18 +776,18 @@ pub enum AppSettings { /// # Examples /// /// ```rust - /// # use clap::{App, AppSettings, SubCommand, ErrorKind}; + /// # use clap::{App, AppSettings, ErrorKind}; /// let err = App::new("myprog") /// .setting(AppSettings::SubcommandRequired) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ + /// .subcommand(App::new("test")) + /// .try_get_matches_from(vec![ /// "myprog", /// ]); /// assert!(err.is_err()); /// assert_eq!(err.unwrap_err().kind, ErrorKind::MissingSubcommand); /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html SubcommandRequired, /// Specifies that the final positional argument is a "VarArg" and that `clap` should not @@ -888,7 +804,7 @@ pub enum AppSettings { /// # use clap::{App, Arg, AppSettings}; /// let m = App::new("myprog") /// .setting(AppSettings::TrailingVarArg) - /// .arg(Arg::from_usage("... 'commands to run'")) + /// .arg(Arg::from("... 'commands to run'")) /// .get_matches_from(vec!["myprog", "arg1", "-r", "val1"]); /// /// let trail: Vec<&str> = m.values_of("cmd").unwrap().collect(); @@ -908,7 +824,7 @@ pub enum AppSettings { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// # use clap::{App, Arg, AppSettings}; /// App::new("myprog") /// .setting(AppSettings::UnifiedHelpMessage) /// .get_matches(); @@ -916,26 +832,26 @@ pub enum AppSettings { /// ``` UnifiedHelpMessage, - /// Disables `-V` and `--version` for all [`SubCommand`]s + /// Disables `-V` and `--version` for all [``]s /// (Defaults to `false`; subcommands *do* have version flags.) /// - /// **NOTE:** This setting must be set **prior** to adding any subcommands. + /// **NOTE:** This setting must be set **prior** adding any subcommands /// /// # Examples /// /// ```rust - /// # use clap::{App, SubCommand, AppSettings, ErrorKind}; + /// # use clap::{App, AppSettings, ErrorKind}; /// let res = App::new("myprog") /// .version("v1.1") /// .setting(AppSettings::VersionlessSubcommands) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches_from_safe(vec![ + /// .subcommand(App::new("test")) + /// .try_get_matches_from(vec![ /// "myprog", "test", "-V" /// ]); /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html VersionlessSubcommands, /// Will display a message "Press \[ENTER\]/\[RETURN\] to continue..." and wait for user before @@ -945,7 +861,7 @@ pub enum AppSettings { /// Windows where a user tries to open the binary by double-clicking instead of using the /// command line. /// - /// **NOTE:** This setting is **not** recursive with [`SubCommand`]s, meaning if you wish this + /// **NOTE:** This setting is **not** recursive with [``]s, meaning if you wish this /// behavior for all subcommands, you must set this on each command (needing this is extremely /// rare) /// @@ -957,33 +873,38 @@ pub enum AppSettings { /// .setting(AppSettings::WaitOnError) /// # ; /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html WaitOnError, - #[doc(hidden)] NeedsLongVersion, + /// @TODO-v3: @docs write them...maybe rename + NoAutoHelp, - #[doc(hidden)] NeedsLongHelp, + /// @TODO-v3: @docs write them...maybe rename + NoAutoVersion, - #[doc(hidden)] NeedsSubcommandHelp, + #[doc(hidden)] + LowIndexMultiplePositional, - #[doc(hidden)] LowIndexMultiplePositional, + #[doc(hidden)] + TrailingValues, - #[doc(hidden)] TrailingValues, + #[doc(hidden)] + ValidNegNumFound, - #[doc(hidden)] ValidNegNumFound, + #[doc(hidden)] + Built, - #[doc(hidden)] Propagated, + #[doc(hidden)] + ValidArgFound, - #[doc(hidden)] ValidArgFound, - - #[doc(hidden)] ContainsLast, + #[doc(hidden)] + ContainsLast, } impl FromStr for AppSettings { type Err = String; fn from_str(s: &str) -> Result::Err> { match &*s.to_ascii_lowercase() { - "disablehelpflags" => Ok(AppSettings::DisableHelpFlags), "argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp), "argsnegatesubcommands" => Ok(AppSettings::ArgsNegateSubcommands), "allowinvalidutf8" => Ok(AppSettings::AllowInvalidUtf8), @@ -1016,7 +937,7 @@ impl FromStr for AppSettings { "waitonerror" => Ok(AppSettings::WaitOnError), "validnegnumfound" => Ok(AppSettings::ValidNegNumFound), "validargfound" => Ok(AppSettings::ValidArgFound), - "propagated" => Ok(AppSettings::Propagated), + "built" => Ok(AppSettings::Built), "trailingvalues" => Ok(AppSettings::TrailingValues), _ => Err("unknown AppSetting, cannot convert from str".to_owned()), } @@ -1029,10 +950,6 @@ mod test { #[test] fn app_settings_fromstr() { - assert_eq!( - "disablehelpflags".parse::().unwrap(), - AppSettings::DisableHelpFlags - ); assert_eq!( "argsnegatesubcommands".parse::().unwrap(), AppSettings::ArgsNegateSubcommands @@ -1158,8 +1075,8 @@ mod test { AppSettings::ValidArgFound ); assert_eq!( - "propagated".parse::().unwrap(), - AppSettings::Propagated + "built".parse::().unwrap(), + AppSettings::Built ); assert_eq!( "trailingvalues".parse::().unwrap(), diff --git a/src/args/arg.rs b/src/build/arg/mod.rs similarity index 71% rename from src/args/arg.rs rename to src/build/arg/mod.rs index 50a30abb7ac5..690cccb4a879 100644 --- a/src/args/arg.rs +++ b/src/build/arg/mod.rs @@ -1,20 +1,33 @@ -#[cfg(feature = "yaml")] -use std::collections::BTreeMap; -use std::rc::Rc; +mod settings; +pub use self::settings::{ArgFlags, ArgSettings}; + +// Std +use std::borrow::Cow; +use std::cmp::{Ord, Ordering}; +use std::env; use std::ffi::{OsStr, OsString}; -#[cfg(any(target_os = "windows", target_arch = "wasm32"))] -use osstringext::OsStrExt3; +use std::fmt::{self, Display, Formatter}; #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] use std::os::unix::ffi::OsStrExt; -use std::env; +use std::rc::Rc; +use std::str; +// Third Party +use crate::util::VecMap; #[cfg(feature = "yaml")] -use yaml_rust::Yaml; -use map::VecMap; +use yaml_rust; + +// Internal +use crate::build::UsageParser; +use crate::util::Key; +#[cfg(any(target_os = "windows", target_arch = "wasm32"))] +use crate::util::OsStrExt3; +use crate::INTERNAL_ERROR_MSG; + +type Validator = Rc Result<(), String>>; +type ValidatorOs = Rc Result<(), String>>; -use usage_parser::UsageParser; -use args::settings::ArgSettings; -use args::arg_builder::{Base, Switched, Valued}; +type Id = u64; /// The abstract representation of a command line argument. Used to set all the options and /// relationships that define a valid argument for the program. @@ -29,29 +42,92 @@ use args::arg_builder::{Base, Switched, Valued}; /// # use clap::Arg; /// // Using the traditional builder pattern and setting each option manually /// let cfg = Arg::with_name("config") -/// .short("c") +/// .short('c') /// .long("config") /// .takes_value(true) /// .value_name("FILE") /// .help("Provides a config file to myprog"); /// // Using a usage string (setting a similar argument to the one above) -/// let input = Arg::from_usage("-i, --input=[FILE] 'Provides an input file to the program'"); +/// let input = Arg::from("-i, --input=[FILE] 'Provides an input file to the program'"); /// ``` /// [`Arg`]: ./struct.Arg.html #[allow(missing_debug_implementations)] #[derive(Default, Clone)] -pub struct Arg<'a, 'b> -where - 'a: 'b, -{ - #[doc(hidden)] pub b: Base<'a, 'b>, - #[doc(hidden)] pub s: Switched<'b>, - #[doc(hidden)] pub v: Valued<'a, 'b>, - #[doc(hidden)] pub index: Option, - #[doc(hidden)] pub r_ifs: Option>, +pub struct Arg<'help> { + #[doc(hidden)] + pub id: Id, + #[doc(hidden)] + pub name: &'help str, + #[doc(hidden)] + pub help: Option<&'help str>, + #[doc(hidden)] + pub long_help: Option<&'help str>, + #[doc(hidden)] + pub blacklist: Option>, + #[doc(hidden)] + pub settings: ArgFlags, + #[doc(hidden)] + pub r_unless: Option>, + #[doc(hidden)] + pub overrides: Option>, + #[doc(hidden)] + pub groups: Option>, + #[doc(hidden)] + pub requires: Option, Id)>>, + #[doc(hidden)] + pub short: Option, + #[doc(hidden)] + pub long: Option<&'help str>, + #[doc(hidden)] + pub aliases: Option>, // (name, visible) + #[doc(hidden)] + pub disp_ord: usize, + #[doc(hidden)] + pub unified_ord: usize, + #[doc(hidden)] + pub possible_vals: Option>, + #[doc(hidden)] + pub val_names: Option>, + #[doc(hidden)] + pub num_vals: Option, + #[doc(hidden)] + pub max_vals: Option, + #[doc(hidden)] + pub min_vals: Option, + #[doc(hidden)] + pub validator: Option, + #[doc(hidden)] + pub validator_os: Option, + #[doc(hidden)] + pub val_delim: Option, + #[doc(hidden)] + pub default_val: Option<&'help OsStr>, + #[doc(hidden)] + pub default_vals_ifs: Option, &'help OsStr)>>, + #[doc(hidden)] + pub env: Option<(&'help OsStr, Option)>, + #[doc(hidden)] + pub terminator: Option<&'help str>, + #[doc(hidden)] + pub index: Option, + #[doc(hidden)] + pub r_ifs: Option>, + #[doc(hidden)] + pub help_heading: Option<&'help str>, + #[doc(hidden)] + pub global: bool, } -impl<'a, 'b> Arg<'a, 'b> { +impl<'help> Arg<'help> { + /// @TODO @p2 @docs @v3-beta1: Write Docs + pub fn new(t: T) -> Self { + Arg { + id: t.key(), + disp_ord: 999, + unified_ord: 999, + ..Default::default() + } + } /// Creates a new instance of [`Arg`] using a unique string name. The name will be used to get /// information about whether or not the argument was used at runtime, get values, set /// relationships with other args, etc.. @@ -69,9 +145,12 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg`]: ./struct.Arg.html - pub fn with_name(n: &'a str) -> Self { + pub fn with_name(n: &'help str) -> Self { Arg { - b: Base::new(n), + id: n.key(), + name: n, + disp_ord: 999, + unified_ord: 999, ..Default::default() } } @@ -91,7 +170,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg`]: ./struct.Arg.html #[cfg(feature = "yaml")] - pub fn from_yaml(y: &BTreeMap) -> Arg { + pub fn from_yaml(y: &yaml_rust::yaml::Hash) -> Arg { // We WANT this to panic on error...so expect() is good. let name_yml = y.keys().nth(0).unwrap(); let name_str = name_yml.as_str().unwrap(); @@ -100,7 +179,7 @@ impl<'a, 'b> Arg<'a, 'b> { for (k, v) in arg_settings.iter() { a = match k.as_str().unwrap() { - "short" => yaml_to_str!(a, v, short), + "short" => yaml_to_char!(a, v, short), "long" => yaml_to_str!(a, v, long), "aliases" => yaml_vec_or_str!(v, a, alias), "help" => yaml_to_str!(a, v, help), @@ -114,7 +193,6 @@ impl<'a, 'b> Arg<'a, 'b> { "multiple" => yaml_to_bool!(a, v, multiple), "hidden" => yaml_to_bool!(a, v, hidden), "next_line_help" => yaml_to_bool!(a, v, next_line_help), - "empty_values" => yaml_to_bool!(a, v, empty_values), "group" => yaml_to_str!(a, v, group), "number_of_values" => yaml_to_u64!(a, v, number_of_values), "max_values" => yaml_to_u64!(a, v, max_values), @@ -122,7 +200,7 @@ impl<'a, 'b> Arg<'a, 'b> { "value_name" => yaml_to_str!(a, v, value_name), "use_delimiter" => yaml_to_bool!(a, v, use_delimiter), "allow_hyphen_values" => yaml_to_bool!(a, v, allow_hyphen_values), - "last" => yaml_to_bool!(a, v, last), + "require_equals" => yaml_to_bool!(a, v, require_equals), "require_delimiter" => yaml_to_bool!(a, v, require_delimiter), "value_delimiter" => yaml_to_str!(a, v, value_delimiter), "required_unless" => yaml_to_str!(a, v, required_unless), @@ -139,7 +217,6 @@ impl<'a, 'b> Arg<'a, 'b> { "conflicts_with" => yaml_vec_or_str!(v, a, conflicts_with), "overrides_with" => yaml_vec_or_str!(v, a, overrides_with), "possible_values" => yaml_vec_or_str!(v, a, possible_value), - "case_insensitive" => yaml_to_bool!(a, v, case_insensitive), "required_unless_one" => yaml_vec_or_str!(v, a, required_unless), "required_unless_all" => { a = yaml_vec_or_str!(v, a, required_unless); @@ -156,142 +233,6 @@ impl<'a, 'b> Arg<'a, 'b> { a } - /// Creates a new instance of [`Arg`] from a usage string. Allows creation of basic settings - /// for the [`Arg`]. The syntax is flexible, but there are some rules to follow. - /// - /// **NOTE**: Not all settings may be set using the usage string method. Some properties are - /// only available via the builder pattern. - /// - /// **NOTE**: Only ASCII values are officially supported in [`Arg::from_usage`] strings. Some - /// UTF-8 codepoints may work just fine, but this is not guaranteed. - /// - /// # Syntax - /// - /// Usage strings typically following the form: - /// - /// ```notrust - /// [explicit name] [short] [long] [value names] [help string] - /// ``` - /// - /// This is not a hard rule as the attributes can appear in other orders. There are also - /// several additional sigils which denote additional settings. Below are the details of each - /// portion of the string. - /// - /// ### Explicit Name - /// - /// This is an optional field, if it's omitted the argument will use one of the additional - /// fields as the name using the following priority order: - /// - /// * Explicit Name (This always takes precedence when present) - /// * Long - /// * Short - /// * Value Name - /// - /// `clap` determines explicit names as the first string of characters between either `[]` or - /// `<>` where `[]` has the dual notation of meaning the argument is optional, and `<>` meaning - /// the argument is required. - /// - /// Explicit names may be followed by: - /// * The multiple denotation `...` - /// - /// Example explicit names as follows (`ename` for an optional argument, and `rname` for a - /// required argument): - /// - /// ```notrust - /// [ename] -s, --long 'some flag' - /// -r, --longer 'some other flag' - /// ``` - /// - /// ### Short - /// - /// This is set by placing a single character after a leading `-`. - /// - /// Shorts may be followed by - /// * The multiple denotation `...` - /// * An optional comma `,` which is cosmetic only - /// * Value notation - /// - /// Example shorts are as follows (`-s`, and `-r`): - /// - /// ```notrust - /// -s, --long 'some flag' - /// -r [val], --longer 'some option' - /// ``` - /// - /// ### Long - /// - /// This is set by placing a word (no spaces) after a leading `--`. - /// - /// Shorts may be followed by - /// * The multiple denotation `...` - /// * Value notation - /// - /// Example longs are as follows (`--some`, and `--rapid`): - /// - /// ```notrust - /// -s, --some 'some flag' - /// --rapid=[FILE] 'some option' - /// ``` - /// - /// ### Values (Value Notation) - /// - /// This is set by placing a word(s) between `[]` or `<>` optionally after `=` (although this - /// is cosmetic only and does not affect functionality). If an explicit name has **not** been - /// set, using `<>` will denote a required argument, and `[]` will denote an optional argument - /// - /// Values may be followed by - /// * The multiple denotation `...` - /// * More Value notation - /// - /// More than one value will also implicitly set the arguments number of values, i.e. having - /// two values, `--option [val1] [val2]` specifies that in order for option to be satisified it - /// must receive exactly two values - /// - /// Example values are as follows (`FILE`, and `SPEED`): - /// - /// ```notrust - /// -s, --some [FILE] 'some option' - /// --rapid=... 'some required multiple option' - /// ``` - /// - /// ### Help String - /// - /// The help string is denoted between a pair of single quotes `''` and may contain any - /// characters. - /// - /// Example help strings are as follows: - /// - /// ```notrust - /// -s, --some [FILE] 'some option' - /// --rapid=... 'some required multiple option' - /// ``` - /// - /// ### Additional Sigils - /// - /// Multiple notation `...` (three consecutive dots/periods) specifies that this argument may - /// be used multiple times. Do not confuse multiple occurrences (`...`) with multiple values. - /// `--option val1 val2` is a single occurrence with multiple values. `--flag --flag` is - /// multiple occurrences (and then you can obviously have instances of both as well) - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// App::new("prog") - /// .args(&[ - /// Arg::from_usage("--config 'a required file for the configuration and no short'"), - /// Arg::from_usage("-d, --debug... 'turns on debugging information and allows multiples'"), - /// Arg::from_usage("[input] 'an optional input file to use'") - /// ]) - /// # ; - /// ``` - /// [`Arg`]: ./struct.Arg.html - /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage - pub fn from_usage(u: &'a str) -> Self { - let parser = UsageParser::from_usage(u); - parser.parse() - } - /// Sets the short version of the argument without the preceding `-`. /// /// By default `clap` automatically assigns `V` and `h` to the auto-generated `version` and @@ -299,18 +240,15 @@ impl<'a, 'b> Arg<'a, 'b> { /// arguments, in which case `clap` simply will not assign those to the auto-generated /// `version` or `help` arguments. /// - /// **NOTE:** Any leading `-` characters will be stripped, and only the first - /// non `-` character will be used as the [`short`] version - /// /// # Examples /// - /// To set [`short`] use a single valid UTF-8 code point. If you supply a leading `-` such as + /// To set [`short`] use a single valid UTF-8 character. If you supply a leading `-` such as /// `-c`, the `-` will be stripped. /// /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("config") - /// .short("c") + /// .short('c') /// # ; /// ``` /// @@ -320,7 +258,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// # use clap::{App, Arg}; /// let m = App::new("prog") /// .arg(Arg::with_name("config") - /// .short("c")) + /// .short('c')) /// .get_matches_from(vec![ /// "prog", "-c" /// ]); @@ -328,8 +266,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert!(m.is_present("config")); /// ``` /// [`short`]: ./struct.Arg.html#method.short - pub fn short>(mut self, s: S) -> Self { - self.s.short = s.as_ref().trim_left_matches(|c| c == '-').chars().nth(0); + pub fn short(mut self, s: char) -> Self { + self.short = Some(s); self } @@ -368,8 +306,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// assert!(m.is_present("cfg")); /// ``` - pub fn long(mut self, l: &'b str) -> Self { - self.s.long = Some(l.trim_left_matches(|c| c == '-')); + pub fn long(mut self, l: &'help str) -> Self { + self.long = Some(l.trim_start_matches(|c| c == '-')); self } @@ -394,11 +332,11 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert_eq!(m.value_of("test"), Some("cool")); /// ``` /// [`Arg`]: ./struct.Arg.html - pub fn alias>(mut self, name: S) -> Self { - if let Some(ref mut als) = self.s.aliases { + pub fn alias>(mut self, name: S) -> Self { + if let Some(ref mut als) = self.aliases { als.push((name.into(), false)); } else { - self.s.aliases = Some(vec![(name.into(), false)]); + self.aliases = Some(vec![(name.into(), false)]); } self } @@ -424,13 +362,13 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert!(m.is_present("test")); /// ``` /// [`Arg`]: ./struct.Arg.html - pub fn aliases(mut self, names: &[&'b str]) -> Self { - if let Some(ref mut als) = self.s.aliases { + pub fn aliases(mut self, names: &[&'help str]) -> Self { + if let Some(ref mut als) = self.aliases { for n in names { als.push((n, false)); } } else { - self.s.aliases = Some(names.iter().map(|n| (*n, false)).collect::>()); + self.aliases = Some(names.iter().map(|&x| (x, false)).collect()); } self } @@ -455,11 +393,11 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg`]: ./struct.Arg.html /// [`App::alias`]: ./struct.Arg.html#method.alias - pub fn visible_alias>(mut self, name: S) -> Self { - if let Some(ref mut als) = self.s.aliases { + pub fn visible_alias>(mut self, name: S) -> Self { + if let Some(ref mut als) = self.aliases { als.push((name.into(), true)); } else { - self.s.aliases = Some(vec![(name.into(), true)]); + self.aliases = Some(vec![(name.into(), true)]); } self } @@ -482,13 +420,13 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg`]: ./struct.Arg.html /// [`App::aliases`]: ./struct.Arg.html#method.aliases - pub fn visible_aliases(mut self, names: &[&'b str]) -> Self { - if let Some(ref mut als) = self.s.aliases { + pub fn visible_aliases(mut self, names: &[&'help str]) -> Self { + if let Some(ref mut als) = self.aliases { for n in names { als.push((n, true)); } } else { - self.s.aliases = Some(names.iter().map(|n| (*n, true)).collect::>()); + self.aliases = Some(names.iter().map(|n| (*n, true)).collect::>()); } self } @@ -543,8 +481,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// -V, --version Prints version information /// ``` /// [`Arg::long_help`]: ./struct.Arg.html#method.long_help - pub fn help(mut self, h: &'b str) -> Self { - self.b.help = Some(h); + pub fn help(mut self, h: &'help str) -> Self { + self.help = Some(h); self } @@ -614,507 +552,242 @@ impl<'a, 'b> Arg<'a, 'b> { /// Prints version information /// ``` /// [`Arg::help`]: ./struct.Arg.html#method.help - pub fn long_help(mut self, h: &'b str) -> Self { - self.b.long_help = Some(h); + pub fn long_help(mut self, h: &'help str) -> Self { + self.long_help = Some(h); self } - /// Specifies that this arg is the last, or final, positional argument (i.e. has the highest - /// index) and is *only* able to be accessed via the `--` syntax (i.e. `$ prog args -- - /// last_arg`). Even, if no other arguments are left to parse, if the user omits the `--` syntax - /// they will receive an [`UnknownArgument`] error. Setting an argument to `.last(true)` also - /// allows one to access this arg early using the `--` syntax. Accessing an arg early, even with - /// the `--` syntax is otherwise not possible. - /// - /// **NOTE:** This will change the usage string to look like `$ prog [FLAGS] [-- ]` if - /// `ARG` is marked as `.last(true)`. - /// - /// **NOTE:** This setting will imply [`AppSettings::DontCollapseArgsInUsage`] because failing - /// to set this can make the usage string very confusing. - /// - /// **NOTE**: This setting only applies to positional arguments, and has no affect on FLAGS / - /// OPTIONS + /// Sets an arg that override this arg's required setting. (i.e. this arg will be required + /// unless this other argument is present). /// - /// **CAUTION:** Setting an argument to `.last(true)` *and* having child subcommands is not - /// recommended with the exception of *also* using [`AppSettings::ArgsNegateSubcommands`] - /// (or [`AppSettings::SubcommandsNegateReqs`] if the argument marked `.last(true)` is also - /// marked [`.required(true)`]) + /// **Pro Tip:** Using [`Arg::required_unless`] implies [`Arg::required`] and is therefore not + /// mandatory to also set. /// /// # Examples /// /// ```rust /// # use clap::Arg; - /// Arg::with_name("args") - /// .last(true) + /// Arg::with_name("config") + /// .required_unless("debug") /// # ; /// ``` /// - /// Setting [`Arg::last(true)`] ensures the arg has the highest [index] of all positional args - /// and requires that the `--` syntax be used to access it early. + /// Setting [`Arg::required_unless(name)`] requires that the argument be used at runtime + /// *unless* `name` is present. In the following example, the required argument is *not* + /// provided, but it's not an error because the `unless` arg has been supplied. /// /// ```rust /// # use clap::{App, Arg}; /// let res = App::new("prog") - /// .arg(Arg::with_name("first")) - /// .arg(Arg::with_name("second")) - /// .arg(Arg::with_name("third").last(true)) - /// .get_matches_from_safe(vec![ - /// "prog", "one", "--", "three" + /// .arg(Arg::with_name("cfg") + /// .required_unless("dbg") + /// .takes_value(true) + /// .long("config")) + /// .arg(Arg::with_name("dbg") + /// .long("debug")) + /// .try_get_matches_from(vec![ + /// "prog", "--debug" /// ]); /// /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert_eq!(m.value_of("third"), Some("three")); - /// assert!(m.value_of("second").is_none()); /// ``` /// - /// Even if the positional argument marked `.last(true)` is the only argument left to parse, - /// failing to use the `--` syntax results in an error. + /// Setting [`Arg::required_unless(name)`] and *not* supplying `name` or this arg is an error. /// /// ```rust /// # use clap::{App, Arg, ErrorKind}; /// let res = App::new("prog") - /// .arg(Arg::with_name("first")) - /// .arg(Arg::with_name("second")) - /// .arg(Arg::with_name("third").last(true)) - /// .get_matches_from_safe(vec![ - /// "prog", "one", "two", "three" + /// .arg(Arg::with_name("cfg") + /// .required_unless("dbg") + /// .takes_value(true) + /// .long("config")) + /// .arg(Arg::with_name("dbg") + /// .long("debug")) + /// .try_get_matches_from(vec![ + /// "prog" /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); /// ``` - /// [`Arg::last(true)`]: ./struct.Arg.html#method.last - /// [index]: ./struct.Arg.html#method.index - /// [`AppSettings::DontCollapseArgsInUsage`]: ./enum.AppSettings.html#variant.DontCollapseArgsInUsage - /// [`AppSettings::ArgsNegateSubcommands`]: ./enum.AppSettings.html#variant.ArgsNegateSubcommands - /// [`AppSettings::SubcommandsNegateReqs`]: ./enum.AppSettings.html#variant.SubcommandsNegateReqs - /// [`.required(true)`]: ./struct.Arg.html#method.required - /// [`UnknownArgument`]: ./enum.ErrorKind.html#variant.UnknownArgument - pub fn last(self, l: bool) -> Self { - if l { - self.set(ArgSettings::Last) + /// [`Arg::required_unless`]: ./struct.Arg.html#method.required_unless + /// [`Arg::required`]: ./struct.Arg.html#method.required + /// [`Arg::required_unless(name)`]: ./struct.Arg.html#method.required_unless + pub fn required_unless(mut self, arg_id: T) -> Self { + let name = arg_id.key(); + if let Some(ref mut vec) = self.r_unless { + vec.push(name); } else { - self.unset(ArgSettings::Last) + self.r_unless = Some(vec![name]); } + self } - /// Sets whether or not the argument is required by default. Required by default means it is - /// required, when no other conflicting rules have been evaluated. Conflicting rules take - /// precedence over being required. **Default:** `false` + /// Sets args that override this arg's required setting. (i.e. this arg will be required unless + /// all these other arguments are present). /// - /// **NOTE:** Flags (i.e. not positional, or arguments that take values) cannot be required by - /// default. This is simply because if a flag should be required, it should simply be implied - /// as no additional information is required from user. Flags by their very nature are simply - /// yes/no, or true/false. + /// **NOTE:** If you wish for this argument to only be required if *one of* these args are + /// present see [`Arg::required_unless_one`] /// /// # Examples /// /// ```rust /// # use clap::Arg; /// Arg::with_name("config") - /// .required(true) + /// .required_unless_all(&["cfg", "dbg"]) /// # ; /// ``` /// - /// Setting [`Arg::required(true)`] requires that the argument be used at runtime. + /// Setting [`Arg::required_unless_all(names)`] requires that the argument be used at runtime + /// *unless* *all* the args in `names` are present. In the following example, the required + /// argument is *not* provided, but it's not an error because all the `unless` args have been + /// supplied. /// /// ```rust /// # use clap::{App, Arg}; /// let res = App::new("prog") /// .arg(Arg::with_name("cfg") - /// .required(true) + /// .required_unless_all(&["dbg", "infile"]) /// .takes_value(true) /// .long("config")) - /// .get_matches_from_safe(vec![ - /// "prog", "--config", "file.conf" + /// .arg(Arg::with_name("dbg") + /// .long("debug")) + /// .arg(Arg::with_name("infile") + /// .short('i') + /// .takes_value(true)) + /// .try_get_matches_from(vec![ + /// "prog", "--debug", "-i", "file" /// ]); /// /// assert!(res.is_ok()); /// ``` /// - /// Setting [`Arg::required(true)`] and *not* supplying that argument is an error. + /// Setting [`Arg::required_unless_all(names)`] and *not* supplying *all* of `names` or this + /// arg is an error. /// /// ```rust /// # use clap::{App, Arg, ErrorKind}; /// let res = App::new("prog") /// .arg(Arg::with_name("cfg") - /// .required(true) + /// .required_unless_all(&["dbg", "infile"]) /// .takes_value(true) /// .long("config")) - /// .get_matches_from_safe(vec![ + /// .arg(Arg::with_name("dbg") + /// .long("debug")) + /// .arg(Arg::with_name("infile") + /// .short('i') + /// .takes_value(true)) + /// .try_get_matches_from(vec![ /// "prog" /// ]); /// /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); /// ``` - /// [`Arg::required(true)`]: ./struct.Arg.html#method.required - pub fn required(self, r: bool) -> Self { - if r { - self.set(ArgSettings::Required) + /// [`Arg::required_unless_one`]: ./struct.Arg.html#method.required_unless_one + /// [`Arg::required_unless_all(names)`]: ./struct.Arg.html#method.required_unless_all + pub fn required_unless_all(mut self, names: &[&str]) -> Self { + if let Some(ref mut vec) = self.r_unless { + for s in names { + vec.push(s.key()); + } } else { - self.unset(ArgSettings::Required) + self.r_unless = Some(names.iter().map(Key::key).collect()); } + self.setting(ArgSettings::RequiredUnlessAll) } - /// Requires that options use the `--option=val` syntax (i.e. an equals between the option and - /// associated value) **Default:** `false` + /// Sets args that override this arg's [required] setting. (i.e. this arg will be required + /// unless *at least one of* these other arguments are present). /// - /// **NOTE:** This setting also removes the default of allowing empty values and implies - /// [`Arg::empty_values(false)`]. + /// **NOTE:** If you wish for this argument to only be required if *all of* these args are + /// present see [`Arg::required_unless_all`] /// /// # Examples /// /// ```rust /// # use clap::Arg; /// Arg::with_name("config") - /// .long("config") - /// .takes_value(true) - /// .require_equals(true) + /// .required_unless_all(&["cfg", "dbg"]) /// # ; /// ``` /// - /// Setting [`Arg::require_equals(true)`] requires that the option have an equals sign between - /// it and the associated value. + /// Setting [`Arg::required_unless_one(names)`] requires that the argument be used at runtime + /// *unless* *at least one of* the args in `names` are present. In the following example, the + /// required argument is *not* provided, but it's not an error because one the `unless` args + /// have been supplied. /// /// ```rust /// # use clap::{App, Arg}; /// let res = App::new("prog") /// .arg(Arg::with_name("cfg") - /// .require_equals(true) + /// .required_unless_one(&["dbg", "infile"]) /// .takes_value(true) /// .long("config")) - /// .get_matches_from_safe(vec![ - /// "prog", "--config=file.conf" + /// .arg(Arg::with_name("dbg") + /// .long("debug")) + /// .arg(Arg::with_name("infile") + /// .short('i') + /// .takes_value(true)) + /// .try_get_matches_from(vec![ + /// "prog", "--debug" /// ]); /// /// assert!(res.is_ok()); /// ``` /// - /// Setting [`Arg::require_equals(true)`] and *not* supplying the equals will cause an error - /// unless [`Arg::empty_values(true)`] is set. + /// Setting [`Arg::required_unless_one(names)`] and *not* supplying *at least one of* `names` + /// or this arg is an error. /// /// ```rust /// # use clap::{App, Arg, ErrorKind}; /// let res = App::new("prog") /// .arg(Arg::with_name("cfg") - /// .require_equals(true) + /// .required_unless_one(&["dbg", "infile"]) /// .takes_value(true) /// .long("config")) - /// .get_matches_from_safe(vec![ - /// "prog", "--config", "file.conf" + /// .arg(Arg::with_name("dbg") + /// .long("debug")) + /// .arg(Arg::with_name("infile") + /// .short('i') + /// .takes_value(true)) + /// .try_get_matches_from(vec![ + /// "prog" /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); /// ``` - /// [`Arg::require_equals(true)`]: ./struct.Arg.html#method.require_equals - /// [`Arg::empty_values(true)`]: ./struct.Arg.html#method.empty_values - /// [`Arg::empty_values(false)`]: ./struct.Arg.html#method.empty_values - pub fn require_equals(mut self, r: bool) -> Self { - if r { - self.unsetb(ArgSettings::EmptyValues); - self.set(ArgSettings::RequireEquals) + /// [required]: ./struct.Arg.html#method.required + /// [`Arg::required_unless_one(names)`]: ./struct.Arg.html#method.required_unless_one + /// [`Arg::required_unless_all`]: ./struct.Arg.html#method.required_unless_all + pub fn required_unless_one(mut self, names: &[&str]) -> Self { + if let Some(ref mut vec) = self.r_unless { + for s in names { + vec.push(s.key()); + } } else { - self.unset(ArgSettings::RequireEquals) + self.r_unless = Some(names.iter().map(Key::key).collect()); } + self } - /// Allows values which start with a leading hyphen (`-`) + /// Sets a conflicting argument by name. I.e. when using this argument, + /// the following argument can't be present and vice versa. /// - /// **WARNING**: Take caution when using this setting combined with [`Arg::multiple(true)`], as - /// this becomes ambiguous `$ prog --arg -- -- val`. All three `--, --, val` will be values - /// when the user may have thought the second `--` would constitute the normal, "Only - /// positional args follow" idiom. To fix this, consider using [`Arg::number_of_values(1)`] + /// **NOTE:** Conflicting rules take precedence over being required by default. Conflict rules + /// only need to be set for one of the two arguments, they do not need to be set for each. /// - /// **WARNING**: When building your CLIs, consider the effects of allowing leading hyphens and - /// the user passing in a value that matches a valid short. For example `prog -opt -F` where - /// `-F` is supposed to be a value, yet `-F` is *also* a valid short for another arg. Care should - /// should be taken when designing these args. This is compounded by the ability to "stack" - /// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid - /// shorts. + /// **NOTE:** Defining a conflict is two-way, but does *not* need to defined for both arguments + /// (i.e. if A conflicts with B, defining A.conflicts_with(B) is sufficient. You do not need + /// need to also do B.conflicts_with(A)) /// /// # Examples /// /// ```rust /// # use clap::Arg; - /// Arg::with_name("pattern") - /// .allow_hyphen_values(true) - /// # ; - /// ``` - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("pat") - /// .allow_hyphen_values(true) - /// .takes_value(true) - /// .long("pattern")) - /// .get_matches_from(vec![ - /// "prog", "--pattern", "-file" - /// ]); - /// - /// assert_eq!(m.value_of("pat"), Some("-file")); - /// ``` - /// - /// Not setting [`Arg::allow_hyphen_values(true)`] and supplying a value which starts with a - /// hyphen is an error. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("pat") - /// .takes_value(true) - /// .long("pattern")) - /// .get_matches_from_safe(vec![ - /// "prog", "--pattern", "-file" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - /// [`Arg::allow_hyphen_values(true)`]: ./struct.Arg.html#method.allow_hyphen_values - /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple - /// [`Arg::number_of_values(1)`]: ./struct.Arg.html#method.number_of_values - pub fn allow_hyphen_values(self, a: bool) -> Self { - if a { - self.set(ArgSettings::AllowLeadingHyphen) - } else { - self.unset(ArgSettings::AllowLeadingHyphen) - } - } - /// Sets an arg that override this arg's required setting. (i.e. this arg will be required - /// unless this other argument is present). - /// - /// **Pro Tip:** Using [`Arg::required_unless`] implies [`Arg::required`] and is therefore not - /// mandatory to also set. - /// - /// # Examples - /// - /// ```rust - /// # use clap::Arg; - /// Arg::with_name("config") - /// .required_unless("debug") - /// # ; - /// ``` - /// - /// Setting [`Arg::required_unless(name)`] requires that the argument be used at runtime - /// *unless* `name` is present. In the following example, the required argument is *not* - /// provided, but it's not an error because the `unless` arg has been supplied. - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .required_unless("dbg") - /// .takes_value(true) - /// .long("config")) - /// .arg(Arg::with_name("dbg") - /// .long("debug")) - /// .get_matches_from_safe(vec![ - /// "prog", "--debug" - /// ]); - /// - /// assert!(res.is_ok()); - /// ``` - /// - /// Setting [`Arg::required_unless(name)`] and *not* supplying `name` or this arg is an error. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .required_unless("dbg") - /// .takes_value(true) - /// .long("config")) - /// .arg(Arg::with_name("dbg") - /// .long("debug")) - /// .get_matches_from_safe(vec![ - /// "prog" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); - /// ``` - /// [`Arg::required_unless`]: ./struct.Arg.html#method.required_unless - /// [`Arg::required`]: ./struct.Arg.html#method.required - /// [`Arg::required_unless(name)`]: ./struct.Arg.html#method.required_unless - pub fn required_unless(mut self, name: &'a str) -> Self { - if let Some(ref mut vec) = self.b.r_unless { - vec.push(name); - } else { - self.b.r_unless = Some(vec![name]); - } - self.required(true) - } - - /// Sets args that override this arg's required setting. (i.e. this arg will be required unless - /// all these other arguments are present). - /// - /// **NOTE:** If you wish for this argument to only be required if *one of* these args are - /// present see [`Arg::required_unless_one`] - /// - /// # Examples - /// - /// ```rust - /// # use clap::Arg; - /// Arg::with_name("config") - /// .required_unless_all(&["cfg", "dbg"]) - /// # ; - /// ``` - /// - /// Setting [`Arg::required_unless_all(names)`] requires that the argument be used at runtime - /// *unless* *all* the args in `names` are present. In the following example, the required - /// argument is *not* provided, but it's not an error because all the `unless` args have been - /// supplied. - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .required_unless_all(&["dbg", "infile"]) - /// .takes_value(true) - /// .long("config")) - /// .arg(Arg::with_name("dbg") - /// .long("debug")) - /// .arg(Arg::with_name("infile") - /// .short("i") - /// .takes_value(true)) - /// .get_matches_from_safe(vec![ - /// "prog", "--debug", "-i", "file" - /// ]); - /// - /// assert!(res.is_ok()); - /// ``` - /// - /// Setting [`Arg::required_unless_all(names)`] and *not* supplying *all* of `names` or this - /// arg is an error. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .required_unless_all(&["dbg", "infile"]) - /// .takes_value(true) - /// .long("config")) - /// .arg(Arg::with_name("dbg") - /// .long("debug")) - /// .arg(Arg::with_name("infile") - /// .short("i") - /// .takes_value(true)) - /// .get_matches_from_safe(vec![ - /// "prog" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); - /// ``` - /// [`Arg::required_unless_one`]: ./struct.Arg.html#method.required_unless_one - /// [`Arg::required_unless_all(names)`]: ./struct.Arg.html#method.required_unless_all - pub fn required_unless_all(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.b.r_unless { - for s in names { - vec.push(s); - } - } else { - self.b.r_unless = Some(names.iter().map(|s| *s).collect::>()); - } - self.setb(ArgSettings::RequiredUnlessAll); - self.required(true) - } - - /// Sets args that override this arg's [required] setting. (i.e. this arg will be required - /// unless *at least one of* these other arguments are present). - /// - /// **NOTE:** If you wish for this argument to only be required if *all of* these args are - /// present see [`Arg::required_unless_all`] - /// - /// # Examples - /// - /// ```rust - /// # use clap::Arg; - /// Arg::with_name("config") - /// .required_unless_all(&["cfg", "dbg"]) - /// # ; - /// ``` - /// - /// Setting [`Arg::required_unless_one(names)`] requires that the argument be used at runtime - /// *unless* *at least one of* the args in `names` are present. In the following example, the - /// required argument is *not* provided, but it's not an error because one the `unless` args - /// have been supplied. - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .required_unless_one(&["dbg", "infile"]) - /// .takes_value(true) - /// .long("config")) - /// .arg(Arg::with_name("dbg") - /// .long("debug")) - /// .arg(Arg::with_name("infile") - /// .short("i") - /// .takes_value(true)) - /// .get_matches_from_safe(vec![ - /// "prog", "--debug" - /// ]); - /// - /// assert!(res.is_ok()); - /// ``` - /// - /// Setting [`Arg::required_unless_one(names)`] and *not* supplying *at least one of* `names` - /// or this arg is an error. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .required_unless_one(&["dbg", "infile"]) - /// .takes_value(true) - /// .long("config")) - /// .arg(Arg::with_name("dbg") - /// .long("debug")) - /// .arg(Arg::with_name("infile") - /// .short("i") - /// .takes_value(true)) - /// .get_matches_from_safe(vec![ - /// "prog" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); - /// ``` - /// [required]: ./struct.Arg.html#method.required - /// [`Arg::required_unless_one(names)`]: ./struct.Arg.html#method.required_unless_one - /// [`Arg::required_unless_all`]: ./struct.Arg.html#method.required_unless_all - pub fn required_unless_one(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.b.r_unless { - for s in names { - vec.push(s); - } - } else { - self.b.r_unless = Some(names.iter().map(|s| *s).collect::>()); - } - self.required(true) - } - - /// Sets a conflicting argument by name. I.e. when using this argument, - /// the following argument can't be present and vice versa. - /// - /// **NOTE:** Conflicting rules take precedence over being required by default. Conflict rules - /// only need to be set for one of the two arguments, they do not need to be set for each. - /// - /// **NOTE:** Defining a conflict is two-way, but does *not* need to defined for both arguments - /// (i.e. if A conflicts with B, defining A.conflicts_with(B) is sufficient. You do not need - /// need to also do B.conflicts_with(A)) - /// - /// # Examples - /// - /// ```rust - /// # use clap::Arg; - /// Arg::with_name("config") - /// .conflicts_with("debug") + /// Arg::with_name("config") + /// .conflicts_with("debug") /// # ; /// ``` /// @@ -1129,18 +802,19 @@ impl<'a, 'b> Arg<'a, 'b> { /// .long("config")) /// .arg(Arg::with_name("debug") /// .long("debug")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--debug", "--config", "file.conf" /// ]); /// /// assert!(res.is_err()); /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); /// ``` - pub fn conflicts_with(mut self, name: &'a str) -> Self { - if let Some(ref mut vec) = self.b.blacklist { + pub fn conflicts_with(mut self, arg_id: T) -> Self { + let name = arg_id.key(); + if let Some(ref mut vec) = self.blacklist { vec.push(name); } else { - self.b.blacklist = Some(vec![name]); + self.blacklist = Some(vec![name]); } self } @@ -1178,7 +852,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .long("debug")) /// .arg(Arg::with_name("input") /// .index(1)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--config", "file.conf", "file.txt" /// ]); /// @@ -1186,13 +860,13 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); /// ``` /// [`Arg::conflicts_with`]: ./struct.Arg.html#method.conflicts_with - pub fn conflicts_with_all(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.b.blacklist { + pub fn conflicts_with_all(mut self, names: &[&str]) -> Self { + if let Some(ref mut vec) = self.blacklist { for s in names { - vec.push(s); + vec.push(s.key()); } } else { - self.b.blacklist = Some(names.iter().map(|s| *s).collect::>()); + self.blacklist = Some(names.iter().map(Key::key).collect()); } self } @@ -1204,19 +878,20 @@ impl<'a, 'b> Arg<'a, 'b> { /// **NOTE:** When an argument is overridden it is essentially as if it never was used, any /// conflicts, requirements, etc. are evaluated **after** all "overrides" have been removed /// - /// **WARNING:** Positional arguments cannot override themselves (or we would never be able - /// to advance to the next positional). If a positional agument lists itself as an override, - /// it is simply ignored. + /// **WARNING:** Positional arguments and options which accept [`Multiple*`] cannot override + /// themselves (or we would never be able to advance to the next positional). If a positional + /// argument or option with one of the [`Multiple*`] settings lists itself as an override, it is + /// simply ignored. /// /// # Examples /// - /// ```rust + /// ```rust # use clap::{App, Arg}; /// # use clap::{App, Arg}; /// let m = App::new("prog") - /// .arg(Arg::from_usage("-f, --flag 'some flag'") + /// .arg(Arg::from("-f, --flag 'some flag'") /// .conflicts_with("debug")) - /// .arg(Arg::from_usage("-d, --debug 'other flag'")) - /// .arg(Arg::from_usage("-c, --color 'third flag'") + /// .arg(Arg::from("-d, --debug 'other flag'")) + /// .arg(Arg::from("-c, --color 'third flag'") /// .overrides_with("flag")) /// .get_matches_from(vec![ /// "prog", "-f", "-d", "-c"]); @@ -1238,41 +913,42 @@ impl<'a, 'b> Arg<'a, 'b> { /// ```rust /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from_usage("--flag 'some flag'").overrides_with("flag")) + /// .arg(Arg::from("--flag 'some flag'").overrides_with("flag")) /// .get_matches_from(vec!["posix", "--flag", "--flag"]); /// assert!(m.is_present("flag")); /// assert_eq!(m.occurrences_of("flag"), 1); /// ``` - /// Making a arg `multiple(true)` and override itself is essentially meaningless. Therefore + /// Making a arg [`Multiple*``] and override itself is essentially meaningless. Therefore /// clap ignores an override of self if it's a flag and it already accepts multiple occurrences. /// /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from_usage("--flag... 'some flag'").overrides_with("flag")) + /// .arg(Arg::from("--flag... 'some flag'").overrides_with("flag")) /// .get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); /// assert!(m.is_present("flag")); /// assert_eq!(m.occurrences_of("flag"), 4); /// ``` - /// Now notice with options (which *do not* set `multiple(true)`), it's as if only the last - /// occurrence happened. + /// Now notice with options (which *do not* set one of the [`Multiple*`]), it's as if only the + /// last occurrence happened. /// /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from_usage("--opt [val] 'some option'").overrides_with("opt")) + /// .arg(Arg::from("--opt [val] 'some option'").overrides_with("opt")) /// .get_matches_from(vec!["", "--opt=some", "--opt=other"]); /// assert!(m.is_present("opt")); /// assert_eq!(m.occurrences_of("opt"), 1); /// assert_eq!(m.value_of("opt"), Some("other")); /// ``` /// - /// Just like flags, options with `multiple(true)` set, will ignore the "override self" setting. + /// Just like flags, options with one of the [`Multiple*``] set, will ignore the "override self" + /// setting. /// /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from_usage("--opt [val]... 'some option'") + /// .arg(Arg::from("--opt [val]... 'some option'") /// .overrides_with("opt")) /// .get_matches_from(vec!["", "--opt", "first", "over", "--opt", "other", "val"]); /// assert!(m.is_present("opt")); @@ -1281,25 +957,27 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// /// A safe thing to do if you'd like to support an option which supports multiple values, but - /// also is "overridable" by itself, is to use `use_delimiter(false)` and *not* use - /// `multiple(true)` while telling users to seperate values with a comma (i.e. `val1,val2`) + /// also is "overridable" by itself, is to not use [`UseValueDelimiter`] and *not* use + /// `MultipleValues` while telling users to separate values with a comma (i.e. `val1,val2`) /// /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from_usage("--opt [val] 'some option'") - /// .overrides_with("opt") - /// .use_delimiter(false)) + /// .arg(Arg::from("--opt [val] 'some option'") + /// .overrides_with("opt")) /// .get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]); /// assert!(m.is_present("opt")); /// assert_eq!(m.occurrences_of("opt"), 1); /// assert_eq!(m.values_of("opt").unwrap().collect::>(), &["one,two"]); /// ``` - pub fn overrides_with(mut self, name: &'a str) -> Self { - if let Some(ref mut vec) = self.b.overrides { + /// [`Multiple*`]: ./enum.ArgSettings.html#variant.MultipleValues + /// [`UseValueDelimiter`]: ./enum.ArgSettings.html#variant.UseValueDelimiter + pub fn overrides_with(mut self, arg_id: T) -> Self { + let name = arg_id.key(); + if let Some(ref mut vec) = self.overrides { vec.push(name); } else { - self.b.overrides = Some(vec![name]); + self.overrides = Some(vec![name]); } self } @@ -1316,10 +994,10 @@ impl<'a, 'b> Arg<'a, 'b> { /// ```rust /// # use clap::{App, Arg}; /// let m = App::new("prog") - /// .arg(Arg::from_usage("-f, --flag 'some flag'") + /// .arg(Arg::from("-f, --flag 'some flag'") /// .conflicts_with("color")) - /// .arg(Arg::from_usage("-d, --debug 'other flag'")) - /// .arg(Arg::from_usage("-c, --color 'third flag'") + /// .arg(Arg::from("-d, --debug 'other flag'")) + /// .arg(Arg::from("-c, --color 'third flag'") /// .overrides_with_all(&["flag", "debug"])) /// .get_matches_from(vec![ /// "prog", "-f", "-d", "-c"]); @@ -1331,13 +1009,13 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert!(!m.is_present("debug")); /// assert!(!m.is_present("flag")); /// ``` - pub fn overrides_with_all(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.b.overrides { + pub fn overrides_with_all(mut self, names: &[T]) -> Self { + if let Some(ref mut vec) = self.overrides { for s in names { - vec.push(s); + vec.push(s.key()); } } else { - self.b.overrides = Some(names.iter().map(|s| *s).collect::>()); + self.overrides = Some(names.iter().map(Key::key).collect()); } self } @@ -1369,7 +1047,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .long("config")) /// .arg(Arg::with_name("input") /// .index(1)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog" /// ]); /// @@ -1387,7 +1065,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .long("config")) /// .arg(Arg::with_name("input") /// .index(1)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--config", "file.conf" /// ]); /// @@ -1397,13 +1075,14 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::requires(name)`]: ./struct.Arg.html#method.requires /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [override]: ./struct.Arg.html#method.overrides_with - pub fn requires(mut self, name: &'a str) -> Self { - if let Some(ref mut vec) = self.b.requires { + pub fn requires(mut self, arg_id: T) -> Self { + let name = arg_id.key(); + if let Some(ref mut vec) = self.requires { vec.push((None, name)); } else { let mut vec = vec![]; vec.push((None, name)); - self.b.requires = Some(vec); + self.requires = Some(vec); } self } @@ -1439,7 +1118,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .requires_if("my.cfg", "other") /// .long("config")) /// .arg(Arg::with_name("other")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--config", "some.cfg" /// ]); /// @@ -1457,7 +1136,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .requires_if("my.cfg", "input") /// .long("config")) /// .arg(Arg::with_name("input")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--config", "my.cfg" /// ]); /// @@ -1467,11 +1146,12 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::requires(name)`]: ./struct.Arg.html#method.requires /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [override]: ./struct.Arg.html#method.overrides_with - pub fn requires_if(mut self, val: &'b str, arg: &'a str) -> Self { - if let Some(ref mut vec) = self.b.requires { + pub fn requires_if(mut self, val: &'help str, arg_id: T) -> Self { + let arg = arg_id.key(); + if let Some(ref mut vec) = self.requires { vec.push((Some(val), arg)); } else { - self.b.requires = Some(vec![(Some(val), arg)]); + self.requires = Some(vec![(Some(val), arg)]); } self } @@ -1517,7 +1197,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .long("option") /// .takes_value(true)) /// .arg(Arg::with_name("other")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--config", "special.conf" /// ]); /// @@ -1527,17 +1207,17 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::requires(name)`]: ./struct.Arg.html#method.requires /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [override]: ./struct.Arg.html#method.overrides_with - pub fn requires_ifs(mut self, ifs: &[(&'b str, &'a str)]) -> Self { - if let Some(ref mut vec) = self.b.requires { - for &(val, arg) in ifs { - vec.push((Some(val), arg)); + pub fn requires_ifs(mut self, ifs: &[(&'help str, T)]) -> Self { + if let Some(ref mut vec) = self.requires { + for (val, arg) in ifs { + vec.push((Some(val), arg.key())); } } else { let mut vec = vec![]; - for &(val, arg) in ifs { - vec.push((Some(val), arg)); + for (val, arg) in ifs { + vec.push((Some(*val), arg.key())); } - self.b.requires = Some(vec); + self.requires = Some(vec); } self } @@ -1575,7 +1255,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("other") /// .long("other") /// .takes_value(true)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--other", "not-special" /// ]); /// @@ -1595,7 +1275,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("other") /// .long("other") /// .takes_value(true)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--other", "special" /// ]); /// @@ -1605,7 +1285,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::requires(name)`]: ./struct.Arg.html#method.requires /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [required]: ./struct.Arg.html#method.required - pub fn required_if(mut self, arg: &'a str, val: &'b str) -> Self { + pub fn required_if(mut self, arg_id: T, val: &'help str) -> Self { + let arg = arg_id.key(); if let Some(ref mut vec) = self.r_ifs { vec.push((arg, val)); } else { @@ -1658,7 +1339,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("option") /// .takes_value(true) /// .long("option")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--option", "other" /// ]); /// @@ -1684,7 +1365,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("option") /// .takes_value(true) /// .long("option")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--option", "spec" /// ]); /// @@ -1694,15 +1375,15 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::requires(name)`]: ./struct.Arg.html#method.requires /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [required]: ./struct.Arg.html#method.required - pub fn required_ifs(mut self, ifs: &[(&'a str, &'b str)]) -> Self { + pub fn required_ifs(mut self, ifs: &[(T, &'help str)]) -> Self { if let Some(ref mut vec) = self.r_ifs { for r_if in ifs { - vec.push((r_if.0, r_if.1)); + vec.push((r_if.0.key(), r_if.1)); } } else { let mut vec = vec![]; for r_if in ifs { - vec.push((r_if.0, r_if.1)); + vec.push((r_if.0.key(), r_if.1)); } self.r_ifs = Some(vec); } @@ -1739,7 +1420,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .index(1)) /// .arg(Arg::with_name("output") /// .index(2)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog" /// ]); /// @@ -1760,7 +1441,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .index(1)) /// .arg(Arg::with_name("output") /// .index(2)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--config", "file.conf", "in.txt" /// ]); /// @@ -1771,138 +1452,22 @@ impl<'a, 'b> Arg<'a, 'b> { /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [override]: ./struct.Arg.html#method.overrides_with /// [`Arg::requires_all(&[arg, arg2])`]: ./struct.Arg.html#method.requires_all - pub fn requires_all(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.b.requires { + pub fn requires_all(mut self, names: &[T]) -> Self { + if let Some(ref mut vec) = self.requires { for s in names { - vec.push((None, s)); + vec.push((None, s.key())); } } else { let mut vec = vec![]; for s in names { - vec.push((None, *s)); + vec.push((None, s.key())); } - self.b.requires = Some(vec); + self.requires = Some(vec); } self } - /// Specifies that the argument takes a value at run time. - /// - /// **NOTE:** values for arguments may be specified in any of the following methods - /// - /// * Using a space such as `-o value` or `--option value` - /// * Using an equals and no space such as `-o=value` or `--option=value` - /// * Use a short and no space such as `-ovalue` - /// - /// **NOTE:** By default, args which allow [multiple values] are delimited by commas, meaning - /// `--option=val1,val2,val3` is three values for the `--option` argument. If you wish to - /// change the delimiter to another character you can use [`Arg::value_delimiter(char)`], - /// alternatively you can turn delimiting values **OFF** by using [`Arg::use_delimiter(false)`] - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("config") - /// .takes_value(true) - /// # ; - /// ``` - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("mode") - /// .long("mode") - /// .takes_value(true)) - /// .get_matches_from(vec![ - /// "prog", "--mode", "fast" - /// ]); - /// - /// assert!(m.is_present("mode")); - /// assert_eq!(m.value_of("mode"), Some("fast")); - /// ``` - /// [`Arg::value_delimiter(char)`]: ./struct.Arg.html#method.value_delimiter - /// [`Arg::use_delimiter(false)`]: ./struct.Arg.html#method.use_delimiter - /// [multiple values]: ./struct.Arg.html#method.multiple - pub fn takes_value(self, tv: bool) -> Self { - if tv { - self.set(ArgSettings::TakesValue) - } else { - self.unset(ArgSettings::TakesValue) - } - } - - /// Specifies if the possible values of an argument should be displayed in the help text or - /// not. Defaults to `false` (i.e. show possible values) - /// - /// This is useful for args with many values, or ones which are explained elsewhere in the - /// help text. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("config") - /// .hide_possible_values(true) - /// # ; - /// ``` - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("mode") - /// .long("mode") - /// .possible_values(&["fast", "slow"]) - /// .takes_value(true) - /// .hide_possible_values(true)); - /// - /// ``` - /// - /// If we were to run the above program with `--help` the `[values: fast, slow]` portion of - /// the help text would be omitted. - pub fn hide_possible_values(self, hide: bool) -> Self { - if hide { - self.set(ArgSettings::HidePossibleValues) - } else { - self.unset(ArgSettings::HidePossibleValues) - } - } - - /// Specifies if the default value of an argument should be displayed in the help text or - /// not. Defaults to `false` (i.e. show default value) - /// - /// This is useful when default behavior of an arg is explained elsewhere in the help text. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("config") - /// .hide_default_value(true) - /// # ; - /// ``` - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("connect") - /// .arg(Arg::with_name("host") - /// .long("host") - /// .default_value("localhost") - /// .hide_default_value(true)); - /// - /// ``` - /// - /// If we were to run the above program with `--help` the `[default: localhost]` portion of - /// the help text would be omitted. - pub fn hide_default_value(self, hide: bool) -> Self { - if hide { - self.set(ArgSettings::HideDefaultValue) - } else { - self.unset(ArgSettings::HideDefaultValue) - } - } - - /// Specifies the index of a positional argument **starting at** 1. + /// Specifies the index of a positional argument **starting at** 1. /// /// **NOTE:** The index refers to position according to **other positional argument**. It does /// not define position in the argument list as a whole. @@ -1954,205 +1519,6 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Specifies that the argument may appear more than once. For flags, this results - /// in the number of occurrences of the flag being recorded. For example `-ddd` or `-d -d -d` - /// would count as three occurrences. For options there is a distinct difference in multiple - /// occurrences vs multiple values. - /// - /// For example, `--opt val1 val2` is one occurrence, but two values. Whereas - /// `--opt val1 --opt val2` is two occurrences. - /// - /// **WARNING:** - /// - /// Setting `multiple(true)` for an [option] with no other details, allows multiple values - /// **and** multiple occurrences because it isn't possible to have more occurrences than values - /// for options. Because multiple values are allowed, `--option val1 val2 val3` is perfectly - /// valid, be careful when designing a CLI where positional arguments are expected after a - /// option which accepts multiple values, as `clap` will continue parsing *values* until it - /// reaches the max or specific number of values defined, or another flag or option. - /// - /// **Pro Tip**: - /// - /// It's possible to define an option which allows multiple occurrences, but only one value per - /// occurrence. To do this use [`Arg::number_of_values(1)`] in coordination with - /// [`Arg::multiple(true)`]. - /// - /// **WARNING:** - /// - /// When using args with `multiple(true)` on [options] or [positionals] (i.e. those args that - /// accept values) and [subcommands], one needs to consider the possibility of an argument value - /// being the same as a valid subcommand. By default `clap` will parse the argument in question - /// as a value *only if* a value is possible at that moment. Otherwise it will be parsed as a - /// subcommand. In effect, this means using `multiple(true)` with no additional parameters and - /// a possible value that coincides with a subcommand name, the subcommand cannot be called - /// unless another argument is passed first. - /// - /// As an example, consider a CLI with an option `--ui-paths=...` and subcommand `signer` - /// - /// The following would be parsed as values to `--ui-paths`. - /// - /// ```notrust - /// $ program --ui-paths path1 path2 signer - /// ``` - /// - /// This is because `--ui-paths` accepts multiple values. `clap` will continue parsing values - /// until another argument is reached and it knows `--ui-paths` is done. - /// - /// By adding additional parameters to `--ui-paths` we can solve this issue. Consider adding - /// [`Arg::number_of_values(1)`] as discussed above. The following are all valid, and `signer` - /// is parsed as both a subcommand and a value in the second case. - /// - /// ```notrust - /// $ program --ui-paths path1 signer - /// $ program --ui-paths path1 --ui-paths signer signer - /// ``` - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("debug") - /// .short("d") - /// .multiple(true) - /// # ; - /// ``` - /// An example with flags - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("verbose") - /// .multiple(true) - /// .short("v")) - /// .get_matches_from(vec![ - /// "prog", "-v", "-v", "-v" // note, -vvv would have same result - /// ]); - /// - /// assert!(m.is_present("verbose")); - /// assert_eq!(m.occurrences_of("verbose"), 3); - /// ``` - /// - /// An example with options - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("file") - /// .multiple(true) - /// .takes_value(true) - /// .short("F")) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3" - /// ]); - /// - /// assert!(m.is_present("file")); - /// assert_eq!(m.occurrences_of("file"), 1); // notice only one occurrence - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// ``` - /// This is functionally equivalent to the example above - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("file") - /// .multiple(true) - /// .takes_value(true) - /// .short("F")) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" - /// ]); - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// - /// assert!(m.is_present("file")); - /// assert_eq!(m.occurrences_of("file"), 3); // Notice 3 occurrences - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// ``` - /// - /// A common mistake is to define an option which allows multiples, and a positional argument - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("file") - /// .multiple(true) - /// .takes_value(true) - /// .short("F")) - /// .arg(Arg::with_name("word") - /// .index(1)) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "file2", "file3", "word" - /// ]); - /// - /// assert!(m.is_present("file")); - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3", "word"]); // wait...what?! - /// assert!(!m.is_present("word")); // but we clearly used word! - /// ``` - /// The problem is clap doesn't know when to stop parsing values for "files". This is further - /// compounded by if we'd said `word -F file1 file2` it would have worked fine, so it would - /// appear to only fail sometimes...not good! - /// - /// A solution for the example above is to specify that `-F` only accepts one value, but is - /// allowed to appear multiple times - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("file") - /// .multiple(true) - /// .takes_value(true) - /// .number_of_values(1) - /// .short("F")) - /// .arg(Arg::with_name("word") - /// .index(1)) - /// .get_matches_from(vec![ - /// "prog", "-F", "file1", "-F", "file2", "-F", "file3", "word" - /// ]); - /// - /// assert!(m.is_present("file")); - /// let files: Vec<_> = m.values_of("file").unwrap().collect(); - /// assert_eq!(files, ["file1", "file2", "file3"]); - /// assert!(m.is_present("word")); - /// assert_eq!(m.value_of("word"), Some("word")); - /// ``` - /// As a final example, notice if we define [`Arg::number_of_values(1)`] and try to run the - /// problem example above, it would have been a runtime error with a pretty message to the - /// user :) - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("file") - /// .multiple(true) - /// .takes_value(true) - /// .number_of_values(1) - /// .short("F")) - /// .arg(Arg::with_name("word") - /// .index(1)) - /// .get_matches_from_safe(vec![ - /// "prog", "-F", "file1", "file2", "file3", "word" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - /// [option]: ./struct.Arg.html#method.takes_value - /// [options]: ./struct.Arg.html#method.takes_value - /// [subcommands]: ./struct.SubCommand.html - /// [positionals]: ./struct.Arg.html#method.index - /// [`Arg::number_of_values(1)`]: ./struct.Arg.html#method.number_of_values - /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple - pub fn multiple(self, multi: bool) -> Self { - if multi { - self.set(ArgSettings::Multiple) - } else { - self.unset(ArgSettings::Multiple) - } - } - /// Specifies a value that *stops* parsing multiple values of a give argument. By default when /// one sets [`multiple(true)`] on an argument, clap will continue parsing values for that /// argument until it reaches another valid argument, or one of the other more specific settings @@ -2198,159 +1564,14 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`min_values`]: ./struct.Arg.html#method.min_values /// [`number_of_values`]: ./struct.Arg.html#method.number_of_values /// [`max_values`]: ./struct.Arg.html#method.max_values - pub fn value_terminator(mut self, term: &'b str) -> Self { + pub fn value_terminator(mut self, term: &'help str) -> Self { self.setb(ArgSettings::TakesValue); - self.v.terminator = Some(term); + self.terminator = Some(term); self } - /// Specifies that an argument can be matched to all child [`SubCommand`]s. - /// - /// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands), however - /// their values once a user uses them will be propagated back up to parents. In effect, this - /// means one should *define* all global arguments at the top level, however it doesn't matter - /// where the user *uses* the global argument. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("debug") - /// .short("d") - /// .global(true) - /// # ; - /// ``` - /// - /// For example, assume an application with two subcommands, and you'd like to define a - /// `--verbose` flag that can be called on any of the subcommands and parent, but you don't - /// want to clutter the source with three duplicate [`Arg`] definitions. - /// - /// ```rust - /// # use clap::{App, Arg, SubCommand}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("verb") - /// .long("verbose") - /// .short("v") - /// .global(true)) - /// .subcommand(SubCommand::with_name("test")) - /// .subcommand(SubCommand::with_name("do-stuff")) - /// .get_matches_from(vec![ - /// "prog", "do-stuff", "--verbose" - /// ]); - /// - /// assert_eq!(m.subcommand_name(), Some("do-stuff")); - /// let sub_m = m.subcommand_matches("do-stuff").unwrap(); - /// assert!(sub_m.is_present("verb")); - /// ``` - /// [`SubCommand`]: ./struct.SubCommand.html - /// [required]: ./struct.Arg.html#method.required - /// [`ArgMatches`]: ./struct.ArgMatches.html - /// [`ArgMatches::is_present("flag")`]: ./struct.ArgMatches.html#method.is_present - /// [`Arg`]: ./struct.Arg.html - pub fn global(self, g: bool) -> Self { - if g { - self.set(ArgSettings::Global) - } else { - self.unset(ArgSettings::Global) - } - } - - /// Allows an argument to accept explicitly empty values. An empty value must be specified at - /// the command line with an explicit `""`, or `''` - /// - /// **NOTE:** Defaults to `true` (Explicitly empty values are allowed) - /// - /// **NOTE:** Implicitly sets [`Arg::takes_value(true)`] when set to `false` - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("file") - /// .long("file") - /// .empty_values(false) - /// # ; - /// ``` - /// The default is to allow empty values, such as `--option ""` would be an empty value. But - /// we can change to make empty values become an error. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .long("config") - /// .short("v") - /// .empty_values(false)) - /// .get_matches_from_safe(vec![ - /// "prog", "--config=" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); - /// ``` - /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value - pub fn empty_values(mut self, ev: bool) -> Self { - if ev { - self.set(ArgSettings::EmptyValues) - } else { - self = self.set(ArgSettings::TakesValue); - self.unset(ArgSettings::EmptyValues) - } - } - - /// Hides an argument from help message output. - /// - /// **NOTE:** Implicitly sets [`Arg::hidden_short_help(true)`] and [`Arg::hidden_long_help(true)`] - /// when set to true - /// - /// **NOTE:** This does **not** hide the argument from usage strings on error - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("debug") - /// .hidden(true) - /// # ; - /// ``` - /// Setting `hidden(true)` will hide the argument when displaying help text - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("cfg") - /// .long("config") - /// .hidden(true) - /// .help("Some help text describing the --config arg")) - /// .get_matches_from(vec![ - /// "prog", "--help" - /// ]); - /// ``` - /// - /// The above example displays - /// - /// ```notrust - /// helptest - /// - /// USAGE: - /// helptest [FLAGS] - /// - /// FLAGS: - /// -h, --help Prints help information - /// -V, --version Prints version information - /// ``` - /// [`Arg::hidden_short_help(true)`]: ./struct.Arg.html#method.hidden_short_help - /// [`Arg::hidden_long_help(true)`]: ./struct.Arg.html#method.hidden_long_help - pub fn hidden(self, h: bool) -> Self { - if h { - self.set(ArgSettings::Hidden) - } else { - self.unset(ArgSettings::Hidden) - } - } - - /// Specifies a list of possible values for this argument. At runtime, `clap` verifies that - /// only one of the specified values was used, or fails with an error message. + /// Specifies a list of possible values for this argument. At runtime, `clap` verifies that + /// only one of the specified values was used, or fails with an error message. /// /// **NOTE:** This setting only applies to [options] and [positional arguments] /// @@ -2388,7 +1609,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .long("mode") /// .takes_value(true) /// .possible_values(&["fast", "slow", "medium"])) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--mode", "wrong" /// ]); /// assert!(res.is_err()); @@ -2396,13 +1617,14 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [options]: ./struct.Arg.html#method.takes_value /// [positional arguments]: ./struct.Arg.html#method.index - pub fn possible_values(mut self, names: &[&'b str]) -> Self { - if let Some(ref mut vec) = self.v.possible_vals { + pub fn possible_values(mut self, names: &[&'help str]) -> Self { + self.setb(ArgSettings::TakesValue); + if let Some(ref mut vec) = self.possible_vals { for s in names { vec.push(s); } } else { - self.v.possible_vals = Some(names.iter().map(|s| *s).collect::>()); + self.possible_vals = Some(names.to_vec()); } self } @@ -2452,7 +1674,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .possible_value("fast") /// .possible_value("slow") /// .possible_value("medium")) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "--mode", "wrong" /// ]); /// assert!(res.is_err()); @@ -2460,68 +1682,16 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [options]: ./struct.Arg.html#method.takes_value /// [positional arguments]: ./struct.Arg.html#method.index - pub fn possible_value(mut self, name: &'b str) -> Self { - if let Some(ref mut vec) = self.v.possible_vals { + pub fn possible_value(mut self, name: &'help str) -> Self { + self.setb(ArgSettings::TakesValue); + if let Some(ref mut vec) = self.possible_vals { vec.push(name); } else { - self.v.possible_vals = Some(vec![name]); + self.possible_vals = Some(vec![name]); } self } - /// When used with [`Arg::possible_values`] it allows the argument value to pass validation even if - /// the case differs from that of the specified `possible_value`. - /// - /// **Pro Tip:** Use this setting with [`arg_enum!`] - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// # use std::ascii::AsciiExt; - /// let m = App::new("pv") - /// .arg(Arg::with_name("option") - /// .long("--option") - /// .takes_value(true) - /// .possible_value("test123") - /// .case_insensitive(true)) - /// .get_matches_from(vec![ - /// "pv", "--option", "TeSt123", - /// ]); - /// - /// assert!(m.value_of("option").unwrap().eq_ignore_ascii_case("test123")); - /// ``` - /// - /// This setting also works when multiple values can be defined: - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("pv") - /// .arg(Arg::with_name("option") - /// .short("-o") - /// .long("--option") - /// .takes_value(true) - /// .possible_value("test123") - /// .possible_value("test321") - /// .multiple(true) - /// .case_insensitive(true)) - /// .get_matches_from(vec![ - /// "pv", "--option", "TeSt123", "teST123", "tESt321" - /// ]); - /// - /// let matched_vals = m.values_of("option").unwrap().collect::>(); - /// assert_eq!(&*matched_vals, &["TeSt123", "teST123", "tESt321"]); - /// ``` - /// [`Arg::case_insensitive(true)`]: ./struct.Arg.html#method.possible_values - /// [`arg_enum!`]: ./macro.arg_enum.html - pub fn case_insensitive(self, ci: bool) -> Self { - if ci { - self.set(ArgSettings::CaseInsensitive) - } else { - self.unset(ArgSettings::CaseInsensitive) - } - } - /// Specifies the name of the [`ArgGroup`] the argument belongs to. /// /// # Examples @@ -2552,11 +1722,12 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert!(m.is_present("mode")); /// ``` /// [`ArgGroup`]: ./struct.ArgGroup.html - pub fn group(mut self, name: &'a str) -> Self { - if let Some(ref mut vec) = self.b.groups { + pub fn group(mut self, group_id: T) -> Self { + let name = group_id.key(); + if let Some(ref mut vec) = self.groups { vec.push(name); } else { - self.b.groups = Some(vec![name]); + self.groups = Some(vec![name]); } self } @@ -2592,13 +1763,13 @@ impl<'a, 'b> Arg<'a, 'b> { /// assert!(m.is_present("verbosity")); /// ``` /// [`ArgGroup`]: ./struct.ArgGroup.html - pub fn groups(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.b.groups { - for s in names { - vec.push(s); + pub fn groups(mut self, group_ids: &[T]) -> Self { + if let Some(ref mut vec) = self.groups { + for s in group_ids { + vec.push(s.key()); } } else { - self.b.groups = Some(names.into_iter().map(|s| *s).collect::>()); + self.groups = Some(group_ids.iter().map(Key::key).collect()); } self } @@ -2617,7 +1788,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("file") - /// .short("f") + /// .short('f') /// .number_of_values(3) /// # ; /// ``` @@ -2630,8 +1801,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("file") /// .takes_value(true) /// .number_of_values(2) - /// .short("F")) - /// .get_matches_from_safe(vec![ + /// .short('F')) + /// .try_get_matches_from(vec![ /// "prog", "-F", "file1" /// ]); /// @@ -2641,7 +1812,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn number_of_values(mut self, qty: u64) -> Self { self.setb(ArgSettings::TakesValue); - self.v.num_vals = Some(qty); + self.num_vals = Some(qty); self } @@ -2671,7 +1842,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("file") /// .index(1) /// .validator(has_at)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "some@file" /// ]); /// assert!(res.is_ok()); @@ -2681,11 +1852,14 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html /// [`Err(String)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err /// [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html - pub fn validator(mut self, f: F) -> Self + pub fn validator(mut self, f: F) -> Self where - F: Fn(String) -> Result<(), String> + 'static, + F: Fn(String) -> Result + 'static, + E: ToString, { - self.v.validator = Some(Rc::new(f)); + self.validator = Some(Rc::new(move |s| { + f(s).map(|_| ()).map_err(|e| e.to_string()) + })); self } @@ -2699,15 +1873,15 @@ impl<'a, 'b> Arg<'a, 'b> { /// # use clap::{App, Arg}; /// # use std::ffi::{OsStr, OsString}; /// # use std::os::unix::ffi::OsStrExt; - /// fn has_ampersand(v: &OsStr) -> Result<(), OsString> { + /// fn has_ampersand(v: &OsStr) -> Result<(), String> { /// if v.as_bytes().iter().any(|b| *b == b'&') { return Ok(()); } - /// Err(OsString::from("The value did not contain the required & sigil")) + /// Err(String::from("The value did not contain the required & sigil")) /// } /// let res = App::new("prog") /// .arg(Arg::with_name("file") /// .index(1) /// .validator_os(has_ampersand)) - /// .get_matches_from_safe(vec![ + /// .try_get_matches_from(vec![ /// "prog", "Fish & chips" /// ]); /// assert!(res.is_ok()); @@ -2719,11 +1893,11 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html /// [`Err(String)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err /// [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html - pub fn validator_os(mut self, f: F) -> Self + pub fn validator_os(mut self, f: F) -> Self where - F: Fn(&OsStr) -> Result<(), OsString> + 'static, + F: Fn(&OsStr) -> Result + 'static, { - self.v.validator_os = Some(Rc::new(f)); + self.validator_os = Some(Rc::new(move |s| f(s).map(|_| ()))); self } @@ -2742,7 +1916,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("file") - /// .short("f") + /// .short('f') /// .max_values(3) /// # ; /// ``` @@ -2755,8 +1929,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("file") /// .takes_value(true) /// .max_values(3) - /// .short("F")) - /// .get_matches_from_safe(vec![ + /// .short('F')) + /// .try_get_matches_from(vec![ /// "prog", "-F", "file1", "file2" /// ]); /// @@ -2774,8 +1948,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("file") /// .takes_value(true) /// .max_values(2) - /// .short("F")) - /// .get_matches_from_safe(vec![ + /// .short('F')) + /// .try_get_matches_from(vec![ /// "prog", "-F", "file1", "file2", "file3" /// ]); /// @@ -2785,7 +1959,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn max_values(mut self, qty: u64) -> Self { self.setb(ArgSettings::TakesValue); - self.v.max_vals = Some(qty); + self.setb(ArgSettings::MultipleValues); + self.max_vals = Some(qty); self } @@ -2805,7 +1980,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("file") - /// .short("f") + /// .short('f') /// .min_values(3) /// # ; /// ``` @@ -2818,8 +1993,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("file") /// .takes_value(true) /// .min_values(2) - /// .short("F")) - /// .get_matches_from_safe(vec![ + /// .short('F')) + /// .try_get_matches_from(vec![ /// "prog", "-F", "file1", "file2", "file3" /// ]); /// @@ -2837,8 +2012,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .arg(Arg::with_name("file") /// .takes_value(true) /// .min_values(2) - /// .short("F")) - /// .get_matches_from_safe(vec![ + /// .short('F')) + /// .try_get_matches_from(vec![ /// "prog", "-F", "file1" /// ]); /// @@ -2847,219 +2022,73 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn min_values(mut self, qty: u64) -> Self { - self.v.min_vals = Some(qty); - self.set(ArgSettings::TakesValue) + self.min_vals = Some(qty); + self.setting(ArgSettings::TakesValue) } - /// Specifies whether or not an argument should allow grouping of multiple values via a - /// delimiter. I.e. should `--option=val1,val2,val3` be parsed as three values (`val1`, `val2`, - /// and `val3`) or as a single value (`val1,val2,val3`). Defaults to using `,` (comma) as the - /// value delimiter for all arguments that accept values (options and positional arguments) - /// - /// **NOTE:** The default is `false`. When set to `true` the default [`Arg::value_delimiter`] - /// is the comma `,`. - /// - /// # Examples + /// Specifies the separator to use when values are clumped together, defaults to `,` (comma). /// - /// The following example shows the default behavior. + /// **NOTE:** implicitly sets [`Arg::use_delimiter(true)`] /// - /// ```rust - /// # use clap::{App, Arg}; - /// let delims = App::new("prog") - /// .arg(Arg::with_name("option") - /// .long("option") - /// .use_delimiter(true) - /// .takes_value(true)) - /// .get_matches_from(vec![ - /// "prog", "--option=val1,val2,val3", - /// ]); + /// **NOTE:** implicitly sets [`Arg::takes_value(true)`] /// - /// assert!(delims.is_present("option")); - /// assert_eq!(delims.occurrences_of("option"), 1); - /// assert_eq!(delims.values_of("option").unwrap().collect::>(), ["val1", "val2", "val3"]); - /// ``` - /// The next example shows the difference when turning delimiters off. This is the default - /// behavior + /// # Examples /// /// ```rust /// # use clap::{App, Arg}; - /// let nodelims = App::new("prog") - /// .arg(Arg::with_name("option") - /// .long("option") - /// .use_delimiter(false) - /// .takes_value(true)) + /// let m = App::new("prog") + /// .arg(Arg::with_name("config") + /// .short('c') + /// .long("config") + /// .value_delimiter(";")) /// .get_matches_from(vec![ - /// "prog", "--option=val1,val2,val3", + /// "prog", "--config=val1;val2;val3" /// ]); /// - /// assert!(nodelims.is_present("option")); - /// assert_eq!(nodelims.occurrences_of("option"), 1); - /// assert_eq!(nodelims.value_of("option").unwrap(), "val1,val2,val3"); + /// assert_eq!(m.values_of("config").unwrap().collect::>(), ["val1", "val2", "val3"]) /// ``` - /// [`Arg::value_delimiter`]: ./struct.Arg.html#method.value_delimiter - pub fn use_delimiter(mut self, d: bool) -> Self { - if d { - if self.v.val_delim.is_none() { - self.v.val_delim = Some(','); - } - self.setb(ArgSettings::TakesValue); - self.setb(ArgSettings::UseValueDelimiter); - self.unset(ArgSettings::ValueDelimiterNotSet) - } else { - self.v.val_delim = None; - self.unsetb(ArgSettings::UseValueDelimiter); - self.unset(ArgSettings::ValueDelimiterNotSet) - } + /// [`Arg::use_delimiter(true)`]: ./struct.Arg.html#method.use_delimiter + /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value + pub fn value_delimiter(mut self, d: &str) -> Self { + self.unsetb(ArgSettings::ValueDelimiterNotSet); + self.setb(ArgSettings::TakesValue); + self.setb(ArgSettings::UseValueDelimiter); + self.val_delim = Some( + d.chars() + .nth(0) + .expect("Failed to get value_delimiter from arg"), + ); + self } - /// Specifies that *multiple values* may only be set using the delimiter. This means if an - /// if an option is encountered, and no delimiter is found, it automatically assumed that no - /// additional values for that option follow. This is unlike the default, where it is generally - /// assumed that more values will follow regardless of whether or not a delimiter is used. + /// Specify multiple names for values of option arguments. These names are cosmetic only, used + /// for help and usage strings only. The names are **not** used to access arguments. The values + /// of the arguments are accessed in numeric order (i.e. if you specify two names `one` and + /// `two` `one` will be the first matched value, `two` will be the second). /// - /// **NOTE:** The default is `false`. + /// This setting can be very helpful when describing the type of input the user should be + /// using, such as `FILE`, `INTERFACE`, etc. Although not required, it's somewhat convention to + /// use all capital letters for the value name. /// - /// **NOTE:** Setting this to true implies [`Arg::use_delimiter(true)`] + /// **Pro Tip:** It may help to use [`Arg::next_line_help(true)`] if there are long, or + /// multiple value names in order to not throw off the help text alignment of all options. /// - /// **NOTE:** It's a good idea to inform the user that use of a delimiter is required, either - /// through help text or other means. + /// **NOTE:** This implicitly sets [`Arg::number_of_values`] if the number of value names is + /// greater than one. I.e. be aware that the number of "names" you set for the values, will be + /// the *exact* number of values required to satisfy this argument /// - /// # Examples + /// **NOTE:** implicitly sets [`Arg::takes_value(true)`] /// - /// These examples demonstrate what happens when `require_delimiter(true)` is used. Notice - /// everything works in this first example, as we use a delimiter, as expected. + /// **NOTE:** Does *not* require or imply [`Arg::multiple(true)`]. + /// + /// # Examples /// /// ```rust /// # use clap::{App, Arg}; - /// let delims = App::new("prog") - /// .arg(Arg::with_name("opt") - /// .short("o") - /// .takes_value(true) - /// .multiple(true) - /// .require_delimiter(true)) - /// .get_matches_from(vec![ - /// "prog", "-o", "val1,val2,val3", - /// ]); - /// - /// assert!(delims.is_present("opt")); - /// assert_eq!(delims.values_of("opt").unwrap().collect::>(), ["val1", "val2", "val3"]); - /// ``` - /// In this next example, we will *not* use a delimiter. Notice it's now an error. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::with_name("opt") - /// .short("o") - /// .takes_value(true) - /// .multiple(true) - /// .require_delimiter(true)) - /// .get_matches_from_safe(vec![ - /// "prog", "-o", "val1", "val2", "val3", - /// ]); - /// - /// assert!(res.is_err()); - /// let err = res.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::UnknownArgument); - /// ``` - /// What's happening is `-o` is getting `val1`, and because delimiters are required yet none - /// were present, it stops parsing `-o`. At this point it reaches `val2` and because no - /// positional arguments have been defined, it's an error of an unexpected argument. - /// - /// In this final example, we contrast the above with `clap`'s default behavior where the above - /// is *not* an error. - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let delims = App::new("prog") - /// .arg(Arg::with_name("opt") - /// .short("o") - /// .takes_value(true) - /// .multiple(true)) - /// .get_matches_from(vec![ - /// "prog", "-o", "val1", "val2", "val3", - /// ]); - /// - /// assert!(delims.is_present("opt")); - /// assert_eq!(delims.values_of("opt").unwrap().collect::>(), ["val1", "val2", "val3"]); - /// ``` - /// [`Arg::use_delimiter(true)`]: ./struct.Arg.html#method.use_delimiter - pub fn require_delimiter(mut self, d: bool) -> Self { - if d { - self = self.use_delimiter(true); - self.unsetb(ArgSettings::ValueDelimiterNotSet); - self.setb(ArgSettings::UseValueDelimiter); - self.set(ArgSettings::RequireDelimiter) - } else { - self = self.use_delimiter(false); - self.unsetb(ArgSettings::UseValueDelimiter); - self.unset(ArgSettings::RequireDelimiter) - } - } - - /// Specifies the separator to use when values are clumped together, defaults to `,` (comma). - /// - /// **NOTE:** implicitly sets [`Arg::use_delimiter(true)`] - /// - /// **NOTE:** implicitly sets [`Arg::takes_value(true)`] - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("config") - /// .short("c") - /// .long("config") - /// .value_delimiter(";")) - /// .get_matches_from(vec![ - /// "prog", "--config=val1;val2;val3" - /// ]); - /// - /// assert_eq!(m.values_of("config").unwrap().collect::>(), ["val1", "val2", "val3"]) - /// ``` - /// [`Arg::use_delimiter(true)`]: ./struct.Arg.html#method.use_delimiter - /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value - pub fn value_delimiter(mut self, d: &str) -> Self { - self.unsetb(ArgSettings::ValueDelimiterNotSet); - self.setb(ArgSettings::TakesValue); - self.setb(ArgSettings::UseValueDelimiter); - self.v.val_delim = Some( - d.chars() - .nth(0) - .expect("Failed to get value_delimiter from arg"), - ); - self - } - - /// Specify multiple names for values of option arguments. These names are cosmetic only, used - /// for help and usage strings only. The names are **not** used to access arguments. The values - /// of the arguments are accessed in numeric order (i.e. if you specify two names `one` and - /// `two` `one` will be the first matched value, `two` will be the second). - /// - /// This setting can be very helpful when describing the type of input the user should be - /// using, such as `FILE`, `INTERFACE`, etc. Although not required, it's somewhat convention to - /// use all capital letters for the value name. - /// - /// **Pro Tip:** It may help to use [`Arg::next_line_help(true)`] if there are long, or - /// multiple value names in order to not throw off the help text alignment of all options. - /// - /// **NOTE:** This implicitly sets [`Arg::number_of_values`] if the number of value names is - /// greater than one. I.e. be aware that the number of "names" you set for the values, will be - /// the *exact* number of values required to satisfy this argument - /// - /// **NOTE:** implicitly sets [`Arg::takes_value(true)`] - /// - /// **NOTE:** Does *not* require or imply [`Arg::multiple(true)`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("speed") - /// .short("s") - /// .value_names(&["fast", "slow"]) - /// # ; + /// Arg::with_name("speed") + /// .short('s') + /// .value_names(&["fast", "slow"]) + /// # ; /// ``` /// /// ```rust @@ -3091,13 +2120,13 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::number_of_values`]: ./struct.Arg.html#method.number_of_values /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple - pub fn value_names(mut self, names: &[&'b str]) -> Self { + pub fn value_names(mut self, names: &[&'help str]) -> Self { self.setb(ArgSettings::TakesValue); if self.is_set(ArgSettings::ValueDelimiterNotSet) { self.unsetb(ArgSettings::ValueDelimiterNotSet); self.setb(ArgSettings::UseValueDelimiter); } - if let Some(ref mut vals) = self.v.val_names { + if let Some(ref mut vals) = self.val_names { let mut l = vals.len(); for s in names { vals.insert(l, s); @@ -3108,7 +2137,7 @@ impl<'a, 'b> Arg<'a, 'b> { for (i, n) in names.iter().enumerate() { vm.insert(i, *n); } - self.v.val_names = Some(vm); + self.val_names = Some(vm); } self } @@ -3159,15 +2188,15 @@ impl<'a, 'b> Arg<'a, 'b> { /// [option]: ./struct.Arg.html#method.takes_value /// [positional]: ./struct.Arg.html#method.index /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value - pub fn value_name(mut self, name: &'b str) -> Self { + pub fn value_name(mut self, name: &'help str) -> Self { self.setb(ArgSettings::TakesValue); - if let Some(ref mut vals) = self.v.val_names { + if let Some(ref mut vals) = self.val_names { let l = vals.len(); vals.insert(l, name); } else { let mut vm = VecMap::new(); vm.insert(0, name); - self.v.val_names = Some(vm); + self.val_names = Some(vm); } self } @@ -3235,7 +2264,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`ArgMatches::is_present`]: ./struct.ArgMatches.html#method.is_present /// [`Arg::default_value_if`]: ./struct.Arg.html#method.default_value_if - pub fn default_value(self, val: &'a str) -> Self { + pub fn default_value(self, val: &'help str) -> Self { self.default_value_os(OsStr::from_bytes(val.as_bytes())) } @@ -3243,9 +2272,9 @@ impl<'a, 'b> Arg<'a, 'b> { /// only using [`OsStr`]s instead. /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html - pub fn default_value_os(mut self, val: &'a OsStr) -> Self { + pub fn default_value_os(mut self, val: &'help OsStr) -> Self { self.setb(ArgSettings::TakesValue); - self.v.default_val = Some(val); + self.default_val = Some(val); self } @@ -3345,9 +2374,14 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value - pub fn default_value_if(self, arg: &'a str, val: Option<&'b str>, default: &'b str) -> Self { + pub fn default_value_if( + self, + arg_id: T, + val: Option<&'help str>, + default: &'help str, + ) -> Self { self.default_value_if_os( - arg, + arg_id, val.map(str::as_bytes).map(OsStr::from_bytes), OsStr::from_bytes(default.as_bytes()), ) @@ -3357,20 +2391,21 @@ impl<'a, 'b> Arg<'a, 'b> { /// only using [`OsStr`]s instead. /// [`Arg::default_value_if`]: ./struct.Arg.html#method.default_value_if /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html - pub fn default_value_if_os( + pub fn default_value_if_os( mut self, - arg: &'a str, - val: Option<&'b OsStr>, - default: &'b OsStr, + arg_id: T, + val: Option<&'help OsStr>, + default: &'help OsStr, ) -> Self { + let arg = arg_id.key(); self.setb(ArgSettings::TakesValue); - if let Some(ref mut vm) = self.v.default_vals_ifs { + if let Some(ref mut vm) = self.default_vals_ifs { let l = vm.len(); vm.insert(l, (arg, val, default)); } else { let mut vm = VecMap::new(); vm.insert(0, (arg, val, default)); - self.v.default_vals_ifs = Some(vm); + self.default_vals_ifs = Some(vm); } self } @@ -3459,8 +2494,11 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value - pub fn default_value_ifs(mut self, ifs: &[(&'a str, Option<&'b str>, &'b str)]) -> Self { - for &(arg, val, default) in ifs { + pub fn default_value_ifs( + mut self, + ifs: &[(T, Option<&'help str>, &'help str)], + ) -> Self { + for (arg, val, default) in ifs { self = self.default_value_if_os( arg, val.map(str::as_bytes).map(OsStr::from_bytes), @@ -3475,9 +2513,12 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::default_value_ifs`]: ./struct.Arg.html#method.default_value_ifs /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html #[cfg_attr(feature = "lints", allow(explicit_counter_loop))] - pub fn default_value_ifs_os(mut self, ifs: &[(&'a str, Option<&'b OsStr>, &'b OsStr)]) -> Self { - for &(arg, val, default) in ifs { - self = self.default_value_if_os(arg, val, default); + pub fn default_value_ifs_os( + mut self, + ifs: &[(T, Option<&'help OsStr>, &'help OsStr)], + ) -> Self { + for (arg, val, default) in ifs { + self = self.default_value_if_os(arg.key(), *val, default); } self } @@ -3581,147 +2622,1153 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// assert_eq!(m.values_of("flag").unwrap().collect::>(), vec!["env1", "env2"]); /// ``` - /// [`ArgMatches::occurrences_of`]: ./struct.ArgMatches.html#method.occurrences_of - /// [`ArgMatches::value_of`]: ./struct.ArgMatches.html#method.value_of - /// [`ArgMatches::is_present`]: ./struct.ArgMatches.html#method.is_present - /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value - /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple - /// [`Arg::use_delimiter(true)`]: ./struct.Arg.html#method.use_delimiter - pub fn env(self, name: &'a str) -> Self { - self.env_os(OsStr::new(name)) - } + pub fn env(self, name: &'help str) -> Self { self.env_os(OsStr::new(name)) } /// Specifies that if the value is not passed in as an argument, that it should be retrieved /// from the environment if available in the exact same manner as [`Arg::env`] only using /// [`OsStr`]s instead. - pub fn env_os(mut self, name: &'a OsStr) -> Self { + pub fn env_os(mut self, name: &'help OsStr) -> Self { self.setb(ArgSettings::TakesValue); - self.v.env = Some((name, env::var_os(name))); + self.env = Some((name, env::var_os(name))); self } - /// @TODO @p2 @docs @release: write docs - pub fn hide_env_values(self, hide: bool) -> Self { - if hide { - self.set(ArgSettings::HideEnvValues) + /// Allows custom ordering of args within the help message. Args with a lower value will be + /// displayed first in the help message. This is helpful when one would like to emphasise + /// frequently used args, or prioritize those towards the top of the list. Duplicate values + /// **are** allowed. Args with duplicate display orders will be displayed in alphabetical + /// order. + /// + /// **NOTE:** The default is 999 for all arguments. + /// + /// **NOTE:** This setting is ignored for [positional arguments] which are always displayed in + /// [index] order. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("a") // Typically args are grouped alphabetically by name. + /// // Args without a display_order have a value of 999 and are + /// // displayed alphabetically with all other 999 valued args. + /// .long("long-option") + /// .short('o') + /// .takes_value(true) + /// .help("Some help and text")) + /// .arg(Arg::with_name("b") + /// .long("other-option") + /// .short('O') + /// .takes_value(true) + /// .display_order(1) // In order to force this arg to appear *first* + /// // all we have to do is give it a value lower than 999. + /// // Any other args with a value of 1 will be displayed + /// // alphabetically with this one...then 2 values, then 3, etc. + /// .help("I should be first!")) + /// .get_matches_from(vec![ + /// "prog", "--help" + /// ]); + /// ``` + /// + /// The above example displays the following help message + /// + /// ```notrust + /// cust-ord + /// + /// USAGE: + /// cust-ord [FLAGS] [OPTIONS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// + /// OPTIONS: + /// -O, --other-option I should be first! + /// -o, --long-option Some help and text + /// ``` + /// [positional arguments]: ./struct.Arg.html#method.index + /// [index]: ./struct.Arg.html#method.index + pub fn display_order(mut self, ord: usize) -> Self { + self.disp_ord = ord; + self + } + + /// Specifies that this arg is the last, or final, positional argument (i.e. has the highest + /// index) and is *only* able to be accessed via the `--` syntax (i.e. `$ prog args -- + /// last_arg`). Even, if no other arguments are left to parse, if the user omits the `--` syntax + /// they will receive an [`UnknownArgument`] error. Setting an argument to `.last(true)` also + /// allows one to access this arg early using the `--` syntax. Accessing an arg early, even with + /// the `--` syntax is otherwise not possible. + /// + /// **NOTE:** This will change the usage string to look like `$ prog [FLAGS] [-- ]` if + /// `ARG` is marked as `.last(true)`. + /// + /// **NOTE:** This setting will imply [`AppSettings::DontCollapseArgsInUsage`] because failing + /// to set this can make the usage string very confusing. + /// + /// **NOTE**: This setting only applies to positional arguments, and has no affect on FLAGS / + /// OPTIONS + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// **CAUTION:** Using this setting *and* having child subcommands is not + /// recommended with the exception of *also* using [`AppSettings::ArgsNegateSubcommands`] + /// (or [`AppSettings::SubcommandsNegateReqs`] if the argument marked `Last` is also + /// marked [`ArgSettings::Required`]) + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Arg, ArgSettings}; + /// Arg::with_name("args") + /// .setting(ArgSettings::Last) + /// # ; + /// ``` + /// + /// Setting [`Last`] ensures the arg has the highest [index] of all positional args + /// and requires that the `--` syntax be used to access it early. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("first")) + /// .arg(Arg::with_name("second")) + /// .arg(Arg::with_name("third") + /// .setting(ArgSettings::Last)) + /// .try_get_matches_from(vec![ + /// "prog", "one", "--", "three" + /// ]); + /// + /// assert!(res.is_ok()); + /// let m = res.unwrap(); + /// assert_eq!(m.value_of("third"), Some("three")); + /// assert!(m.value_of("second").is_none()); + /// ``` + /// + /// Even if the positional argument marked `Last` is the only argument left to parse, + /// failing to use the `--` syntax results in an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("first")) + /// .arg(Arg::with_name("second")) + /// .arg(Arg::with_name("third") + /// .setting(ArgSettings::Last)) + /// .try_get_matches_from(vec![ + /// "prog", "one", "two", "three" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// ``` + /// [index]: ./struct.Arg.html#method.index + /// [`AppSettings::DontCollapseArgsInUsage`]: ./enum.AppSettings.html#variant.DontCollapseArgsInUsage + /// [`AppSettings::ArgsNegateSubcommands`]: ./enum.AppSettings.html#variant.ArgsNegateSubcommands + /// [`AppSettings::SubcommandsNegateReqs`]: ./enum.AppSettings.html#variant.SubcommandsNegateReqs + /// [`ArgSettings::Required`]: ./enum.ArgSetings.html#variant.Required + /// [`UnknownArgument`]: ./enum.ErrorKind.html#variant.UnknownArgument + pub fn last(self, l: bool) -> Self { + if l { + self.setting(ArgSettings::Last) + } else { + self.unset_setting(ArgSettings::Last) + } + } + + /// Specifies that the argument is required by default. Required by default means it is + /// required, when no other conflicting rules or overrides have been evaluated. Conflicting + /// rules take precedence over being required. + /// + /// **Pro tip:** Flags (i.e. not positional, or arguments that take values) shouldn't be + /// required by default. This is because if a flag were to be required, it should simply be + /// implied. No additional information is required from user. Flags by their very nature are + /// simply boolean on/off switches. The only time a user *should* be required to use a flag + /// is if the operation is destructive in nature, and the user is essentially proving to you, + /// "Yes, I know what I'm doing." + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Arg, ArgSettings}; + /// Arg::with_name("config") + /// .setting(ArgSettings::Required) + /// # ; + /// ``` + /// + /// Setting [`Required`] requires that the argument be used at runtime. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .settings(&[ArgSettings::Required, ArgSettings::TakesValue]) + /// .long("config")) + /// .try_get_matches_from(vec![ + /// "prog", "--config", "file.conf" + /// ]); + /// + /// assert!(res.is_ok()); + /// ``` + /// + /// Not setting [`Required`] and then *not* supplying that argument at runtime is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings, ErrorKind}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .settings(&[ArgSettings::Required, ArgSettings::TakesValue]) + /// .long("config")) + /// .try_get_matches_from(vec![ + /// "prog" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// ``` + /// [`Required`]: ./enum.ArgSettings.html#variant.Required + pub fn required(self, r: bool) -> Self { + if r { + self.setting(ArgSettings::Required) + } else { + self.unset_setting(ArgSettings::Required) + } + } + + /// Specifies that the argument takes a value at run time. + /// + /// **NOTE:** values for arguments may be specified in any of the following methods + /// + /// * Using a space such as `-o value` or `--option value` + /// * Using an equals and no space such as `-o=value` or `--option=value` + /// * Use a short and no space such as `-ovalue` + /// + /// **NOTE:** By default, args which allow [multiple values] are delimited by commas, meaning + /// `--option=val1,val2,val3` is three values for the `--option` argument. If you wish to + /// change the delimiter to another character you can use [`Arg::value_delimiter(char)`], + /// alternatively you can turn delimiting values **OFF** by using + /// [`Arg::unset_setting(ArgSettings::UseValueDelimiter`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("config") + /// .setting(ArgSettings::TakesValue) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("mode") + /// .long("mode") + /// .setting(ArgSettings::TakesValue)) + /// .get_matches_from(vec![ + /// "prog", "--mode", "fast" + /// ]); + /// + /// assert!(m.is_present("mode")); + /// assert_eq!(m.value_of("mode"), Some("fast")); + /// ``` + /// [`Arg::value_delimiter(char)`]: ./struct.Arg.html#method.value_delimiter + /// [`Arg::unset_setting(ArgSettings::UseValueDelimiter`]: ./enum.ArgSettings.html#variant.UseValueDelimiter + /// [multiple values]: ./enum.ArgSettings.html#variant.MultipleValues + pub fn takes_value(self, tv: bool) -> Self { + if tv { + self.setting(ArgSettings::TakesValue) + } else { + self.unset_setting(ArgSettings::TakesValue) + } + } + + /// Allows values which start with a leading hyphen (`-`) + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// **WARNING**: Take caution when using this setting combined with + /// [`ArgSettings::MultipleValues`], as this becomes ambiguous `$ prog --arg -- -- val`. All + /// three `--, --, val` will be values when the user may have thought the second `--` would + /// constitute the normal, "Only positional args follow" idiom. To fix this, consider using + /// [`ArgSettings::MultipleOccurrences`] which only allows a single value at a time. + /// + /// **WARNING**: When building your CLIs, consider the effects of allowing leading hyphens and + /// the user passing in a value that matches a valid short. For example `prog -opt -F` where + /// `-F` is supposed to be a value, yet `-F` is *also* a valid short for another arg. Care should + /// should be taken when designing these args. This is compounded by the ability to "stack" + /// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid + /// shorts. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Arg, ArgSettings}; + /// Arg::with_name("pattern") + /// .setting(ArgSettings::AllowHyphenValues) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("pat") + /// .setting(ArgSettings::AllowHyphenValues) + /// .long("pattern")) + /// .get_matches_from(vec![ + /// "prog", "--pattern", "-file" + /// ]); + /// + /// assert_eq!(m.value_of("pat"), Some("-file")); + /// ``` + /// + /// Not setting [`Arg::allow_hyphen_values(true)`] and supplying a value which starts with a + /// hyphen is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("pat") + /// .setting(ArgSettings::TakesValue) + /// .long("pattern")) + /// .try_get_matches_from(vec![ + /// "prog", "--pattern", "-file" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// ``` + /// [`ArgSettings::AllowHyphenValues`]: ./enum.ArgSettings.html#variant.AllowHyphenValues + /// [`ArgSettings::MultipleValues`]: ./enum.ArgSettings.html#variant.MultipleValues + /// [`ArgSettings::MultipleOccurrences`]: ./enum.ArgSettings.html#variant.MultipleOccurrences + /// [`Arg::number_of_values(1)`]: ./struct.Arg.html#method.number_of_values + pub fn allow_hyphen_values(self, a: bool) -> Self { + if a { + self.setting(ArgSettings::AllowHyphenValues) + } else { + self.unset_setting(ArgSettings::AllowHyphenValues) + } + } + + /// Requires that options use the `--option=val` syntax (i.e. an equals between the option and + /// associated value) **Default:** `false` + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Arg, ArgSettings}; + /// Arg::with_name("config") + /// .long("config") + /// .setting(ArgSettings::RequireEquals) + /// # ; + /// ``` + /// + /// Setting [`RequireEquals`] requires that the option have an equals sign between + /// it and the associated value. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .setting(ArgSettings::RequireEquals) + /// .long("config")) + /// .try_get_matches_from(vec![ + /// "prog", "--config=file.conf" + /// ]); + /// + /// assert!(res.is_ok()); + /// ``` + /// + /// Setting [`RequireEquals`] and *not* supplying the equals will cause an error + /// unless [`ArgSettings::EmptyValues`] is set. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .setting(ArgSettings::RequireEquals) + /// .long("config")) + /// .try_get_matches_from(vec![ + /// "prog", "--config", "file.conf" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); + /// ``` + /// [`RequireEquals`]: ./enum.ArgSettings.html#variant.RequireEquals + /// [`ArgSettings::EmptyValues`]: ./enum.ArgSettings.html#variant.EmptyValues + /// [`ArgSettings::EmptyValues`]: ./enum.ArgSettings.html#variant.TakesValue + pub fn require_equals(mut self, r: bool) -> Self { + if r { + self.unsetb(ArgSettings::AllowEmptyValues); + self.setting(ArgSettings::RequireEquals) + } else { + self.unset_setting(ArgSettings::RequireEquals) + } + } + + /// Specifies that an argument can be matched to all child [``]s. + /// + /// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands), however + /// their values once a user uses them will be propagated back up to parents. In effect, this + /// means one should *define* all global arguments at the top level, however it doesn't matter + /// where the user *uses* the global argument. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("debug") + /// .short('d') + /// .global(true) + /// # ; + /// ``` + /// + /// For example, assume an appliction with two subcommands, and you'd like to define a + /// `--verbose` flag that can be called on any of the subcommands and parent, but you don't + /// want to clutter the source with three duplicate [`Arg`] definitions. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("verb") + /// .long("verbose") + /// .short('v') + /// .global(true)) + /// .subcommand(App::new("test")) + /// .subcommand(App::new("do-stuff")) + /// .get_matches_from(vec![ + /// "prog", "do-stuff", "--verbose" + /// ]); + /// + /// assert_eq!(m.subcommand_name(), Some("do-stuff")); + /// let sub_m = m.subcommand_matches("do-stuff").unwrap(); + /// assert!(sub_m.is_present("verb")); + /// ``` + /// [``]: ./struct.App.html#method.subcommand + /// [required]: ./enum.ArgSettings.html#variant.Required + /// [`ArgMatches`]: ./struct.ArgMatches.html + /// [`ArgMatches::is_present("flag")`]: ./struct.ArgMatches.html#method.is_present + /// [`Arg`]: ./struct.Arg.html + pub fn global(mut self, g: bool) -> Self { + self.global = g; + self + } + + /// Specifies that *multiple values* may only be set using the delimiter. This means if an + /// if an option is encountered, and no delimiter is found, it automatically assumed that no + /// additional values for that option follow. This is unlike the default, where it is generally + /// assumed that more values will follow regardless of whether or not a delimiter is used. + /// + /// **NOTE:** The default is `false`. + /// + /// **NOTE:** Setting this implies [`ArgSettings::UseValueDelimiter`] and + /// [`ArgSettings::TakesValue`] + /// + /// **NOTE:** It's a good idea to inform the user that use of a delimiter is required, either + /// through help text or other means. + /// + /// # Examples + /// + /// These examples demonstrate what happens when `require_delimiter(true)` is used. Notice + /// everything works in this first example, as we use a delimiter, as expected. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let delims = App::new("prog") + /// .arg(Arg::with_name("opt") + /// .short('o') + /// .settings(&[ArgSettings::RequireDelimiter, ArgSettings::MultipleValues])) + /// .get_matches_from(vec![ + /// "prog", "-o", "val1,val2,val3", + /// ]); + /// + /// assert!(delims.is_present("opt")); + /// assert_eq!(delims.values_of("opt").unwrap().collect::>(), ["val1", "val2", "val3"]); + /// ``` + /// In this next example, we will *not* use a delimiter. Notice it's now an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("opt") + /// .short('o') + /// .setting(ArgSettings::RequireDelimiter)) + /// .try_get_matches_from(vec![ + /// "prog", "-o", "val1", "val2", "val3", + /// ]); + /// + /// assert!(res.is_err()); + /// let err = res.unwrap_err(); + /// assert_eq!(err.kind, ErrorKind::UnknownArgument); + /// ``` + /// What's happening is `-o` is getting `val1`, and because delimiters are required yet none + /// were present, it stops parsing `-o`. At this point it reaches `val2` and because no + /// positional arguments have been defined, it's an error of an unexpected argument. + /// + /// In this final example, we contrast the above with `clap`'s default behavior where the above + /// is *not* an error. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let delims = App::new("prog") + /// .arg(Arg::with_name("opt") + /// .short('o') + /// .setting(ArgSettings::MultipleValues)) + /// .get_matches_from(vec![ + /// "prog", "-o", "val1", "val2", "val3", + /// ]); + /// + /// assert!(delims.is_present("opt")); + /// assert_eq!(delims.values_of("opt").unwrap().collect::>(), ["val1", "val2", "val3"]); + /// ``` + /// [`ArgSettings::UseValueDelimiter`]: ./enum.ArgSettings.html#variant.UseValueDelimiter + /// [`ArgSettings::TakesValue`]: ./enum.ArgSettings.html#variant.TakesValue + pub fn require_delimiter(mut self, d: bool) -> Self { + if d { + self.setb(ArgSettings::UseValueDelimiter); + self.unsetb(ArgSettings::ValueDelimiterNotSet); + self.setb(ArgSettings::UseValueDelimiter); + self.setting(ArgSettings::RequireDelimiter) + } else { + self.unsetb(ArgSettings::UseValueDelimiter); + self.unsetb(ArgSettings::UseValueDelimiter); + self.unset_setting(ArgSettings::RequireDelimiter) + } + } + + /// Specifies if the possible values of an argument should be displayed in the help text or + /// not. Defaults to `false` (i.e. show possible values) + /// + /// This is useful for args with many values, or ones which are explained elsewhere in the + /// help text. + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("config") + /// .setting(ArgSettings::HidePossibleValues) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("mode") + /// .long("mode") + /// .possible_values(&["fast", "slow"]) + /// .setting(ArgSettings::HidePossibleValues)); + /// ``` + /// If we were to run the above program with `--help` the `[values: fast, slow]` portion of + /// the help text would be omitted. + pub fn hide_possible_values(self, hide: bool) -> Self { + if hide { + self.setting(ArgSettings::HidePossibleValues) + } else { + self.unset_setting(ArgSettings::HidePossibleValues) + } + } + + /// Specifies that the default value of an argument should not be displayed in the help text. + /// + /// This is useful when default behavior of an arg is explained elsewhere in the help text. + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("config") + /// .setting(ArgSettings::HideDefaultValue) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("connect") + /// .arg(Arg::with_name("host") + /// .long("host") + /// .default_value("localhost") + /// .setting(ArgSettings::HideDefaultValue)); + /// + /// ``` + /// + /// If we were to run the above program with `--help` the `[default: localhost]` portion of + /// the help text would be omitted. + pub fn hide_default_value(self, hide: bool) -> Self { + if hide { + self.setting(ArgSettings::HideDefaultValue) + } else { + self.unset_setting(ArgSettings::HideDefaultValue) + } + } + + /// Hides an argument from help message output. + /// + /// **NOTE:** This does **not** hide the argument from usage strings on error + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("debug") + /// .setting(ArgSettings::Hidden) + /// # ; + /// ``` + /// Setting `Hidden` will hide the argument when displaying help text + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .setting(ArgSettings::Hidden) + /// .help("Some help text describing the --config arg")) + /// .get_matches_from(vec![ + /// "prog", "--help" + /// ]); + /// ``` + /// + /// The above example displays + /// + /// ```notrust + /// helptest + /// + /// USAGE: + /// helptest [FLAGS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// ``` + pub fn hidden(self, h: bool) -> Self { + if h { + self.setting(ArgSettings::Hidden) + } else { + self.unset_setting(ArgSettings::Hidden) + } + } + + /// When used with [`Arg::possible_values`] it allows the argument value to pass validation even + /// if the case differs from that of the specified `possible_value`. + /// + /// **Pro Tip:** Use this setting with [`arg_enum!`] + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("pv") + /// .arg(Arg::with_name("option") + /// .long("--option") + /// .setting(ArgSettings::IgnoreCase) + /// .possible_value("test123")) + /// .get_matches_from(vec![ + /// "pv", "--option", "TeSt123", + /// ]); + /// + /// assert!(m.value_of("option").unwrap().eq_ignore_ascii_case("test123")); + /// ``` + /// + /// This setting also works when multiple values can be defined: + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("pv") + /// .arg(Arg::with_name("option") + /// .short('o') + /// .long("--option") + /// .settings(&[ArgSettings::IgnoreCase, ArgSettings::MultipleValues]) + /// .possible_value("test123") + /// .possible_value("test321")) + /// .get_matches_from(vec![ + /// "pv", "--option", "TeSt123", "teST123", "tESt321" + /// ]); + /// + /// let matched_vals = m.values_of("option").unwrap().collect::>(); + /// assert_eq!(&*matched_vals, &["TeSt123", "teST123", "tESt321"]); + /// ``` + /// [`arg_enum!`]: ./macro.arg_enum.html + pub fn case_insensitive(self, ci: bool) -> Self { + if ci { + self.setting(ArgSettings::IgnoreCase) + } else { + self.unset_setting(ArgSettings::IgnoreCase) + } + } + + /// Specifies that an argument should allow grouping of multiple values via a + /// delimiter. I.e. should `--option=val1,val2,val3` be parsed as three values (`val1`, `val2`, + /// and `val3`) or as a single value (`val1,val2,val3`). Defaults to using `,` (comma) as the + /// value delimiter for all arguments that accept values (options and positional arguments) + /// + /// **NOTE:** When this setting is used, it will default [`Arg::value_delimiter`] + /// to the comma `,`. + /// + /// **NOTE:** Implicitly sets [`ArgSettings::TakesValue`] + /// + /// # Examples + /// + /// The following example shows the default behavior. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let delims = App::new("prog") + /// .arg(Arg::with_name("option") + /// .long("option") + /// .setting(ArgSettings::UseValueDelimiter) + /// .takes_value(true)) + /// .get_matches_from(vec![ + /// "prog", "--option=val1,val2,val3", + /// ]); + /// + /// assert!(delims.is_present("option")); + /// assert_eq!(delims.occurrences_of("option"), 1); + /// assert_eq!(delims.values_of("option").unwrap().collect::>(), ["val1", "val2", "val3"]); + /// ``` + /// The next example shows the difference when turning delimiters off. This is the default + /// behavior + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let nodelims = App::new("prog") + /// .arg(Arg::with_name("option") + /// .long("option") + /// .setting(ArgSettings::TakesValue)) + /// .get_matches_from(vec![ + /// "prog", "--option=val1,val2,val3", + /// ]); + /// + /// assert!(nodelims.is_present("option")); + /// assert_eq!(nodelims.occurrences_of("option"), 1); + /// assert_eq!(nodelims.value_of("option").unwrap(), "val1,val2,val3"); + /// ``` + /// [`Arg::value_delimiter`]: ./struct.Arg.html#method.value_delimiter + pub fn use_delimiter(mut self, d: bool) -> Self { + if d { + if self.val_delim.is_none() { + self.val_delim = Some(','); + } + self.setb(ArgSettings::TakesValue); + self.setb(ArgSettings::UseValueDelimiter); + self.unset_setting(ArgSettings::ValueDelimiterNotSet) + } else { + self.val_delim = None; + self.unsetb(ArgSettings::UseValueDelimiter); + self.unset_setting(ArgSettings::ValueDelimiterNotSet) + } + } + + /// Specifies that any values inside the associated ENV variables of an argument should not be + /// displayed in the help text. + /// + /// This is useful when ENV vars contain sensitive values. + /// + /// **NOTE:** Setting this implies [`ArgSettings::TakesValue`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("config") + /// .setting(ArgSettings::HideDefaultValue) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("connect") + /// .arg(Arg::with_name("host") + /// .long("host") + /// .env("CONNECT") + /// .setting(ArgSettings::HideEnvValues)); + /// + /// ``` + /// + /// If we were to run the above program with `$ CONNECT=super_secret connect --help` the + /// `[default: CONNECT=super_secret]` portion of the help text would be omitted. + pub fn hide_env_values(self, hide: bool) -> Self { + if hide { + self.setting(ArgSettings::HideEnvValues) + } else { + self.unset_setting(ArgSettings::HideEnvValues) + } + } + + /// When set to `true` the help string will be displayed on the line after the argument and + /// indented once. This can be helpful for arguments with very long or complex help messages. + /// This can also be helpful for arguments with very long flag names, or many/long value names. + /// + /// **NOTE:** To apply this setting to all arguments consider using + /// [`AppSettings::NextLineHelp`] + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("opt") + /// .long("long-option-flag") + /// .short('o') + /// .settings(&[ArgSettings::TakesValue, ArgSettings::NextLineHelp]) + /// .value_names(&["value1", "value2"]) + /// .help("Some really long help and complex\n\ + /// help that makes more sense to be\n\ + /// on a line after the option")) + /// .get_matches_from(vec![ + /// "prog", "--help" + /// ]); + /// ``` + /// + /// The above example displays the following help message + /// + /// ```notrust + /// nlh + /// + /// USAGE: + /// nlh [FLAGS] [OPTIONS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// + /// OPTIONS: + /// -o, --long-option-flag + /// Some really long help and complex + /// help that makes more sense to be + /// on a line after the option + /// ``` + /// [`AppSettings::NextLineHelp`]: ./enum.AppSettings.html#variant.NextLineHelp + pub fn next_line_help(mut self, nlh: bool) -> Self { + if nlh { + self.setb(ArgSettings::NextLineHelp); + } else { + self.unsetb(ArgSettings::NextLineHelp); + } + self + } + + /// Specifies that the argument may have an unknown number of multiple values. Without any other + /// settings, this argument may appear only *once*. + /// + /// For example, `--opt val1 val2` is allowed, but `--opt val1 val2 --opt val3` is not. + /// + /// **NOTE:** Implicitly sets [`ArgSettings::TakesValue`] + /// + /// **WARNING:** + /// + /// Setting `MultipleValues` for an argument that takes a value, but with no other details can + /// be dangerous in some circumstances. Because multiple values are allowed, + /// `--option val1 val2 val3` is perfectly valid. Be careful when designing a CLI where + /// positional arguments are *also* expected as `clap` will continue parsing *values* until one + /// of the following happens: + /// + /// * It reaches the [maximum number of values] + /// * It reaches a [specific number of values] + /// * It finds another flag or option (i.e. something that starts with a `-`) + /// + /// **WARNING:** + /// + /// When using args with `MultipleValues` and [subcommands], one needs to consider the + /// posibility of an argument value being the same as a valid subcommand. By default `clap` will + /// parse the argument in question as a value *only if* a value is possible at that moment. + /// Otherwise it will be parsed as a subcommand. In effect, this means using `MultipleValues` with no + /// additional parameters and a value that coincides with a subcommand name, the subcommand + /// cannot be called unless another argument is passed between them. + /// + /// As an example, consider a CLI with an option `--ui-paths=...` and subcommand `signer` + /// + /// The following would be parsed as values to `--ui-paths`. + /// + /// ```notrust + /// $ program --ui-paths path1 path2 signer + /// ``` + /// + /// This is because `--ui-paths` accepts multiple values. `clap` will continue parsing values + /// until another argument is reached and it knows `--ui-paths` is done parsing. + /// + /// By adding additional parameters to `--ui-paths` we can solve this issue. Consider adding + /// [`Arg::number_of_values(1)`] or using *only* [`MultipleOccurrences`]. The following are all + /// valid, and `signer` is parsed as a subcommand in the first case, but a value in the second + /// case. + /// + /// ```notrust + /// $ program --ui-paths path1 signer + /// $ program --ui-paths path1 --ui-paths signer signer + /// ``` + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("debug") + /// .short('d') + /// .setting(ArgSettings::MultipleValues) + /// # ; + /// ``` + /// An example with flags + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("verbose") + /// .setting(ArgSettings::MultipleOccurrences) + /// .short('v')) + /// .get_matches_from(vec![ + /// "prog", "-v", "-v", "-v" // note, -vvv would have same result + /// ]); + /// + /// assert!(m.is_present("verbose")); + /// assert_eq!(m.occurrences_of("verbose"), 3); + /// ``` + /// + /// An example with options + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("file") + /// .setting(ArgSettings::MultipleValues) // implies TakesValue + /// .short('F')) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "file2", "file3" + /// ]); + /// + /// assert!(m.is_present("file")); + /// assert_eq!(m.occurrences_of("file"), 1); // notice only one occurrence + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// ``` + /// Although `MultipleVlaues` has been specified, we cannot use the argument more than once. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("file") + /// .setting(ArgSettings::MultipleValues) // implies TakesValue + /// .short('F')) + /// .try_get_matches_from(vec![ + /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" + /// ]); + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage) + /// ``` + /// + /// A common mistake is to define an option which allows multiple values, and a positional + /// argument. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("file") + /// .setting(ArgSettings::MultipleValues) // implies TakesValue + /// .short('F')) + /// .arg(Arg::with_name("word") + /// .index(1)) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "file2", "file3", "word" + /// ]); + /// + /// assert!(m.is_present("file")); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3", "word"]); // wait...what?! + /// assert!(!m.is_present("word")); // but we clearly used word! + /// ``` + /// The problem is `clap` doesn't know when to stop parsing values for "files". This is further + /// compounded by if we'd said `word -F file1 file2` it would have worked fine, so it would + /// appear to only fail sometimes...not good! + /// + /// A solution for the example above is to limit how many values with a [maxium], or [specific] + /// number, or to say [`MultipleOccurrences`] is ok, but multiple values is not. + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("file") + /// .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]) + /// .short('F')) + /// .arg(Arg::with_name("word") + /// .index(1)) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "-F", "file2", "-F", "file3", "word" + /// ]); + /// + /// assert!(m.is_present("file")); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// assert!(m.is_present("word")); + /// assert_eq!(m.value_of("word"), Some("word")); + /// ``` + /// As a final example, let's fix the above error and get a pretty message to the user :) + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("file") + /// .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]) + /// .short('F')) + /// .arg(Arg::with_name("word") + /// .index(1)) + /// .try_get_matches_from(vec![ + /// "prog", "-F", "file1", "file2", "file3", "word" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// ``` + /// [option]: ./enum.ArgSettings.html#variant.TakesValue + /// [options]: ./enum.ArgSettings.html#variant.TakesValue + /// [subcommands]: ./struct.App.html#method.subcommand + /// [positionals]: ./struct.Arg.html#method.index + /// [`Arg::number_of_values(1)`]: ./struct.Arg.html#method.number_of_values + /// [`MultipleOccurrences`]: ./enum.ArgSettings.html#variant.MultipleOccurrences + /// [`MultipleValues`]: ./enum.ArgSettings.html#variant.MultipleValues + /// [maximum number of values]: ./struct.Arg.html#method.max_values + /// [specific number of values]: ./struct.Arg.html#method.number_of_values + /// [maximum]: ./struct.Arg.html#method.max_values + /// [specific]: ./struct.Arg.html#method.number_of_values + pub fn multiple(mut self, multi: bool) -> Self { + if multi { + self.setb(ArgSettings::MultipleOccurrences); + self.setting(ArgSettings::MultipleValues) } else { - self.unset(ArgSettings::HideEnvValues) + self.unsetb(ArgSettings::MultipleOccurrences); + self.unset_setting(ArgSettings::MultipleValues) } } - /// When set to `true` the help string will be displayed on the line after the argument and - /// indented once. This can be helpful for arguments with very long or complex help messages. - /// This can also be helpful for arguments with very long flag names, or many/long value names. + /// Allows an argument to accept explicitly empty values. An empty value must be specified at + /// the command line with an explicit `""`, `''`, or `--option=` /// - /// **NOTE:** To apply this setting to all arguments consider using - /// [`AppSettings::NextLineHelp`] + /// **NOTE:** By default empty values are *not* allowed + /// + /// **NOTE:** Implicitly sets [`ArgSettings::TakesValue`] /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") - /// .arg(Arg::with_name("opt") - /// .long("long-option-flag") - /// .short("o") - /// .takes_value(true) - /// .value_names(&["value1", "value2"]) - /// .help("Some really long help and complex\n\ - /// help that makes more sense to be\n\ - /// on a line after the option") - /// .next_line_help(true)) - /// .get_matches_from(vec![ - /// "prog", "--help" - /// ]); + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("file") + /// .long("file") + /// .setting(ArgSettings::AllowEmptyValues) + /// # ; /// ``` + /// The default is to *not* allow empty values. /// - /// The above example displays the following help message - /// - /// ```notrust - /// nlh + /// ```rust + /// # use clap::{App, Arg, ErrorKind, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .short('v') + /// .setting(ArgSettings::TakesValue)) + /// .try_get_matches_from(vec![ + /// "prog", "--config=" + /// ]); /// - /// USAGE: - /// nlh [FLAGS] [OPTIONS] + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); + /// ``` + /// By adding this setting, we can allow empty values /// - /// FLAGS: - /// -h, --help Prints help information - /// -V, --version Prints version information + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let res = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .short('v') + /// .setting(ArgSettings::AllowEmptyValues)) // implies TakesValue + /// .try_get_matches_from(vec![ + /// "prog", "--config=" + /// ]); /// - /// OPTIONS: - /// -o, --long-option-flag - /// Some really long help and complex - /// help that makes more sense to be - /// on a line after the option + /// assert!(res.is_ok()); + /// assert_eq!(res.unwrap().value_of("config"), None); /// ``` - /// [`AppSettings::NextLineHelp`]: ./enum.AppSettings.html#variant.NextLineHelp - pub fn next_line_help(mut self, nlh: bool) -> Self { - if nlh { - self.setb(ArgSettings::NextLineHelp); + /// [`ArgSettings::TakesValue`]: ./enum.ArgSettings.html#variant.TakesValue + pub fn multiple_values(self, multi: bool) -> Self { + if multi { + self.setting(ArgSettings::MultipleValues) } else { - self.unsetb(ArgSettings::NextLineHelp); + self.unset_setting(ArgSettings::MultipleValues) } - self } - /// Allows custom ordering of args within the help message. Args with a lower value will be - /// displayed first in the help message. This is helpful when one would like to emphasise - /// frequently used args, or prioritize those towards the top of the list. Duplicate values - /// **are** allowed. Args with duplicate display orders will be displayed in alphabetical - /// order. - /// - /// **NOTE:** The default is 999 for all arguments. + /// Specifies that the argument may appear more than once. + /// For flags, this results + /// in the number of occurrences of the flag being recorded. For example `-ddd` or `-d -d -d` + /// would count as three occurrences. For options or arguments that take a value, this + /// *does not* affect how many values they can accept. (i.e. only one at a time is allowed) /// - /// **NOTE:** This setting is ignored for [positional arguments] which are always displayed in - /// [index] order. + /// For example, `--opt val1 --opt val2` is allowed, but `--opt val1 val2` is not. /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{App, Arg, ArgSettings}; + /// Arg::with_name("debug") + /// .short('d') + /// .setting(ArgSettings::MultipleOccurrences) + /// # ; + /// ``` + /// An example with flags + /// + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; /// let m = App::new("prog") - /// .arg(Arg::with_name("a") // Typically args are grouped alphabetically by name. - /// // Args without a display_order have a value of 999 and are - /// // displayed alphabetically with all other 999 valued args. - /// .long("long-option") - /// .short("o") - /// .takes_value(true) - /// .help("Some help and text")) - /// .arg(Arg::with_name("b") - /// .long("other-option") - /// .short("O") - /// .takes_value(true) - /// .display_order(1) // In order to force this arg to appear *first* - /// // all we have to do is give it a value lower than 999. - /// // Any other args with a value of 1 will be displayed - /// // alphabetically with this one...then 2 values, then 3, etc. - /// .help("I should be first!")) + /// .arg(Arg::with_name("verbose") + /// .setting(ArgSettings::MultipleOccurrences) + /// .short('v')) /// .get_matches_from(vec![ - /// "prog", "--help" + /// "prog", "-v", "-v", "-v" // note, -vvv would have same result /// ]); - /// ``` - /// - /// The above example displays the following help message /// - /// ```notrust - /// cust-ord + /// assert!(m.is_present("verbose")); + /// assert_eq!(m.occurrences_of("verbose"), 3); + /// ``` /// - /// USAGE: - /// cust-ord [FLAGS] [OPTIONS] + /// An example with options /// - /// FLAGS: - /// -h, --help Prints help information - /// -V, --version Prints version information + /// ```rust + /// # use clap::{App, Arg, ArgSettings}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("file") + /// .settings(&[ArgSettings::MultipleOccurrences, ArgSettings::TakesValue]) + /// .short('F')) + /// .get_matches_from(vec![ + /// "prog", "-F", "file1", "-F", "file2", "-F", "file3" + /// ]); /// - /// OPTIONS: - /// -O, --other-option I should be first! - /// -o, --long-option Some help and text + /// assert!(m.is_present("file")); + /// assert_eq!(m.occurrences_of("file"), 3); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); /// ``` - /// [positional arguments]: ./struct.Arg.html#method.index - /// [index]: ./struct.Arg.html#method.index - pub fn display_order(mut self, ord: usize) -> Self { - self.s.disp_ord = ord; - self + /// [option]: ./enum.ArgSettings.html#variant.TakesValue + /// [options]: ./enum.ArgSettings.html#variant.TakesValue + /// [subcommands]: ./struct.App.html#method.subcommand + /// [positionals]: ./struct.Arg.html#method.index + /// [`Arg::number_of_values(1)`]: ./struct.Arg.html#method.number_of_values + /// [`MultipleOccurrences`]: ./enum.ArgSettings.html#variant.MultipleOccurrences + /// [`MultipleValues`]: ./enum.ArgSettings.html#variant.MultipleValues + /// [maximum number of values]: ./struct.Arg.html#method.max_values + /// [specific number of values]: ./struct.Arg.html#method.number_of_values + /// [maximum]: ./struct.Arg.html#method.max_values + /// [specific]: ./struct.Arg.html#method.number_of_values + pub fn multiple_occurrences(self, multi: bool) -> Self { + if multi { + self.setting(ArgSettings::MultipleOccurrences) + } else { + self.unset_setting(ArgSettings::MultipleOccurrences) + } } /// Indicates that all parameters passed after this should not be parsed @@ -3743,9 +3790,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// [`Arg::allow_hyphen_values(true)`]: ./struct.Arg.html#method.allow_hyphen_values /// [`Arg::last(true)`]: ./struct.Arg.html#method.last /// [`AppSettings::TrailingVarArg`]: ./enum.AppSettings.html#variant.TrailingVarArg - pub fn raw(self, raw: bool) -> Self { - self.multiple(raw).allow_hyphen_values(raw).last(raw) - } + pub fn raw(self, raw: bool) -> Self { self.multiple(raw).allow_hyphen_values(raw).last(raw) } /// Hides an argument from short help message output. /// @@ -3818,9 +3863,9 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` pub fn hidden_short_help(self, hide: bool) -> Self { if hide { - self.set(ArgSettings::HiddenShortHelp) + self.setting(ArgSettings::HiddenShortHelp) } else { - self.unset(ArgSettings::HiddenShortHelp) + self.unset_setting(ArgSettings::HiddenShortHelp) } } @@ -3895,60 +3940,433 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` pub fn hidden_long_help(self, hide: bool) -> Self { if hide { - self.set(ArgSettings::HiddenLongHelp) + self.setting(ArgSettings::HiddenLongHelp) } else { - self.unset(ArgSettings::HiddenLongHelp) + self.unset_setting(ArgSettings::HiddenLongHelp) } } - /// Checks if one of the [`ArgSettings`] settings is set for the argument. - /// + // @TODO @docs @v3-beta: write better docs as ArgSettings is now critical + /// Checks if one of the [`ArgSettings`] is set for the argument /// [`ArgSettings`]: ./enum.ArgSettings.html - pub fn is_set(&self, s: ArgSettings) -> bool { - self.b.is_set(s) - } + pub fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) } - /// Sets one of the [`ArgSettings`] settings for the argument. - /// + /// Sets one of the [`ArgSettings`] settings for the argument /// [`ArgSettings`]: ./enum.ArgSettings.html - pub fn set(mut self, s: ArgSettings) -> Self { + pub fn setting(mut self, s: ArgSettings) -> Self { self.setb(s); self } - /// Unsets one of the [`ArgSettings`] settings for the argument. - /// + // @TODO @docs @v3-beta: write better docs as ArgSettings is now critical + /// Sets multiple [`ArgSettings`] for the argument /// [`ArgSettings`]: ./enum.ArgSettings.html - pub fn unset(mut self, s: ArgSettings) -> Self { + pub fn settings(mut self, settings: &[ArgSettings]) -> Self { + for s in settings { + self.settings.set(*s); + } + self + } + + /// Unsets one of the [`ArgSettings`] for the argument + /// [`ArgSettings`]: ./enum.ArgSettings.html + pub fn unset_setting(mut self, s: ArgSettings) -> Self { self.unsetb(s); self } + /// Set a custom heading for this arg to be printed under + pub fn help_heading(mut self, s: Option<&'help str>) -> Self { + self.help_heading = s; + self + } + #[doc(hidden)] - pub fn setb(&mut self, s: ArgSettings) { - self.b.set(s); + pub fn _build(&mut self) { + if (self.is_set(ArgSettings::UseValueDelimiter) + || self.is_set(ArgSettings::RequireDelimiter)) + && self.val_delim.is_none() + { + self.val_delim = Some(','); + } + if self.index.is_some() || (self.short.is_none() && self.long.is_none()) { + if self.max_vals.is_some() + || self.min_vals.is_some() + || (self.num_vals.is_some() && self.num_vals.unwrap() > 1) + { + self.setb(ArgSettings::MultipleValues); + self.setb(ArgSettings::MultipleOccurrences); + } + } else if self.is_set(ArgSettings::TakesValue) { + if let Some(ref vec) = self.val_names { + if vec.len() > 1 { + self.num_vals = Some(vec.len() as u64); + } + } + } + } + + // @TODO @p6 @naming @internal: rename to set_mut + #[doc(hidden)] + pub fn setb(&mut self, s: ArgSettings) { self.settings.set(s); } + + // @TODO @p6 @naming @internal: rename to unset_mut + #[doc(hidden)] + pub fn unsetb(&mut self, s: ArgSettings) { self.settings.unset(s); } + + #[doc(hidden)] + pub fn has_switch(&self) -> bool { self.short.is_some() || self.long.is_some() } + + #[doc(hidden)] + pub fn longest_filter(&self) -> bool { + self.is_set(ArgSettings::TakesValue) || self.long.is_some() || self.short.is_none() + } + + // Used for positionals when printing + #[doc(hidden)] + pub fn multiple_str(&self) -> &str { + let mult_vals = self + .val_names + .as_ref() + .map_or(true, |names| names.len() < 2); + if (self.is_set(ArgSettings::MultipleValues) + || self.is_set(ArgSettings::MultipleOccurrences)) + && mult_vals + { + "..." + } else { + "" + } } + // Used for positionals when printing #[doc(hidden)] - pub fn unsetb(&mut self, s: ArgSettings) { - self.b.unset(s); + pub fn name_no_brackets(&self) -> Cow { + debugln!("PosBuilder::name_no_brackets:{}", self.name); + let mut delim = String::new(); + delim.push(if self.is_set(ArgSettings::RequireDelimiter) { + self.val_delim.expect(INTERNAL_ERROR_MSG) + } else { + ' ' + }); + if let Some(ref names) = self.val_names { + debugln!("PosBuilder:name_no_brackets: val_names={:#?}", names); + if names.len() > 1 { + Cow::Owned( + names + .values() + .map(|n| format!("<{}>", n)) + .collect::>() + .join(&*delim), + ) + } else { + Cow::Borrowed(names.values().next().expect(INTERNAL_ERROR_MSG)) + } + } else { + debugln!("PosBuilder:name_no_brackets: just name"); + Cow::Borrowed(self.name) + } } } -impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> { - fn from(a: &'z Arg<'a, 'b>) -> Self { - Arg { - b: a.b.clone(), - v: a.v.clone(), - s: a.s.clone(), - index: a.index, - r_ifs: a.r_ifs.clone(), +impl<'help, 'z> From<&'z Arg<'help>> for Arg<'help> { + fn from(a: &'z Arg<'help>) -> Self { a.clone() } +} + +impl<'help> From<&'help str> for Arg<'help> { + fn from(s: &'help str) -> Self { UsageParser::from_usage(s).parse() } +} + +impl<'help> PartialEq for Arg<'help> { + fn eq(&self, other: &Arg<'help>) -> bool { self.name == other.name } +} + +impl<'help> Display for Arg<'help> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if self.index.is_some() || (self.long.is_none() && self.short.is_none()) { + // Positional + let mut delim = String::new(); + delim.push(if self.is_set(ArgSettings::RequireDelimiter) { + self.val_delim.expect(INTERNAL_ERROR_MSG) + } else { + ' ' + }); + if let Some(ref names) = self.val_names { + write!( + f, + "{}", + names + .values() + .map(|n| format!("<{}>", n)) + .collect::>() + .join(&*delim) + )?; + } else { + write!(f, "<{}>", self.name)?; + } + if self.settings.is_set(ArgSettings::MultipleValues) + && (self.val_names.is_none() + || (self.val_names.is_some() && self.val_names.as_ref().unwrap().len() == 1)) + { + write!(f, "...")?; + } + return Ok(()); + } else if !self.is_set(ArgSettings::TakesValue) { + // Flag + if let Some(l) = self.long { + write!(f, "--{}", l)?; + } else if let Some(s) = self.short { + write!(f, "-{}", s)?; + } + + return Ok(()); + } + let sep = if self.is_set(ArgSettings::RequireEquals) { + "=" + } else { + " " + }; + // Write the name such --long or -l + if let Some(l) = self.long { + write!(f, "--{}{}", l, sep)?; + } else { + write!(f, "-{}{}", self.short.unwrap(), sep)?; + } + let delim = if self.is_set(ArgSettings::RequireDelimiter) { + self.val_delim.expect(INTERNAL_ERROR_MSG) + } else { + ' ' + }; + + // Write the values such as + if let Some(ref vec) = self.val_names { + let mut it = vec.iter().peekable(); + while let Some((_, val)) = it.next() { + write!(f, "<{}>", val)?; + if it.peek().is_some() { + write!(f, "{}", delim)?; + } + } + let num = vec.len(); + if self.is_set(ArgSettings::MultipleValues) && num == 1 { + write!(f, "...")?; + } + } else if let Some(num) = self.num_vals { + let mut it = (0..num).peekable(); + while let Some(_) = it.next() { + write!(f, "<{}>", self.name)?; + if it.peek().is_some() { + write!(f, "{}", delim)?; + } + } + if self.is_set(ArgSettings::MultipleValues) && num == 1 { + write!(f, "...")?; + } + } else { + write!( + f, + "<{}>{}", + self.name, + if self.is_set(ArgSettings::MultipleOccurrences) { + "..." + } else { + "" + } + )?; } + + Ok(()) + } +} + +impl<'help> PartialOrd for Arg<'help> { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} + +impl<'help> Ord for Arg<'help> { + fn cmp(&self, other: &Arg) -> Ordering { self.name.cmp(&other.name) } +} + +impl<'help> Eq for Arg<'help> {} + +impl<'help> fmt::Debug for Arg<'help> { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!( + f, + "Arg {{ id: {:X?}, name: {:?}, help: {:?}, long_help: {:?}, conflicts_with: {:?}, \ + settings: {:?}, required_unless: {:?}, overrides_with: {:?}, groups: {:?}, \ + requires: {:?}, requires_ifs: {:?}, short: {:?}, index: {:?}, long: {:?}, \ + aliases: {:?}, possible_values: {:?}, value_names: {:?}, number_of_values: {:?}, \ + max_values: {:?}, min_values: {:?}, value_delimiter: {:?}, default_value_ifs: {:?}, \ + value_terminator: {:?}, display_order: {:?}, env: {:?}, unified_ord: {:?}, \ + default_value: {:?}, validator: {}, validator_os: {} \ + }}", + self.id, + self.name, + self.help, + self.long_help, + self.blacklist, + self.settings, + self.r_unless, + self.overrides, + self.groups, + self.requires, + self.r_ifs, + self.short, + self.index, + self.long, + self.aliases, + self.possible_vals, + self.val_names, + self.num_vals, + self.max_vals, + self.min_vals, + self.val_delim, + self.default_vals_ifs, + self.terminator, + self.disp_ord, + self.env, + self.unified_ord, + self.default_val, + self.validator.as_ref().map_or("None", |_| "Some(Fn)"), + self.validator_os.as_ref().map_or("None", |_| "Some(Fn)") + ) } } -impl<'n, 'e> PartialEq for Arg<'n, 'e> { - fn eq(&self, other: &Arg<'n, 'e>) -> bool { - self.b == other.b +// Flags +#[cfg(test)] +mod test { + use super::Arg; + use crate::build::ArgSettings; + use crate::util::VecMap; + + #[test] + fn flag_display() { + let mut f = Arg::with_name("flg"); + f.settings.set(ArgSettings::MultipleOccurrences); + f.long = Some("flag"); + + assert_eq!(&*format!("{}", f), "--flag"); + + let mut f2 = Arg::with_name("flg"); + f2.short = Some('f'); + + assert_eq!(&*format!("{}", f2), "-f"); + } + + #[test] + fn flag_display_single_alias() { + let mut f = Arg::with_name("flg"); + f.long = Some("flag"); + f.aliases = Some(vec![("als", true)]); + + assert_eq!(&*format!("{}", f), "--flag") + } + + #[test] + fn flag_display_multiple_aliases() { + let mut f = Arg::with_name("flg"); + f.short = Some('f'); + f.aliases = Some(vec![ + ("alias_not_visible", false), + ("f2", true), + ("f3", true), + ("f4", true), + ]); + assert_eq!(&*format!("{}", f), "-f"); + } + + // Options + + #[test] + fn option_display1() { + let o = Arg::with_name("opt") + .long("option") + .takes_value(true) + .multiple(true); + + assert_eq!(&*format!("{}", o), "--option ..."); + } + + #[test] + fn option_display2() { + let o2 = Arg::with_name("opt") + .short('o') + .value_names(&["file", "name"]); + + assert_eq!(&*format!("{}", o2), "-o "); + } + + #[test] + fn option_display3() { + let o2 = Arg::with_name("opt") + .short('o') + .multiple(true) + .value_names(&["file", "name"]); + + assert_eq!(&*format!("{}", o2), "-o "); + } + + #[test] + fn option_display_single_alias() { + let o = Arg::with_name("opt") + .takes_value(true) + .long("option") + .visible_alias("als"); + + assert_eq!(&*format!("{}", o), "--option "); + } + + #[test] + fn option_display_multiple_aliases() { + let o = Arg::with_name("opt") + .long("option") + .takes_value(true) + .visible_aliases(&["als2", "als3", "als4"]) + .alias("als_not_visible"); + + assert_eq!(&*format!("{}", o), "--option "); + } + + // Positionals + + #[test] + fn positiona_display_mult() { + let mut p = Arg::with_name("pos").index(1); + p.setb(ArgSettings::MultipleValues); + + assert_eq!(&*format!("{}", p), "..."); + } + + #[test] + fn positional_display_required() { + let mut p2 = Arg::with_name("pos").index(1); + p2.settings.set(ArgSettings::Required); + + assert_eq!(&*format!("{}", p2), ""); + } + + #[test] + fn positional_display_val_names() { + let mut p2 = Arg::with_name("pos").index(1); + let mut vm = VecMap::new(); + vm.insert(0, "file1"); + vm.insert(1, "file2"); + p2.val_names = Some(vm); + + assert_eq!(&*format!("{}", p2), " "); + } + + #[test] + fn positional_display_val_names_req() { + let mut p2 = Arg::with_name("pos").index(1); + p2.settings.set(ArgSettings::Required); + let mut vm = VecMap::new(); + vm.insert(0, "file1"); + vm.insert(1, "file2"); + p2.val_names = Some(vm); + + assert_eq!(&*format!("{}", p2), " "); } } diff --git a/src/args/settings.rs b/src/build/arg/settings.rs similarity index 62% rename from src/args/settings.rs rename to src/build/arg/settings.rs index 7b0e0a26c7a4..0d431fd81f5b 100644 --- a/src/args/settings.rs +++ b/src/build/arg/settings.rs @@ -1,30 +1,29 @@ // Std -#[allow(deprecated, unused_imports)] -use std::ascii::AsciiExt; use std::str::FromStr; bitflags! { struct Flags: u32 { const REQUIRED = 1; - const MULTIPLE = 1 << 1; - const EMPTY_VALS = 1 << 2; + const MULTIPLE_OCC = 1 << 1; + const EMPTY_VALS = 1 << 2 | Self::TAKES_VAL.bits; const GLOBAL = 1 << 3; const HIDDEN = 1 << 4; const TAKES_VAL = 1 << 5; const USE_DELIM = 1 << 6; const NEXT_LINE_HELP = 1 << 7; const R_UNLESS_ALL = 1 << 8; - const REQ_DELIM = 1 << 9; + const REQ_DELIM = 1 << 9 | Self::TAKES_VAL.bits | Self::USE_DELIM.bits; const DELIM_NOT_SET = 1 << 10; - const HIDE_POS_VALS = 1 << 11; - const ALLOW_TAC_VALS = 1 << 12; - const REQUIRE_EQUALS = 1 << 13; - const LAST = 1 << 14; - const HIDE_DEFAULT_VAL = 1 << 15; + const HIDE_POS_VALS = 1 << 11 | Self::TAKES_VAL.bits; + const ALLOW_TAC_VALS = 1 << 12 | Self::TAKES_VAL.bits; + const REQUIRE_EQUALS = 1 << 13 | Self::TAKES_VAL.bits; + const LAST = 1 << 14 | Self::TAKES_VAL.bits; + const HIDE_DEFAULT_VAL = 1 << 15 | Self::TAKES_VAL.bits; const CASE_INSENSITIVE = 1 << 16; const HIDE_ENV_VALS = 1 << 17; const HIDDEN_SHORT_H = 1 << 18; const HIDDEN_LONG_H = 1 << 19; + const MULTIPLE_VALS = 1 << 20 | Self::TAKES_VAL.bits; } } @@ -35,11 +34,12 @@ pub struct ArgFlags(Flags); impl ArgFlags { pub fn new() -> Self { ArgFlags::default() } - impl_settings!{ArgSettings, + // @TODO @p6 @internal: Reorder alphabetically + impl_settings! {ArgSettings, Required => Flags::REQUIRED, - Multiple => Flags::MULTIPLE, - EmptyValues => Flags::EMPTY_VALS, - Global => Flags::GLOBAL, + MultipleOccurrences => Flags::MULTIPLE_OCC, + MultipleValues => Flags::MULTIPLE_VALS, + AllowEmptyValues => Flags::EMPTY_VALS, Hidden => Flags::HIDDEN, TakesValue => Flags::TAKES_VAL, UseValueDelimiter => Flags::USE_DELIM, @@ -48,10 +48,10 @@ impl ArgFlags { RequireDelimiter => Flags::REQ_DELIM, ValueDelimiterNotSet => Flags::DELIM_NOT_SET, HidePossibleValues => Flags::HIDE_POS_VALS, - AllowLeadingHyphen => Flags::ALLOW_TAC_VALS, + AllowHyphenValues => Flags::ALLOW_TAC_VALS, RequireEquals => Flags::REQUIRE_EQUALS, Last => Flags::LAST, - CaseInsensitive => Flags::CASE_INSENSITIVE, + IgnoreCase => Flags::CASE_INSENSITIVE, HideEnvValues => Flags::HIDE_ENV_VALS, HideDefaultValue => Flags::HIDE_DEFAULT_VAL, HiddenShortHelp => Flags::HIDDEN_SHORT_H, @@ -60,58 +60,62 @@ impl ArgFlags { } impl Default for ArgFlags { - fn default() -> Self { ArgFlags(Flags::EMPTY_VALS | Flags::DELIM_NOT_SET) } + fn default() -> Self { ArgFlags(Flags::DELIM_NOT_SET) } } /// Various settings that apply to arguments and may be set, unset, and checked via getter/setter -/// methods [`Arg::set`], [`Arg::unset`], and [`Arg::is_set`] +/// methods [`Arg::setting`], [`Arg::unset_setting`], and [`Arg::is_set`]. This is what the +/// [`Arg`] methods which accept a `bool` use internally. /// -/// [`Arg::set`]: ./struct.Arg.html#method.set -/// [`Arg::unset`]: ./struct.Arg.html#method.unset +/// [`Arg`]: ./struct.Arg.html +/// [`Arg::setting`]: ./struct.Arg.html#method.setting +/// [`Arg::unset_setting`]: ./struct.Arg.html#method.unset_setting /// [`Arg::is_set`]: ./struct.Arg.html#method.is_set #[derive(Debug, PartialEq, Copy, Clone)] pub enum ArgSettings { - /// The argument must be used + /// Specifies that an arg must be used Required, - /// The argument may be used multiple times such as `--flag --flag` - Multiple, - /// The argument allows empty values such as `--option ""` - EmptyValues, - /// The argument should be propagated down through all child [`SubCommand`]s - /// - /// [`SubCommand`]: ./struct.SubCommand.html - Global, - /// The argument should **not** be shown in help text + /// Allows an arg to accept multiple values + MultipleValues, + /// Allows an arg to appear multiple times + MultipleOccurrences, + /// Allows an arg accept empty values such as `""` + AllowEmptyValues, + /// Hides an arg from the help message Hidden, - /// The argument accepts a value, such as `--option ` + /// Allows an argument to take a value (such as `--option value`) TakesValue, - /// Determines if the argument allows values to be grouped via a delimiter + /// Enables a delimiter to break up arguments `--option val1,val2,val3` becomes three values + /// (`val1`, `val2`, and `val3`) instead of the default one (`val1,val2,val3`) UseValueDelimiter, - /// Prints the help text on the line after the argument + /// Tells an arg to display it's help on the line below the arg itself in the help message NextLineHelp, - /// Requires the use of a value delimiter for all multiple values + /// Says that arg *must* use a delimiter to separate values RequireDelimiter, - /// Hides the possible values from the help string + /// Hides the possible values from the help message HidePossibleValues, - /// Allows vals that start with a '-' - AllowLeadingHyphen, - /// Require options use `--option=val` syntax + /// Allows values that start with a hyphen + AllowHyphenValues, + /// Requires that an equals be used to provide a value to an option such as `--option=value` RequireEquals, - /// Specifies that the arg is the last positional argument and may be accessed early via `--` - /// syntax + /// Says that a positional arg will be the last positional, and reuqires `--` to be accessed. + /// It can also be accessed early (i.e. before other positionals) by providing `--` Last, - /// Hides the default value from the help string + /// Hides the default value from the help message HideDefaultValue, - /// Makes `Arg::possible_values` case insensitive - CaseInsensitive, - /// Hides ENV values in the help message + /// Possible values become case insensitive + IgnoreCase, + /// Hides any values currently assigned to ENV variables in the help message (good for sensitive + /// information) HideEnvValues, /// The argument should **not** be shown in short help text HiddenShortHelp, /// The argument should **not** be shown in long help text HiddenLongHelp, - #[doc(hidden)] RequiredUnlessAll, - #[doc(hidden)] ValueDelimiterNotSet, + #[doc(hidden)] + RequiredUnlessAll, + #[doc(hidden)] + ValueDelimiterNotSet, } impl FromStr for ArgSettings { @@ -119,9 +123,7 @@ impl FromStr for ArgSettings { fn from_str(s: &str) -> Result::Err> { match &*s.to_ascii_lowercase() { "required" => Ok(ArgSettings::Required), - "multiple" => Ok(ArgSettings::Multiple), - "global" => Ok(ArgSettings::Global), - "emptyvalues" => Ok(ArgSettings::EmptyValues), + "allowemptyvalues" => Ok(ArgSettings::AllowEmptyValues), "hidden" => Ok(ArgSettings::Hidden), "takesvalue" => Ok(ArgSettings::TakesValue), "usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter), @@ -130,11 +132,11 @@ impl FromStr for ArgSettings { "requiredelimiter" => Ok(ArgSettings::RequireDelimiter), "valuedelimiternotset" => Ok(ArgSettings::ValueDelimiterNotSet), "hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues), - "allowleadinghyphen" => Ok(ArgSettings::AllowLeadingHyphen), + "allowhyphenvalues" => Ok(ArgSettings::AllowHyphenValues), "requireequals" => Ok(ArgSettings::RequireEquals), "last" => Ok(ArgSettings::Last), "hidedefaultvalue" => Ok(ArgSettings::HideDefaultValue), - "caseinsensitive" => Ok(ArgSettings::CaseInsensitive), + "ignorecase" => Ok(ArgSettings::IgnoreCase), "hideenvvalues" => Ok(ArgSettings::HideEnvValues), "hiddenshorthelp" => Ok(ArgSettings::HiddenShortHelp), "hiddenlonghelp" => Ok(ArgSettings::HiddenLongHelp), @@ -150,16 +152,12 @@ mod test { #[test] fn arg_settings_fromstr() { assert_eq!( - "allowleadinghyphen".parse::().unwrap(), - ArgSettings::AllowLeadingHyphen + "allowhyphenvalues".parse::().unwrap(), + ArgSettings::AllowHyphenValues ); assert_eq!( - "emptyvalues".parse::().unwrap(), - ArgSettings::EmptyValues - ); - assert_eq!( - "global".parse::().unwrap(), - ArgSettings::Global + "allowemptyvalues".parse::().unwrap(), + ArgSettings::AllowEmptyValues ); assert_eq!( "hidepossiblevalues".parse::().unwrap(), @@ -169,10 +167,6 @@ mod test { "hidden".parse::().unwrap(), ArgSettings::Hidden ); - assert_eq!( - "multiple".parse::().unwrap(), - ArgSettings::Multiple - ); assert_eq!( "nextlinehelp".parse::().unwrap(), ArgSettings::NextLineHelp @@ -211,8 +205,8 @@ mod test { ArgSettings::HideDefaultValue ); assert_eq!( - "caseinsensitive".parse::().unwrap(), - ArgSettings::CaseInsensitive + "ignorecase".parse::().unwrap(), + ArgSettings::IgnoreCase ); assert_eq!( "hideenvvalues".parse::().unwrap(), diff --git a/src/args/group.rs b/src/build/arg_group.rs similarity index 81% rename from src/args/group.rs rename to src/build/arg_group.rs index f8bfb7a08642..c29080cc0563 100644 --- a/src/args/group.rs +++ b/src/build/arg_group.rs @@ -1,9 +1,14 @@ -#[cfg(feature = "yaml")] -use std::collections::BTreeMap; +// Std use std::fmt::{Debug, Formatter, Result}; +// Third Party #[cfg(feature = "yaml")] -use yaml_rust::Yaml; +use yaml_rust; + +// Internal +use crate::util::Key; + +type Id = u64; /// `ArgGroup`s are a family of related [arguments] and way for you to express, "Any of these /// arguments". By placing arguments in a logical group, you can create easier requirement and @@ -39,15 +44,14 @@ use yaml_rust::Yaml; /// ```rust /// # use clap::{App, ArgGroup, ErrorKind}; /// let result = App::new("app") -/// .args_from_usage( -/// "--set-ver [ver] 'set the version manually' -/// --major 'auto increase major' -/// --minor 'auto increase minor' -/// --patch 'auto increase patch'") +/// .arg("--set-ver [ver] 'set the version manually'") +/// .arg("--major 'auto increase major'") +/// .arg("--minor 'auto increase minor'") +/// .arg("--patch 'auto increase patch'") /// .group(ArgGroup::with_name("vers") -/// .args(&["set-ver", "major", "minor", "patch"]) +/// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) -/// .get_matches_from_safe(vec!["app", "--major", "--patch"]); +/// .try_get_matches_from(vec!["app", "--major", "--patch"]); /// // Because we used two args in the group it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); @@ -58,15 +62,14 @@ use yaml_rust::Yaml; /// ```rust /// # use clap::{App, ArgGroup}; /// let result = App::new("app") -/// .args_from_usage( -/// "--set-ver [ver] 'set the version manually' -/// --major 'auto increase major' -/// --minor 'auto increase minor' -/// --patch 'auto increase patch'") +/// .arg("--set-ver [ver] 'set the version manually'") +/// .arg("--major 'auto increase major'") +/// .arg("--minor 'auto increase minor'") +/// .arg("--patch 'auto increase patch'") /// .group(ArgGroup::with_name("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) -/// .get_matches_from_safe(vec!["app", "--major"]); +/// .try_get_matches_from(vec!["app", "--major"]); /// assert!(result.is_ok()); /// let matches = result.unwrap(); /// // We may not know which of the args was used, so we can test for the group... @@ -79,15 +82,32 @@ use yaml_rust::Yaml; /// [requirement]: ./struct.Arg.html#method.requires #[derive(Default)] pub struct ArgGroup<'a> { - #[doc(hidden)] pub name: &'a str, - #[doc(hidden)] pub args: Vec<&'a str>, - #[doc(hidden)] pub required: bool, - #[doc(hidden)] pub requires: Option>, - #[doc(hidden)] pub conflicts: Option>, - #[doc(hidden)] pub multiple: bool, + #[doc(hidden)] + pub id: Id, + #[doc(hidden)] + pub name: &'a str, + #[doc(hidden)] + pub args: Vec, + #[doc(hidden)] + pub required: bool, + #[doc(hidden)] + pub requires: Option>, + #[doc(hidden)] + pub conflicts: Option>, + #[doc(hidden)] + pub multiple: bool, } impl<'a> ArgGroup<'a> { + #[doc(hidden)] + pub fn _with_id(id: Id) -> Self { + ArgGroup { + id, + ..ArgGroup::default() + } + } + /// @TODO @p2 @docs @v3-beta1: Write Docs + pub fn new(id: T) -> Self { ArgGroup::_with_id(id.key()) } /// Creates a new instance of `ArgGroup` using a unique string name. The name will be used to /// get values from the group or refer to the group inside of conflict and requirement rules. /// @@ -100,12 +120,9 @@ impl<'a> ArgGroup<'a> { /// ``` pub fn with_name(n: &'a str) -> Self { ArgGroup { + id: n.key(), name: n, - required: false, - args: vec![], - requires: None, - conflicts: None, - multiple: false, + ..ArgGroup::default() } } @@ -123,7 +140,9 @@ impl<'a> ArgGroup<'a> { /// # } /// ``` #[cfg(feature = "yaml")] - pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> { ArgGroup::from(y.as_hash().unwrap()) } + pub fn from_yaml(y: &'a yaml_rust::Yaml) -> ArgGroup<'a> { + ArgGroup::from(y.as_hash().unwrap()) + } /// Adds an [argument] to this group by name /// @@ -133,9 +152,9 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup}; /// let m = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .group(ArgGroup::with_name("req_flags") /// .arg("flag") /// .arg("color")) @@ -147,13 +166,8 @@ impl<'a> ArgGroup<'a> { /// ``` /// [argument]: ./struct.Arg.html #[cfg_attr(feature = "lints", allow(should_assert_eq))] - pub fn arg(mut self, n: &'a str) -> Self { - assert!( - self.name != n, - "ArgGroup '{}' can not have same name as arg inside it", - &*self.name - ); - self.args.push(n); + pub fn arg(mut self, arg_id: T) -> Self { + self.args.push(arg_id.key()); self } @@ -165,9 +179,9 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup}; /// let m = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"])) /// .get_matches_from(vec!["myprog", "-f"]); @@ -177,7 +191,7 @@ impl<'a> ArgGroup<'a> { /// assert!(m.is_present("flag")); /// ``` /// [arguments]: ./struct.Arg.html - pub fn args(mut self, ns: &[&'a str]) -> Self { + pub fn args(mut self, ns: &[T]) -> Self { for n in ns { self = self.arg(n); } @@ -195,9 +209,9 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup}; /// let m = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"]) /// .multiple(true)) @@ -212,12 +226,12 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup, ErrorKind}; /// let result = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"])) - /// .get_matches_from_safe(vec!["myprog", "-f", "-c"]); + /// .try_get_matches_from(vec!["myprog", "-f", "-c"]); /// // Because we used both args in the group it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); @@ -234,7 +248,7 @@ impl<'a> ArgGroup<'a> { /// that one argument from this group *must* be present at runtime (unless /// conflicting with another argument). /// - /// **NOTE:** This setting only applies to the current [`App`] / [`SubCommand`], and not + /// **NOTE:** This setting only applies to the current [`App`] / [``], and not /// globally. /// /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with @@ -248,20 +262,20 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup, ErrorKind}; /// let result = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"]) /// .required(true)) - /// .get_matches_from_safe(vec!["myprog"]); + /// .try_get_matches_from(vec!["myprog"]); /// // Because we didn't use any of the args in the group, it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument); /// ``` /// [`App`]: ./struct.App.html - /// [`SubCommand`]: ./struct.SubCommand.html + /// [``]: ./struct..html /// [`ArgGroup::multiple`]: ./struct.ArgGroup.html#method.multiple pub fn required(mut self, r: bool) -> Self { self.required = r; @@ -281,15 +295,15 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup, ErrorKind}; /// let result = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .arg(Arg::with_name("debug") - /// .short("d")) + /// .short('d')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"]) /// .requires("debug")) - /// .get_matches_from_safe(vec!["myprog", "-c"]); + /// .try_get_matches_from(vec!["myprog", "-c"]); /// // because we used an arg from the group, and the group requires "-d" to be used, it's an /// // error /// assert!(result.is_err()); @@ -298,11 +312,12 @@ impl<'a> ArgGroup<'a> { /// ``` /// [required group]: ./struct.ArgGroup.html#method.required /// [argument requirement rules]: ./struct.Arg.html#method.requires - pub fn requires(mut self, n: &'a str) -> Self { + pub fn requires(mut self, id: T) -> Self { + let arg_id = id.key(); if let Some(ref mut reqs) = self.requires { - reqs.push(n); + reqs.push(arg_id); } else { - self.requires = Some(vec![n]); + self.requires = Some(vec![arg_id]); } self } @@ -320,17 +335,17 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup, ErrorKind}; /// let result = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .arg(Arg::with_name("debug") - /// .short("d")) + /// .short('d')) /// .arg(Arg::with_name("verb") - /// .short("v")) + /// .short('v')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"]) /// .requires_all(&["debug", "verb"])) - /// .get_matches_from_safe(vec!["myprog", "-c", "-d"]); + /// .try_get_matches_from(vec!["myprog", "-c", "-d"]); /// // because we used an arg from the group, and the group requires "-d" and "-v" to be used, /// // yet we only used "-d" it's an error /// assert!(result.is_err()); @@ -358,26 +373,27 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup, ErrorKind}; /// let result = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .arg(Arg::with_name("debug") - /// .short("d")) + /// .short('d')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"]) /// .conflicts_with("debug")) - /// .get_matches_from_safe(vec!["myprog", "-c", "-d"]); + /// .try_get_matches_from(vec!["myprog", "-c", "-d"]); /// // because we used an arg from the group, and the group conflicts with "-d", it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); /// assert_eq!(err.kind, ErrorKind::ArgumentConflict); /// ``` /// [argument exclusion rules]: ./struct.Arg.html#method.conflicts_with - pub fn conflicts_with(mut self, n: &'a str) -> Self { + pub fn conflicts_with(mut self, id: T) -> Self { + let arg_id = id.key(); if let Some(ref mut confs) = self.conflicts { - confs.push(n); + confs.push(arg_id); } else { - self.conflicts = Some(vec![n]); + self.conflicts = Some(vec![arg_id]); } self } @@ -394,17 +410,17 @@ impl<'a> ArgGroup<'a> { /// # use clap::{App, Arg, ArgGroup, ErrorKind}; /// let result = App::new("myprog") /// .arg(Arg::with_name("flag") - /// .short("f")) + /// .short('f')) /// .arg(Arg::with_name("color") - /// .short("c")) + /// .short('c')) /// .arg(Arg::with_name("debug") - /// .short("d")) + /// .short('d')) /// .arg(Arg::with_name("verb") - /// .short("v")) + /// .short('v')) /// .group(ArgGroup::with_name("req_flags") /// .args(&["flag", "color"]) /// .conflicts_with_all(&["debug", "verb"])) - /// .get_matches_from_safe(vec!["myprog", "-c", "-v"]); + /// .try_get_matches_from(vec!["myprog", "-c", "-v"]); /// // because we used an arg from the group, and the group conflicts with either "-v" or "-d" /// // it's an error /// assert!(result.is_err()); @@ -431,11 +447,7 @@ impl<'a> Debug for ArgGroup<'a> { \trequires: {:?},\n\ \tconflicts: {:?},\n\ }}", - self.name, - self.args, - self.required, - self.requires, - self.conflicts + self.name, self.args, self.required, self.requires, self.conflicts ) } } @@ -443,6 +455,7 @@ impl<'a> Debug for ArgGroup<'a> { impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> { fn from(g: &'z ArgGroup<'a>) -> Self { ArgGroup { + id: g.id, name: g.name, required: g.required, args: g.args.clone(), @@ -454,8 +467,8 @@ impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> { } #[cfg(feature = "yaml")] -impl<'a> From<&'a BTreeMap> for ArgGroup<'a> { - fn from(b: &'a BTreeMap) -> Self { +impl<'a> From<&'a yaml_rust::yaml::Hash> for ArgGroup<'a> { + fn from(b: &'a yaml_rust::yaml::Hash) -> Self { // We WANT this to panic on error...so expect() is good. let mut a = ArgGroup::default(); let group_settings = if b.len() == 1 { @@ -494,8 +507,7 @@ impl<'a> From<&'a BTreeMap> for ArgGroup<'a> { s => panic!( "Unknown ArgGroup setting '{}' in YAML file for \ ArgGroup '{}'", - s, - a.name + s, a.name ), } } @@ -507,6 +519,7 @@ impl<'a> From<&'a BTreeMap> for ArgGroup<'a> { #[cfg(test)] mod test { use super::ArgGroup; + use super::Key; #[cfg(feature = "yaml")] use yaml_rust::YamlLoader; @@ -524,9 +537,9 @@ mod test { .requires_all(&["r2", "r3"]) .requires("r4"); - let args = vec!["a1", "a4", "a2", "a3"]; - let reqs = vec!["r1", "r2", "r3", "r4"]; - let confs = vec!["c1", "c2", "c3", "c4"]; + let args = vec!["a1".key(), "a4".key(), "a2".key(), "a3".key()]; + let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()]; + let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()]; assert_eq!(g.args, args); assert_eq!(g.requires, Some(reqs)); @@ -547,9 +560,9 @@ mod test { .requires_all(&["r2", "r3"]) .requires("r4"); - let args = vec!["a1", "a4", "a2", "a3"]; - let reqs = vec!["r1", "r2", "r3", "r4"]; - let confs = vec!["c1", "c2", "c3", "c4"]; + let args = vec!["a1".key(), "a4".key(), "a2".key(), "a3".key()]; + let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()]; + let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()]; let debug_str = format!( "{{\n\ @@ -581,9 +594,9 @@ mod test { .requires_all(&["r2", "r3"]) .requires("r4"); - let args = vec!["a1", "a4", "a2", "a3"]; - let reqs = vec!["r1", "r2", "r3", "r4"]; - let confs = vec!["c1", "c2", "c3", "c4"]; + let args = vec!["a1".key(), "a4".key(), "a2".key(), "a3".key()]; + let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()]; + let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()]; let g2 = ArgGroup::from(&g); assert_eq!(g2.args, args); @@ -612,9 +625,9 @@ requires: - r4"; let yml = &YamlLoader::load_from_str(g_yaml).expect("failed to load YAML file")[0]; let g = ArgGroup::from_yaml(yml); - let args = vec!["a1", "a4", "a2", "a3"]; - let reqs = vec!["r1", "r2", "r3", "r4"]; - let confs = vec!["c1", "c2", "c3", "c4"]; + let args = vec!["a1".key(), "a4".key(), "a2".key(), "a3".key()]; + let reqs = vec!["r1".key(), "r2".key(), "r3".key(), "r4".key()]; + let confs = vec!["c1".key(), "c2".key(), "c3".key(), "c4".key()]; assert_eq!(g.args, args); assert_eq!(g.requires, Some(reqs)); assert_eq!(g.conflicts, Some(confs)); @@ -624,6 +637,7 @@ requires: impl<'a> Clone for ArgGroup<'a> { fn clone(&self) -> Self { ArgGroup { + id: self.id, name: self.name, required: self.required, args: self.args.clone(), diff --git a/src/build/macros.rs b/src/build/macros.rs new file mode 100644 index 000000000000..da241dfa72f1 --- /dev/null +++ b/src/build/macros.rs @@ -0,0 +1,139 @@ +#[cfg(feature = "yaml")] +macro_rules! yaml_tuple2 { + ($a:ident, $v:ident, $c:ident) => {{ + if let Some(vec) = $v.as_vec() { + for ys in vec { + if let Some(tup) = ys.as_vec() { + debug_assert_eq!(2, tup.len()); + $a = $a.$c(yaml_str!(tup[0]), yaml_str!(tup[1])); + } else { + panic!("Failed to convert YAML value to vec"); + } + } + } else { + panic!("Failed to convert YAML value to vec"); + } + $a + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_tuple3 { + ($a:ident, $v:ident, $c:ident) => {{ + if let Some(vec) = $v.as_vec() { + for ys in vec { + if let Some(tup) = ys.as_vec() { + debug_assert_eq!(3, tup.len()); + $a = $a.$c(yaml_str!(tup[0]), yaml_opt_str!(tup[1]), yaml_str!(tup[2])); + } else { + panic!("Failed to convert YAML value to vec"); + } + } + } else { + panic!("Failed to convert YAML value to vec"); + } + $a + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_vec_or_str { + ($v:ident, $a:ident, $c:ident) => {{ + let maybe_vec = $v.as_vec(); + if let Some(vec) = maybe_vec { + for ys in vec { + if let Some(s) = ys.as_str() { + $a = $a.$c(s); + } else { + panic!("Failed to convert YAML value {:?} to a string", ys); + } + } + } else { + if let Some(s) = $v.as_str() { + $a = $a.$c(s); + } else { + panic!( + "Failed to convert YAML value {:?} to either a vec or string", + $v + ); + } + } + $a + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_opt_str { + ($v:expr) => {{ + if $v.is_null() { + Some( + $v.as_str() + .unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)), + ) + } else { + None + } + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_char { + ($v:expr) => {{ + $v.as_str() + .unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) + .chars() + .next() + .unwrap_or_else(|| panic!("Expected char")) + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_str { + ($v:expr) => {{ + $v.as_str() + .unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_to_char { + ($a:ident, $v:ident, $c:ident) => {{ + $a.$c(yaml_char!($v)) + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_to_str { + ($a:ident, $v:ident, $c:ident) => {{ + $a.$c(yaml_str!($v)) + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_to_bool { + ($a:ident, $v:ident, $c:ident) => {{ + $a.$c($v + .as_bool() + .unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))) + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_to_u64 { + ($a:ident, $v:ident, $c:ident) => {{ + $a.$c($v + .as_i64() + .unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) + as u64) + }}; +} + +#[cfg(feature = "yaml")] +macro_rules! yaml_to_usize { + ($a:ident, $v:ident, $c:ident) => {{ + $a.$c($v + .as_i64() + .unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) + as usize) + }}; +} diff --git a/src/build/mod.rs b/src/build/mod.rs new file mode 100644 index 000000000000..3d67ba6be205 --- /dev/null +++ b/src/build/mod.rs @@ -0,0 +1,13 @@ +#[macro_use] +mod macros; + +pub mod app; +pub mod arg; + +mod arg_group; +mod usage_parser; + +pub use self::app::{App, AppFlags, AppSettings, Propagation}; +pub use self::arg::{Arg, ArgFlags, ArgSettings}; +pub use self::arg_group::ArgGroup; +pub use self::usage_parser::UsageParser; diff --git a/src/build/usage_parser.rs b/src/build/usage_parser.rs new file mode 100644 index 000000000000..0a6a47eec5fe --- /dev/null +++ b/src/build/usage_parser.rs @@ -0,0 +1,1357 @@ +// Internal +use crate::build::{Arg, ArgSettings}; +use crate::util::{Key, VecMap}; +use crate::INTERNAL_ERROR_MSG; + +#[derive(PartialEq, Debug)] +enum UsageToken { + Name, + ValName, + Short, + Long, + Help, + Multiple, + Unknown, +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct UsageParser<'a> { + usage: &'a str, + pos: usize, + start: usize, + prev: UsageToken, + explicit_name_set: bool, +} + +impl<'a> UsageParser<'a> { + fn new(usage: &'a str) -> Self { + debugln!("UsageParser::new: usage={:?}", usage); + UsageParser { + usage, + pos: 0, + start: 0, + prev: UsageToken::Unknown, + explicit_name_set: false, + } + } + + pub fn from_usage(usage: &'a str) -> Self { + debugln!("UsageParser::from_usage;"); + UsageParser::new(usage) + } + + pub fn parse(mut self) -> Arg<'a> { + debugln!("UsageParser::parse;"); + let mut arg = Arg::default(); + arg.disp_ord = 999; + arg.unified_ord = 999; + loop { + debugln!("UsageParser::parse:iter: pos={};", self.pos); + self.stop_at(token); + if let Some(&c) = self.usage.as_bytes().get(self.pos) { + match c { + b'-' => self.short_or_long(&mut arg), + b'.' => self.multiple(&mut arg), + b'\'' => self.help(&mut arg), + _ => self.name(&mut arg), + } + } else { + break; + } + } + arg.num_vals = match arg.val_names { + Some(ref v) if v.len() >= 2 => Some(v.len() as u64), + _ => None, + }; + if !arg.has_switch() && arg.is_set(ArgSettings::MultipleOccurrences) { + // We had a positional and need to set mult vals too + arg.setb(ArgSettings::MultipleValues); + } + debugln!("UsageParser::parse: vals...{:?}", arg.val_names); + arg + } + + fn name(&mut self, arg: &mut Arg<'a>) { + debugln!("UsageParser::name;"); + if *self + .usage + .as_bytes() + .get(self.pos) + .expect(INTERNAL_ERROR_MSG) + == b'<' + && !self.explicit_name_set + { + arg.setb(ArgSettings::Required); + } + self.pos += 1; + self.stop_at(name_end); + let name = &self.usage[self.start..self.pos]; + if self.prev == UsageToken::Unknown { + debugln!("UsageParser::name: setting name...{}", name); + arg.id = name.key(); + arg.name = name; + if arg.long.is_none() && arg.short.is_none() { + debugln!("UsageParser::name: explicit name set..."); + self.explicit_name_set = true; + self.prev = UsageToken::Name; + } + } else { + debugln!("UsageParser::name: setting val name...{}", name); + if let Some(ref mut v) = arg.val_names { + let len = v.len(); + v.insert(len, name); + } else { + let mut v = VecMap::new(); + v.insert(0, name); + arg.val_names = Some(v); + arg.setb(ArgSettings::TakesValue); + } + self.prev = UsageToken::ValName; + } + } + + fn stop_at(&mut self, f: F) + where + F: Fn(u8) -> bool, + { + debugln!("UsageParser::stop_at;"); + self.start = self.pos; + self.pos += self.usage[self.start..] + .bytes() + .take_while(|&b| f(b)) + .count(); + } + + fn short_or_long(&mut self, arg: &mut Arg<'a>) { + debugln!("UsageParser::short_or_long;"); + self.pos += 1; + if *self + .usage + .as_bytes() + .get(self.pos) + .expect(INTERNAL_ERROR_MSG) + == b'-' + { + self.pos += 1; + self.long(arg); + return; + } + self.short(arg) + } + + fn long(&mut self, arg: &mut Arg<'a>) { + debugln!("UsageParser::long;"); + self.stop_at(long_end); + let name = &self.usage[self.start..self.pos]; + if !self.explicit_name_set { + debugln!("UsageParser::long: setting name...{}", name); + arg.id = name.key(); + arg.name = name; + } + debugln!("UsageParser::long: setting long...{}", name); + arg.long = Some(name); + self.prev = UsageToken::Long; + } + + fn short(&mut self, arg: &mut Arg<'a>) { + debugln!("UsageParser::short;"); + let start = &self.usage[self.pos..]; + let short = start.chars().nth(0).expect(INTERNAL_ERROR_MSG); + debugln!("UsageParser::short: setting short...{}", short); + arg.short = Some(short); + if arg.name.is_empty() { + // --long takes precedence but doesn't set self.explicit_name_set + let name = &start[..short.len_utf8()]; + debugln!("UsageParser::short: setting name...{}", name); + arg.id = name.key(); + arg.name = name; + } + self.prev = UsageToken::Short; + } + + // "something..." + fn multiple(&mut self, arg: &mut Arg) { + debugln!("UsageParser::multiple;"); + let mut dot_counter = 1; + let start = self.pos; + let mut bytes = self.usage[start..].bytes(); + while bytes.next() == Some(b'.') { + dot_counter += 1; + self.pos += 1; + if dot_counter == 3 { + debugln!("UsageParser::multiple: setting multiple"); + if arg.is_set(ArgSettings::TakesValue) { + arg.setb(ArgSettings::MultipleValues); + } + arg.setb(ArgSettings::MultipleOccurrences); + self.prev = UsageToken::Multiple; + self.pos += 1; + break; + } + } + } + + fn help(&mut self, arg: &mut Arg<'a>) { + debugln!("UsageParser::help;"); + self.stop_at(help_start); + self.start = self.pos + 1; + self.pos = self.usage.len() - 1; + debugln!( + "UsageParser::help: setting help...{}", + &self.usage[self.start..self.pos] + ); + arg.help = Some(&self.usage[self.start..self.pos]); + self.pos += 1; // Move to next byte to keep from thinking ending ' is a start + self.prev = UsageToken::Help; + } +} + +#[inline] +fn name_end(b: u8) -> bool { b != b']' && b != b'>' } + +#[inline] +fn token(b: u8) -> bool { b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'-' } + +#[inline] +fn long_end(b: u8) -> bool { + b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'=' && b != b' ' +} + +#[inline] +fn help_start(b: u8) -> bool { b != b'\'' } + +#[cfg(test)] +mod test { + use crate::build::{Arg, ArgSettings}; + + #[test] + fn create_flag_usage() { + let a = Arg::from("[flag] -f 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("[flag] --flag 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.long.unwrap(), "flag"); + assert!(a.short.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("--flag 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.long.unwrap(), "flag"); + assert!(a.short.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("[flag] -f --flag 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.short.unwrap(), 'f'); + assert_eq!(a.long.unwrap(), "flag"); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("[flag] -f... 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("[flag] -f --flag... 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.long.unwrap(), "flag"); + assert_eq!(a.short.unwrap(), 'f'); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("-f --flag... 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.long.unwrap(), "flag"); + assert_eq!(a.short.unwrap(), 'f'); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("--flags"); + assert_eq!(a.name, "flags"); + assert_eq!(a.long.unwrap(), "flags"); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("--flags..."); + assert_eq!(a.name, "flags"); + assert_eq!(a.long.unwrap(), "flags"); + assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("[flags] -f"); + assert_eq!(a.name, "flags"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("[flags] -f..."); + assert_eq!(a.name, "flags"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("-f 'some help info'"); + assert_eq!(a.name, "f"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("-f"); + assert_eq!(a.name, "f"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let a = Arg::from("-f..."); + assert_eq!(a.name, "f"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + } + + #[test] + fn create_option_usage0() { + // Short only + let a = Arg::from("[option] -o [opt] 'some help info'"); + assert_eq!(a.name, "option"); + assert_eq!(a.short.unwrap(), 'o'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_set(ArgSettings::MultipleValues)); + assert!(a.is_set(ArgSettings::TakesValue)); + assert!(!a.is_set(ArgSettings::Required)); + assert_eq!(a.val_names.unwrap().values().collect::>(), [&"opt"]); + assert!(a.num_vals.is_none()); + } + + #[test] + fn create_option_usage1() { + let a = Arg::from("-o [opt] 'some help info'"); + assert_eq!(a.name, "o"); + assert_eq!(a.short.unwrap(), 'o'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_set(ArgSettings::MultipleValues)); + assert!(a.is_set(ArgSettings::TakesValue)); + assert!(!a.is_set(ArgSettings::Required)); + assert_eq!(a.val_names.unwrap().values().collect::>(), [&"opt"]); + assert!(a.num_vals.is_none()); + } + + #[test] + fn create_option_usage2() { + let a = Arg::from("