mirror of
https://github.com/clap-rs/clap
synced 2024-11-14 16:47:21 +00:00
Auto merge of #755 - kbknapp:issues-740,742,744,753,754, r=kbknapp
Issues 740,742,744,753,754
This commit is contained in:
commit
2cde6b5773
43 changed files with 1632 additions and 1775 deletions
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
|
@ -21,7 +21,8 @@ Contributions are always welcome! Please use the following guidelines when contr
|
|||
- `chore` - Catch all or things that have to do with the build system, etc
|
||||
- `examples` - Changes to existing example, or a new example
|
||||
* The `COMPONENT` is optional, and may be a single file, directory, or logical component. Can be omitted if commit applies globally
|
||||
5. Run the tests (`cargo test --no-std-features && cargo test --features yaml`)
|
||||
5. Run the tests (`cargo test --features "yaml unstable"`)
|
||||
5. Run the lints (`cargo build --features lints`) (requires a nightly compiler)
|
||||
6. `git rebase` into concise commits and remove `--fixup`s (`git rebase -i HEAD~NUM` where `NUM` is number of commits back)
|
||||
7. Push your changes back to your fork (`git push origin $your-branch`)
|
||||
8. Create a pull request! (You can also create the pull request first, and we'll merge when ready. This a good way to discuss proposed changes.)
|
||||
|
@ -35,6 +36,7 @@ There are a few goals of `clap` that I'd like to maintain throughout contributio
|
|||
* Remain backwards compatible when possible
|
||||
- If backwards compatibility *must* be broken, use deprecation warnings if at all possible before removing legacy code
|
||||
- This does not apply for security concerns
|
||||
- `clap` officially supports the current stable version of Rust, minus two releases (i.e. if 1.13.0 is current, `clap` must support 1.11.0 and beyond)
|
||||
* Parse arguments quickly
|
||||
- Parsing of arguments shouldn't slow down usage of the main program
|
||||
- This is also true of generating help and usage information (although *slightly* less stringent, as the program is about to exit)
|
||||
|
|
|
@ -3,9 +3,10 @@ language: rust
|
|||
cache: cargo
|
||||
rust:
|
||||
- nightly
|
||||
- nightly-2016-11-06
|
||||
- nightly-2016-11-20
|
||||
- beta
|
||||
- stable
|
||||
- 1.11.0
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
|
|
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -1,3 +1,27 @@
|
|||
<a name="v2.19.0"></a>
|
||||
## v2.19.0 (2016-11-21)
|
||||
|
||||
#### Features
|
||||
|
||||
* allows specifying AllowLeadingHyphen style values, but only for specific args vice command wide ([c0d70feb](https://github.com/kbknapp/clap-rs/commit/c0d70febad9996a77a54107054daf1914c50d4ef), closes [#742](https://github.com/kbknapp/clap-rs/issues/742))
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **Required Unless:** fixes a bug where having required_unless set doesn't work when conflicts are also set ([d20331b6](https://github.com/kbknapp/clap-rs/commit/d20331b6f7940ac3a4e919999f8bb4780875125d), closes [#753](https://github.com/kbknapp/clap-rs/issues/753))
|
||||
* **ZSH Completions:** fixes an issue where zsh completions caused panics if there were no subcommands ([49e7cdab](https://github.com/kbknapp/clap-rs/commit/49e7cdab76dd1ccc07221e360f07808ec62648aa), closes [#754](https://github.com/kbknapp/clap-rs/issues/754))
|
||||
|
||||
#### Improvements
|
||||
|
||||
* **Validators:** improves the error messages for validators ([65eb3385](https://github.com/kbknapp/clap-rs/commit/65eb33859d3ff53e7d3277f02a9d3fd9038a9dfb), closes [#744](https://github.com/kbknapp/clap-rs/issues/744))
|
||||
|
||||
#### Documentation
|
||||
|
||||
* updates the docs landing page ([01e1e33f](https://github.com/kbknapp/clap-rs/commit/01e1e33f377934099a4a725fab5cd6c5ff50eaa2))
|
||||
* adds the macro version back to the readme ([45eb9bf1](https://github.com/kbknapp/clap-rs/commit/45eb9bf130329c3f3853aba0342c2fe3c64ff80f))
|
||||
* fix broken docs links ([808e7cee](https://github.com/kbknapp/clap-rs/commit/808e7ceeb86d4a319bdc270f51c23a64621dbfb3))
|
||||
* **Compatibility Policy:** adds an official compatibility policy to ([760d66dc](https://github.com/kbknapp/clap-rs/commit/760d66dc17310b357f257776624151da933cd25d), closes [#740](https://github.com/kbknapp/clap-rs/issues/740))
|
||||
* **Contributing:** updates the readme to improve the readability and contributing sections ([eb51316c](https://github.com/kbknapp/clap-rs/commit/eb51316cdfdc7258d287ba13b67ef2f42bd2b8f6))
|
||||
|
||||
<a name="v2.18.0"></a>
|
||||
## v2.18.0 (2016-11-05)
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ ansi_term = { version = "~0.9.0", optional = true }
|
|||
term_size = { version = "~0.2.0", optional = true }
|
||||
libc = { version = "~0.2.9", optional = true }
|
||||
yaml-rust = { version = "~0.3.2", optional = true }
|
||||
clippy = { version = "~0.0.98", optional = true }
|
||||
clippy = { version = "~0.0.100", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
regex = "~0.1.69"
|
||||
regex = "~0.1.80"
|
||||
|
||||
[features]
|
||||
default = ["suggestions", "color", "wrap_help"]
|
||||
|
|
477
README.md
477
README.md
|
@ -31,8 +31,12 @@ Table of Contents
|
|||
* [More Information](#more-information)
|
||||
* [Video Tutorials](#video-tutorials)
|
||||
* [How to Contribute](#how-to-contribute)
|
||||
* [Running the tests](#running-the-tests)
|
||||
* [Testing Code](#testing-code)
|
||||
* [Linting Code](#linting-code)
|
||||
* [Debugging Code](#debugging-code)
|
||||
* [Goals](#goals)
|
||||
* [Compatibility Policy](#compatibility-policy)
|
||||
* [Minimum Version of Rust](#minimum-version-of-rust)
|
||||
* [License](#license)
|
||||
* [Recent Breaking Changes](#recent-breaking-changes)
|
||||
* [Deprecations](#deprecations)
|
||||
|
@ -41,288 +45,95 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
|
|||
|
||||
## What's New
|
||||
|
||||
Here's what's new in v2.18.0
|
||||
Here's the highlights for v2.19.0
|
||||
|
||||
* **Arg Setting**: Allows specifying an `AllowLeadingHyphen` style setting for values only for specific args, vice command wide
|
||||
* **Validators:** improves the error messages for validators
|
||||
* Updates the docs landing page
|
||||
* Adds the macro version back to the readme
|
||||
* Fixes some broken docs links
|
||||
* **Compatibility Policy:** adds an official compatibility policy to
|
||||
* **Contributing:** updates the readme to improve the readability and contributing sections
|
||||
* **Required Unless:** fixes a bug where having required_unless set doesn't work when conflicts are also set
|
||||
* **ZSH Completions:** fixes an issue where zsh completions caused panics if there were no subcommands
|
||||
|
||||
Here's the highlights from v2.0.0 to v2.18.0
|
||||
|
||||
* **Completions:** Adds completion support for Microsoft PowerShell! (Thanks to @Arnavion)
|
||||
|
||||
Here's what's new in v2.17.1
|
||||
|
||||
* Fixes a bug where using low index multiples was propagated to subcommands
|
||||
|
||||
Here's what's new in v2.17.0
|
||||
|
||||
* Allows specifying the second to last positional argument as `multiple(true)` (i.e. things such as `mv <files>... <target>`)
|
||||
* Adds an `App::get_name` and `App::get_bin_name`
|
||||
|
||||
Here's what's new in v2.16.4
|
||||
|
||||
* Fixes bug that caused panic on subcommands with aliases
|
||||
* Conflicting argument errors are now symetrical, meaning more consistent and better usage suggestions
|
||||
* Fixes typo in example `13a_enum_values_automatic`
|
||||
* Fixes failing yaml example (#715)
|
||||
* Fixes the `debug` feature (#716)
|
||||
|
||||
Here's the highlights for v2.16.3
|
||||
|
||||
* Fixes a bug where the derived display order isn't propagated
|
||||
* **yaml-example:** fixes some inconsistent args in the example
|
||||
|
||||
Here's the highlights for v2.16.2
|
||||
|
||||
* Fixes a bug where single quotes are not escaped
|
||||
|
||||
Here's the highlights for v2.16.1
|
||||
|
||||
* **Help Message:** fixes a regression bug where args with multiple(true) threw off alignment
|
||||
|
||||
Here's the highlights for v2.16.0
|
||||
|
||||
* **Completions:** adds automatic ZSH completion script generation support! :tada: :tada:
|
||||
* **AppSettings:** adds new setting `AppSettings::AllowNegativeNumbers` which functions like `AllowLeadingHyphen` except only allows undefined negative numbers to pass parsing.
|
||||
* Stabilize `clap_app!` macro (i.e. no longer need to use `unstable` feature)
|
||||
* Deprecate `App::with_defaults`
|
||||
* One can now alias arguments either visibly (which appears in the help text) or invisibly just like subcommands!
|
||||
* The `from_usage` parser now correctly handles non-ascii names / options and help!
|
||||
* **Value Delimiters:** fixes the confusion around implicitly setting value delimiters. (The default is to *not* use a delimiter unless explicitly set)
|
||||
* Changes the default value delimiter rules (i.e. the default is `use_delimiter(false)` *unless* a setting/method that implies multiple values was used) **[Bugfix that *may* "break" code]**
|
||||
* If code breaks, simply add `Arg::use_delimiter(true)` to the affected args
|
||||
* Adds ability to hide the possible values from the help text on a per argument basis, instead of command wide
|
||||
* Allows for limiting detected terminal width (i.e. wrap at `x` length, unless the terminal width is *smaller*)
|
||||
* `clap` now ignores hard newlines in help messages and properly re-aligns text, but still wraps if the term width is too small
|
||||
* Adds support for the setting `Arg::require_delimiter` from YAML
|
||||
* `clap` no longer requires one to use `{n}` inside help text to insert a newline that is properly aligned. One can now use the normal `\n`.
|
||||
* `clap` now ignores hard newlines in help messages and properly re-aligns text, but still wraps if the term width is too small
|
||||
* Errors can now have custom description
|
||||
* Uses `term_size` instead of home-grown solution on Windows
|
||||
* Adds the ability to wrap help text intelligently on Windows!
|
||||
* Moves docs to [docs.rs!](https://docs.rs/clap/)!
|
||||
* Automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long
|
||||
* Vastly improves *development* error messages when using YAML
|
||||
* Adds a shorthand way to ignore help text wrapping and use source formatting (i.e. `App::set_term_width(0)`)
|
||||
* **Help Subcommand:** fixes misleading usage string when using multi-level subcommmands such as `myprog help subcmd1 subcmd2`
|
||||
* **YAML:** allows using lists or single values with certain arg declarations for increased ergonomics
|
||||
* **Fish Shell Completions:** one can generate a basic fish completions script at compile time!
|
||||
* Adds the ability to generate completions to an `io::Write` object
|
||||
* Adds an `App::unset_setting` and `App::unset_settings`
|
||||
* **Completions:** one can now [generate a bash completions](https://docs.rs/clap/2.9.0/clap/struct.App.html#method.gen_completions) script at compile time! These completions work with options using [possible values](https://docs.rs/clap/2.9.0/clap/struct.Arg.html#method.possible_values), [subcommand aliases](https://docs.rs/clap/2.9.0/clap/struct.App.html#method.aliases), and even multiple levels of subcommands
|
||||
* **Arg:** adds new optional setting [`Arg::require_delimiter`](https://docs.rs/clap/2.8.0/clap/struct.Arg.html#method.require_delimiter) which requires val delimiter to parse multiple values
|
||||
* The terminal sizing portion has been factored out into a separate crate, [term_size](https://crates.io/crates/term_size)
|
||||
* Options using multiple values and delimiters no longer parse additional values after a trailing space (i.e. `prog -o 1,2 file.txt` parses as `1,2` for `-o` and `file.txt` for a positional arg)
|
||||
* Using options using multiple values and with an `=` no longer parse args after the trailing space as values (i.e. `prog -o=1 file.txt` parses as `1` for `-o` and `file.txt` for a positional arg)
|
||||
* **Usage Strings:** `[FLAGS]` and `[ARGS]` are no longer blindly added to usage strings, instead only when applicable
|
||||
* `arg_enum!`: allows using more than one meta item, or things like `#[repr(C)]` with `arg_enum!`s
|
||||
* `App::print_help`: now prints the same as would have been printed by `--help` or the like
|
||||
* Prevents invoking `<cmd> help help` and displaying incorrect help message
|
||||
* Subcommand help messages requested via `<cmd> help <sub>` now correctly match `<cmd> <sub> --help`
|
||||
* One can now specify groups which require AT LEAST one of the args
|
||||
* Allows adding multiple ArgGroups per Arg
|
||||
* **Global Settings:** One can now set an `AppSetting` which is propogated down through child subcommands
|
||||
* **Terminal Wrapping:** Allows wrapping at specified term width (Even on Windows!) (can now set an absolute width to "smart" wrap at)
|
||||
* **SubCommands/Aliases:** adds support for visible aliases for subcommands (i.e. aliases that are dipslayed in the help message)
|
||||
* **Subcommands/Aliases:** when viewing the help of an alias, it now display help of the aliased subcommand
|
||||
* Adds new setting to stop delimiting values with `--` or `AppSettings::TrailingVarArg`
|
||||
* Subcommands now support aliases - think of them as hidden subcommands that dispatch to said subcommand automatically
|
||||
* Fixed times when `ArgGroup`s are duplicated in usage strings
|
||||
* **Before Help:** adds support for displaying info before help message
|
||||
* **Required Unless:** adds support for allowing args that are required unless certain other args are present
|
||||
* **New Help Template Engine!**: Now you have full control over the layout of your help message. Major thanks to @hgrecco
|
||||
* **Pull crate Authors from Cargo.toml**: One can now use the `crate_authors!` macro to automatically pull the crate authors from their Cargo.toml file
|
||||
* **Colored Help Messages**: Help messages can now be optionally colored (See the `AppSettings::ColoredHelp` setting). Screenshot below.
|
||||
* **Help text auto wraps and aligns at for subcommands too!** - Long help strings of subcommands will now properly wrap and align to term width on Linux and OS X. This can be turned off as well.
|
||||
* **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OS X (and presumably Unix too). This can be turned off as well.
|
||||
* **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arrange the order of your args and subcommands in help message. This helps to emphasize more popular or important options.
|
||||
* **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order!
|
||||
* **Help subcommand now accepts other subcommands as arguments!** - Similar to other CLI precedents, the `help` subcommand can now accept other subcommands as arguments to display their help message. i.e. `$ myprog help mysubcmd` (*Note* these can even be nested heavily such as `$ myprog help subcmd1 subcmd2 subcmd3` etc.)
|
||||
* **Default Values**: Args can now specify default values
|
||||
* **Next Line Help**: Args can have help strings on the line following the argument (useful for long arguments, or those with many values). This can be set command-wide or for individual args
|
||||
|
||||
Here's a gif of them in action!
|
||||
|
||||
![zsh-comppletions](http://i.imgur.com/rwlMbAv.gif)
|
||||
|
||||
Here's the highlights for v2.15.0
|
||||
An example of the help text wrapping at term width:
|
||||
|
||||
* **AppSettings:** adds new setting `AppSettings::AllowNegativeNumbers` which functions like `AllowLeadingHyphen` except only allows undefined negative numbers to pass parsing.
|
||||
* Improves some of the documentation of `AppSettings` by moving variants into roughly alphabetical order
|
||||
|
||||
Here's the highlights for v2.14.1 (Huge thanks to all the contributors who put in a lot of work this cycle! Especially @tormol @nabijaczleweli and @wdv4758h)
|
||||
|
||||
* Stabilize `clap_app!` macro (i.e. no longer need to use `unstable` feature)
|
||||
* Fixes a bug that made determining when to auto-wrap long help messages inconsistent
|
||||
* Fixes fish completions for nested subcommands
|
||||
* Improve documentation around features
|
||||
* Reword docs for `ErrorKind` and `App::settings`
|
||||
* Fix tests that fail when the `suggestions` feature is disabled
|
||||
* Fix the `OsString`-using doc-tests
|
||||
* Tag non-rust code blocks as such instead of ignoring them
|
||||
* Improve some errors about subcommands
|
||||
* Makes sure the doc-tests don't fail before "missing file" in YAML tests
|
||||
* Deprecate `App::with_defaults`
|
||||
* Make lints not enable other nightly-requiring features
|
||||
|
||||
Here's the highlights for v2.14.0
|
||||
|
||||
* One can now alias arguments either visibly (whichc appears in the help text) or invisibly just like subcommands!
|
||||
* The `from_usage` parser now correctly handles non-ascii names / options and help!
|
||||
* Fixes a bug in the `require_delimiter` code which caused some incorrect parses
|
||||
* Fixes various typos in the docs
|
||||
* Various other small performance improvements and enhancements
|
||||
|
||||
Here's the highlights for v2.13.0
|
||||
|
||||
* **Value Delimiters:** fixes the confusion around implicitly setting value delimiters. (The default is to *not* use a delimiter unless explicitly set)
|
||||
* **Docs:** Updates README.md with new website information and updated video tutorials info
|
||||
* **Docs:** Updates the docs about removing implicit `value_delimiter(true)`
|
||||
* **Docs:** Adds better examples on using default values
|
||||
|
||||
|
||||
Here's the highlights for v2.12.1
|
||||
|
||||
* Fixes a regression-bug where the old `{n}` newline char stopped being replaced a properly re-aligned newline
|
||||
|
||||
Here's the highlights for v2.12.0
|
||||
|
||||
* Changes the default value delimiter rules (i.e. the default is `use_delimiter(false)` *unless* a setting/method that implies multiple values was used) **[Bugfix that *may* "break" code]**
|
||||
* If code breaks, simply add `Arg::use_delimiter(true)` to the affected args
|
||||
* Updates the docs for the `Arg::multiple` method WRT value delimiters and default settings
|
||||
* Adds ability to hide the possible values from the help text on a per argument basis, instead of command wide
|
||||
* Allows for limiting detected terminal width (i.e. wrap at `x` length, unless the terminal width is *smaller*)
|
||||
* Removes some redundant `contains()` checks for minor performance improvements
|
||||
* Fixes a bug where valid args aren't recognized with the `AppSettings::AllowLeadingHyphen` setting
|
||||
* `clap` now ignores hard newlines in help messages and properly re-aligns text, but still wraps if the term width is too small
|
||||
* Makes some minor changes to when next line help is automatically used
|
||||
* Adds support for the setting `Arg::require_delimiter` from YAML
|
||||
* Removes the verbage about using `'{n}'` to insert newlines in help text from the docs (the normal `\n` can now be used)
|
||||
* Documents `AppSetting::DisableVersion`
|
||||
|
||||
Here's the highlights for v2.11.3
|
||||
|
||||
* `clap` no longer requires one to use `{n}` inside help text to insert a newline that is properly aligned. One can now use the normal `\n`.
|
||||
* `clap` now ignores hard newlines in help messages and properly re-aligns text, but still wraps if the term width is too small
|
||||
* Supports setting `Arg::require_delimiter` from YAML
|
||||
|
||||
Here's the highlights for v2.11.2
|
||||
|
||||
* Makes some minor changes to when next line help is automatically used for improved wrapping
|
||||
|
||||
Here's the highlights for v2.11.1
|
||||
|
||||
* Fixes an issue where settings weren't propogated down through grand-child subcommands
|
||||
* Errors can now have custom description
|
||||
* Uses `term_size` instead of home-grown solution on Windows
|
||||
* Updates deps with some minor bug fixes
|
||||
|
||||
|
||||
Here's the highlights for v2.11.0
|
||||
|
||||
* Adds the ability to wrap help text intelligently on Windows!
|
||||
* Moves docs to [docs.rs!](https://docs.rs/clap/)
|
||||
* Fixes some usage strings that contain both args in groups and ones that conflict with each other
|
||||
* Uses standard conventions for bash completion files, namely `{bin}.bash-completion`
|
||||
* Automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long
|
||||
* Vastly improves *development* error messages when using YAML
|
||||
* Adds `App::with_defaults` to automatically use `crate_authors!` and `crate_version!` macros
|
||||
* Other minor improvements and bug fixes
|
||||
|
||||
Here's the highlights for v2.10.4
|
||||
|
||||
* Fixes a bug where help is wrapped incorrectly and causing a panic with some non-English characters
|
||||
|
||||
Here's the highlights for v2.10.3
|
||||
|
||||
* Fixes a bug with non-English characters in help text wrapping, where the character is stripped or causes a panic
|
||||
* Fixes an issue with `strsim` which caused a panic in some scenarios
|
||||
* Adds a shorthand way to ignore help text wrapping and use source formatting (i.e. `App::set_term_width(0)`)
|
||||
|
||||
Here's the highlights for v2.10.2
|
||||
|
||||
* Fixes a critical bug where the help message is printed twice
|
||||
|
||||
Here's the highlights for v2.10.1
|
||||
|
||||
* **Help Subcommand:** fixes misleading usage string when using multi-level subcommmands such as `myprog help subcmd1 subcmd2`
|
||||
* **YAML:** allows using lists or single values with certain arg declarations for increased ergonomics
|
||||
|
||||
|
||||
Here's the highlights for v2.10.0
|
||||
|
||||
|
||||
* **Fish Shell Completions:** one can generate a basic fish completions script at compile time!
|
||||
* **External SubCommands:** fixes a bug which now correctly preserves external subcommand name along with args to said command (Minor breaking change that breaks no known real world code)
|
||||
* **YAML Documentation:** fixes example 17's incorrect reference to arg_groups instead of groups
|
||||
|
||||
|
||||
Here's the highlights for v2.9.3
|
||||
|
||||
* Adds the ability to generate completions to an `io::Write` object
|
||||
* Adds an `App::unset_setting` and `App::unset_settings`
|
||||
* Fixes bug where only first arg in list of `required_unless_one` is recognized
|
||||
* Fixes a typo bug `SubcommandsRequired`->`SubcommandRequired`
|
||||
|
||||
|
||||
Here's the highlights for v2.9.2
|
||||
|
||||
|
||||
* fixes bug where --help and --version short weren't added to the completion list
|
||||
* improves completions allowing multiple bins to have seperate completion files
|
||||
|
||||
Here's the highlights for v2.9.0
|
||||
|
||||
* **Completions:** one can now [generate a bash completions](https://docs.rs/clap/2.9.0/clap/struct.App.html#method.gen_completions) script at compile time! These completions work with options using [possible values](https://docs.rs/clap/2.9.0/clap/struct.Arg.html#method.possible_values), [subcommand aliases](https://docs.rs/clap/2.9.0/clap/struct.App.html#method.aliases), and even multiple levels of subcommands
|
||||
* Minor bug fixes when using `AppSettings::TrailingVarArg` and `AppSettings::AllowLeadingHyphen`
|
||||
|
||||
Here's the highlights for v2.8.0
|
||||
|
||||
* **Arg:** adds new optional setting [`Arg::require_delimiter`](https://docs.rs/clap/2.8.0/clap/struct.Arg.html#method.require_delimiter) which requires val delimiter to parse multiple values
|
||||
* The terminal sizing portion has been factored out into a separate crate, [term_size](https://crates.io/crates/term_size)
|
||||
* Minor bug fixes
|
||||
|
||||
|
||||
Here's the highlights for v2.7.1
|
||||
|
||||
* **Options:**
|
||||
* options using multiple values and delimiters no longer parse additional values after a trailing space (i.e. `prog -o 1,2 file.txt` parses as `1,2` for `-o` and `file.txt` for a positional arg)
|
||||
* using options using multiple values and with an `=` no longer parse args after the trailing space as values (i.e. `prog -o=1 file.txt` parses as `1` for `-o` and `file.txt` for a positional arg)
|
||||
|
||||
Here's the highlights for v2.7.0
|
||||
|
||||
* **Usage Strings:** `[FLAGS]` and `[ARGS]` are no longer blindly added to usage strings, instead only when applicable
|
||||
* `arg_enum!`: allows using more than one meta item, or things like `#[repr(C)]` with `arg_enum!`s
|
||||
* `App::print_help`: now prints the same as would have been printed by `--help` or the like
|
||||
* **Help Messages:**
|
||||
* prevents invoking `<cmd> help help` and displaying incorrect help message
|
||||
* subcommand help messages requested via `<cmd> help <sub>` now correctly match `<cmd> <sub> --help`
|
||||
* **`ArgGroup`s:**
|
||||
* one can now specify groups which require AT LEAST one of the args
|
||||
* allows adding multiple ArgGroups per Arg
|
||||
* **Documentation:** vastly improves `ArgGroup` docs by adding better examples
|
||||
* **Documentation:** fixes a bunch of typos in the documentation
|
||||
|
||||
Here's the highlights for v2.6.0
|
||||
|
||||
* **Global Settings:** One can now set an `AppSetting` which is propogated down through child subcommands
|
||||
* **Terminal Wrapping:** Allows wrapping at specified term width (Even on Windows!) (can now set an absolute width to "smart" wrap at)
|
||||
* **SubCommands/Aliases:** adds support for visible aliases for subcommands (i.e. aliases that are dipslayed in the help message)
|
||||
* **Subcommands/Aliases:** when viewing the help of an alias, it now display help of the aliased subcommand
|
||||
* Improves the default usage string when only a single positional arg is present
|
||||
* Adds new setting to stop delimiting values with `--` or `AppSettings::TrailingVarArg`
|
||||
* `App::before_help` and `App::after_help` now correctly wrap
|
||||
* Fixes bug where positional args are printed out of order when using templates
|
||||
* Fixes bug where one can't override the auto-generated version or help flags
|
||||
* Fixes issue where `App::before_help` wasn't printed
|
||||
* Fixes a failing windows build
|
||||
* Fixes bug where new color settings couldn't be converted from strings
|
||||
* Adds missing YAML methods for App and Arg
|
||||
* Allows printing version to any io::Write object
|
||||
* Removes extra newline from help and version output
|
||||
|
||||
Here's what's new in v.2.5.2
|
||||
|
||||
* Removes trailing newlines from help and version output
|
||||
* Allows printing version to any io::Write object
|
||||
* Inter-links all types and pages
|
||||
* Makes all publicly available types viewable in docs
|
||||
* Fixes bug where one can't override version or help flags
|
||||
* Fixes bug where args are printed out of order when using templates
|
||||
* Fixes issue where `App::before_help` wasn't printed properly
|
||||
|
||||
Here's what's new in v.2.5.0
|
||||
|
||||
* Subcommands now support aliases - think of them as hidden subcommands that dispatch to said subcommand automatically
|
||||
|
||||
Here's what's new in v2.4.3
|
||||
|
||||
* Bug Fixes
|
||||
* Usage strings get de-deuplicated when there are args which are also part ``ArgGroup`s`
|
||||
* Fixed times when `ArgGroup`s are duplicated in usage strings
|
||||
* Improvements
|
||||
* Positional arguments which are part of a group are now formatted in a more readable way (fewer brackets)
|
||||
* Positional arguments use the standard `<>` brackets to reduce confusion
|
||||
* The default help string for the `help` subcommand has been shortened to fit in 80 columns
|
||||
|
||||
Here's the highlights from v2.4.0
|
||||
|
||||
* **Before Help:** adds support for displaying info before help message
|
||||
* **Required Unless:** adds support for allowing args that are required unless certain other args are present
|
||||
* Bug fixes
|
||||
|
||||
Here's the highlights from v2.3.0
|
||||
|
||||
* **New Help Template Engine!**: Now you have full control over the layout of your help message. Major thanks to @hgrecco
|
||||
* **Pull crate Authors from Cargo.toml**: One can now use the `crate_authors!` macro to automatically pull the crate authors from their Cargo.toml file
|
||||
* **Colored Help Messages**: Help messages can now be optionally colored (See the `AppSettings::ColoredHelp` setting). Screenshot below.
|
||||
* A bunch of bug fixes
|
||||
|
||||
Here's the highlights from v2.2.1
|
||||
|
||||
* **Help text auto wraps and aligns at for subcommands too!** - Long help strings of subcommands will now properly wrap and align to term width on Linux and OS X. This can be turned off as well.
|
||||
* Bug fixes
|
||||
![screenshot](http://i.imgur.com/PAJzJJG.png)
|
||||
|
||||
An example of the optional colored help:
|
||||
|
||||
![screenshot](http://i.imgur.com/7fs2h5j.png)
|
||||
|
||||
Here's the highlights from v2.2.0
|
||||
|
||||
* **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OS X (and presumably Unix too). This can be turned off as well.
|
||||
* **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arrange the order of your args and subcommands in help message. This helps to emphasize more popular or important options.
|
||||
* **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order!
|
||||
* **Help subcommand now accepts other subcommands as arguments!** - Similar to other CLI precedents, the `help` subcommand can now accept other subcommands as arguments to display their help message. i.e. `$ myprog help mysubcmd` (*Note* these can even be nested heavily such as `$ myprog help subcmd1 subcmd2 subcmd3` etc.)
|
||||
|
||||
* Other minor bug fixes
|
||||
|
||||
An example of the help text wrapping at term width:
|
||||
|
||||
![screenshot](http://i.imgur.com/PAJzJJG.png)
|
||||
|
||||
In v2.1.2
|
||||
|
||||
* **Default Values**: Args can now specify default values
|
||||
* **Next Line Help**: Args can have help strings on the line following the argument (useful for long arguments, or those with many values). This can be set command-wide or for individual args
|
||||
* **Documentation Examples**: The examples in the documentation have been vastly improved
|
||||
|
||||
For full details, see [CHANGELOG.md](https://github.com/kbknapp/clap-rs/blob/master/CHANGELOG.md)
|
||||
|
||||
|
@ -417,9 +228,9 @@ Below are a few of the features which `clap` supports, full descriptions and usa
|
|||
|
||||
The following examples show a quick example of some of the very basic functionality of `clap`. For more advanced usage, such as requirements, conflicts, groups, multiple values and occurrences see the [documentation](https://docs.rs/clap/), [examples/](examples) directory of this repository or the [video tutorials](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U).
|
||||
|
||||
**NOTE:** All these examples are functionally the same, but show three different styles in which to use `clap`
|
||||
**NOTE:** All of these examples are functionally the same, but show different styles in which to use `clap`
|
||||
|
||||
The following example is show a method that 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.
|
||||
The first example shows a method that 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/01b_quick_example.rs)
|
||||
|
@ -489,7 +300,7 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
The following example is functionally the same as the one above, but shows a far less verbose method but sacrifices some of the advanced configuration options (not shown in this small example).
|
||||
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.
|
||||
|
||||
```rust
|
||||
// (Full example with detailed comments in examples/01a_quick_example.rs)
|
||||
|
@ -519,10 +330,10 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
This final method shows how you can use a YAML file to build your CLI 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
|
||||
(we'll use the same both examples above to keep it functionally equivalent):
|
||||
|
||||
First, create the `cli.yml` file to hold your CLI options, but it could be called anything we like:
|
||||
|
||||
```yaml
|
||||
name: myapp
|
||||
|
@ -555,8 +366,9 @@ subcommands:
|
|||
help: print debug information
|
||||
```
|
||||
|
||||
Since this feature is not compiled in by default we need to enable a feature flag in Cargo.toml:
|
||||
Simply change your `clap = "2"` to `clap = {version = "2", features = ["yaml"]}`.
|
||||
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.19.0"` to `clap = {version = "~2.19.0", features = ["yaml"]}`.
|
||||
|
||||
At last we create our `main.rs` file just like we would have with the previous two examples:
|
||||
|
||||
|
@ -578,6 +390,32 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Finally there is a macro version, which is like a hybrid approach offering the speed of the builder pattern (the first example), but without all the verbosity.
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
fn main() {
|
||||
let matches = clap_app!(myapp =>
|
||||
(version: "1.0")
|
||||
(author: "Kevin K. <kbknapp@gmail.com>")
|
||||
(about: "Does awesome things")
|
||||
(@arg CONFIG: -c --config +takes_value "Sets a custom config file")
|
||||
(@arg INPUT: +required "Sets the input file to use")
|
||||
(@arg debug: -d ... "Sets the level of debugging information")
|
||||
(@subcommand test =>
|
||||
(about: "controls testing features")
|
||||
(version: "1.3")
|
||||
(author: "Someone E. <someone_else@other.com>")
|
||||
(@arg verbose: -v --verbose "Print test information verbosely")
|
||||
)
|
||||
).get_matches();
|
||||
|
||||
// Same as before...
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
```sh
|
||||
|
@ -645,11 +483,11 @@ fn main() {
|
|||
|
||||
## Usage
|
||||
|
||||
For full usage, add `clap` as a dependency in your `Cargo.toml` file to use from crates.io:
|
||||
For full usage, add `clap` as a dependency in your `Cargo.toml` (it is **highly** recommended to use the `~major.minor.patch` style versions in your `Cargo.toml`, for more information see [Compatibility Policy](#compatibility-policy)) to use from crates.io:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
clap = "2"
|
||||
clap = "~2.19.0"
|
||||
```
|
||||
|
||||
Or get the latest changes from the master branch at github:
|
||||
|
@ -677,7 +515,7 @@ To disable these, add this to your `Cargo.toml`:
|
|||
|
||||
```toml
|
||||
[dependencies.clap]
|
||||
version = "2"
|
||||
version = "~2.19.0"
|
||||
default-features = false
|
||||
```
|
||||
|
||||
|
@ -685,7 +523,7 @@ You can also selectively enable only the features you'd like to include, by addi
|
|||
|
||||
```toml
|
||||
[dependencies.clap]
|
||||
version = "2"
|
||||
version = "~2.19.0"
|
||||
default-features = false
|
||||
|
||||
# Cherry-pick the features you'd like to use
|
||||
|
@ -695,6 +533,7 @@ features = [ "suggestions", "color" ]
|
|||
#### Opt-in features
|
||||
|
||||
* **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
|
||||
* **"unstable"**: Enables unstable `clap` features that may change from release to release
|
||||
|
||||
### Dependencies Tree
|
||||
|
||||
|
@ -705,6 +544,7 @@ The following graphic depicts `clap`s dependency graph (generated using [cargo-g
|
|||
* **Blue** Color: Dev dependency, only used while developing.
|
||||
|
||||
![clap dependencies](clap_dep_graph.png)
|
||||
|
||||
### More Information
|
||||
|
||||
You can find complete documentation on the [docs.rs](https://docs.rs/clap/) for this project.
|
||||
|
@ -725,20 +565,65 @@ Another really great way to help is if you find an interesting, or helpful way i
|
|||
|
||||
Please read [CONTRIBUTING.md](.github/CONTRIBUTING.md) before you start contributing.
|
||||
|
||||
|
||||
### Testing Code
|
||||
|
||||
To test with all features both enabled and disabled, you can run theese commands:
|
||||
|
||||
```sh
|
||||
$ cargo test --no-default-features
|
||||
$ cargo test --features yaml
|
||||
$ cargo test --features "yaml unstable"
|
||||
```
|
||||
|
||||
If you have a nightly compiler you can append `--features lints` to both commands
|
||||
to get style warnings and code smells; If you get one from code you think is fine,
|
||||
you can ignore it by prepending `#[cfg_attr(feature="lints", allow(lint_name))]`
|
||||
to the function or impl block.
|
||||
Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the prebuilt recipies. *Not* using `just` is prfeclty fine as well, it simply bundles commands automatically.
|
||||
|
||||
If you are debugging (or just trying to understand the code) you can enable the
|
||||
"debug" feature which will trace function calls and brances in some parts of the code.
|
||||
For example, to test the code, as above simply run:
|
||||
|
||||
```sh
|
||||
$ just run-tests`
|
||||
```
|
||||
|
||||
From here on, I will lis the appropriate `cargo` command as well as the `just` command.
|
||||
|
||||
Sometimes it's helpful to only run a subset of the tests, which can be done via:
|
||||
|
||||
```sh
|
||||
$ cargo test --test <test_name>
|
||||
|
||||
# Or
|
||||
|
||||
$ just run-test <test_name>
|
||||
```
|
||||
|
||||
### Linting Code
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
$ rustup override add nightly
|
||||
$ cargo build --features lints
|
||||
$ rustup override remove
|
||||
|
||||
# Or
|
||||
|
||||
$ just lint
|
||||
```
|
||||
|
||||
### Debugging Code
|
||||
|
||||
Another helpful technique is to see the `clap` debug output while developing features. In order to see the debug output while running the full test suite or individual tests, run:
|
||||
|
||||
```sh
|
||||
$ cargo test --features debug
|
||||
|
||||
# Or for individual tests
|
||||
$ cargo test --test <test_name> --features debug
|
||||
|
||||
# The corresponding just command for individual debugging tests is:
|
||||
$ just debug <test_name>
|
||||
```
|
||||
|
||||
### Goals
|
||||
|
||||
|
@ -754,6 +639,28 @@ There are a few goals of `clap` that I'd like to maintain throughout contributio
|
|||
- Once parsing is complete, the memory footprint of `clap` should be low since the main program is the star of the show
|
||||
* `panic!` on *developer* error, exit gracefully on *end-user* error
|
||||
|
||||
### Compatibility Policy
|
||||
|
||||
Because `clap` takes SemVer and compatibility seriously, this is the official policy regarding breaking changes and previous versions of Rust.
|
||||
|
||||
`clap` will pin the minimum required version of Rust to the CI builds. Bumping the minimum version of Rust is considered a minor breaking change, meaning *at a minimum* the minor version of `clap` will be bumped.
|
||||
|
||||
In order to keep from being suprised of breaking changes, it is **highly** recommended to use the `~major.minor.patch` style in your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
clap = "~2.19.0"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
#### 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.13.0, meaning `clap` is garunteed to compile with 1.11.0 and beyond.
|
||||
At the 1.14.0 release, `clap` will be garunteed to compile with 2.12.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`
|
||||
|
||||
## License
|
||||
|
||||
`clap` is licensed under the MIT license. Please read the [LICENSE-MIT](LICENSE-MIT) file in this repository for more information.
|
||||
|
|
21
justfile
21
justfile
|
@ -11,9 +11,26 @@
|
|||
run-test TEST:
|
||||
cargo test --test {{TEST}}
|
||||
|
||||
debug TEST:
|
||||
cargo test --test {{TEST}} --features debug
|
||||
|
||||
run-tests:
|
||||
cargo test --features "yaml unstable"
|
||||
|
||||
lint:
|
||||
@bench: nightly
|
||||
cargo bench && just remove-nightly
|
||||
|
||||
nightly:
|
||||
rustup override add nightly
|
||||
cargo build --features lints && rustup override remove
|
||||
|
||||
remove-nightly:
|
||||
rustup override remove
|
||||
|
||||
@lint: nightly
|
||||
cargo build --features lints && just remove-nightly
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
find . -type f -name "*.orig" -exec rm {} \;
|
||||
find . -type f -name "*.bk" -exec rm {} \;
|
||||
find . -type f -name ".*~" -exec rm {} \;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
format_strings = false
|
||||
chain_overflow_last = false
|
||||
same_line_if_else = true
|
||||
fn_single_line = true
|
||||
|
|
|
@ -21,14 +21,10 @@ use vec_map::VecMap;
|
|||
|
||||
#[cfg(not(feature = "wrap_help"))]
|
||||
mod term_size {
|
||||
pub fn dimensions() -> Option<(usize, usize)> {
|
||||
None
|
||||
}
|
||||
pub fn dimensions() -> Option<(usize, usize)> { None }
|
||||
}
|
||||
|
||||
fn str_width(s: &str) -> usize {
|
||||
UnicodeWidthStr::width(s)
|
||||
}
|
||||
fn str_width(s: &str) -> usize { UnicodeWidthStr::width(s) }
|
||||
|
||||
const TAB: &'static str = " ";
|
||||
|
||||
|
@ -42,19 +38,13 @@ trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
|
|||
impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
|
||||
where T: ArgWithDisplay<'b, 'c> + DispOrder
|
||||
{
|
||||
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
|
||||
self
|
||||
}
|
||||
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> { self }
|
||||
}
|
||||
|
||||
fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
|
||||
x
|
||||
}
|
||||
fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> { x }
|
||||
|
||||
impl<'b, 'c> DispOrder for App<'b, 'c> {
|
||||
fn disp_ord(&self) -> usize {
|
||||
999
|
||||
}
|
||||
fn disp_ord(&self) -> usize { 999 }
|
||||
}
|
||||
|
||||
macro_rules! color {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
macro_rules! remove_overriden {
|
||||
(@remove $_self:ident, $v:ident, $a:ident.$ov:ident) => {
|
||||
if let Some(ref ora) = $a.$ov {
|
||||
if let Some(ref ora) = $a.$ov() {
|
||||
vec_remove_all!($_self.$v, ora);
|
||||
}
|
||||
};
|
||||
|
@ -11,11 +11,11 @@ macro_rules! remove_overriden {
|
|||
};
|
||||
($_self:ident, $name:expr) => {
|
||||
debugln!("macro=remove_overriden!;");
|
||||
if let Some(ref o) = $_self.opts.iter().filter(|o| o.name == *$name).next() {
|
||||
if let Some(ref o) = $_self.opts.iter().filter(|o| o.b.name == *$name).next() {
|
||||
remove_overriden!(@arg $_self, o);
|
||||
} else if let Some(ref f) = $_self.flags.iter().filter(|f| f.name == *$name).next() {
|
||||
} else if let Some(ref f) = $_self.flags.iter().filter(|f| f.b.name == *$name).next() {
|
||||
remove_overriden!(@arg $_self, f);
|
||||
} else if let Some(p) = $_self.positionals.values().filter(|p| p.name == *$name).next() {
|
||||
} else if let Some(p) = $_self.positionals.values().filter(|p| p.b.name == *$name).next() {
|
||||
remove_overriden!(@arg $_self, p);
|
||||
}
|
||||
};
|
||||
|
@ -55,20 +55,7 @@ macro_rules! arg_post_processing {
|
|||
if $matcher.contains(c) {
|
||||
sdebugln!("Yes");
|
||||
// find who blacklisted us...
|
||||
$me.blacklist.push(&$arg.name);
|
||||
// if let Some(f) = $me.find_flag_mut(c) {
|
||||
// if let Some(ref mut bl) = f.blacklist {
|
||||
// bl.push(&$arg.name);
|
||||
// }
|
||||
// } else if let Some(o) = $me.find_option_mut(c) {
|
||||
// if let Some(ref mut bl) = o.blacklist {
|
||||
// bl.push(&$arg.name);
|
||||
// }
|
||||
// } else if let Some(p) = $me.find_positional_mut(c) {
|
||||
// if let Some(ref mut bl) = p.blacklist {
|
||||
// bl.push(&$arg.name);
|
||||
// }
|
||||
// }
|
||||
$me.blacklist.push(&$arg.b.name);
|
||||
} else {
|
||||
sdebugln!("No");
|
||||
}
|
||||
|
@ -130,7 +117,7 @@ macro_rules! _handle_group_reqs{
|
|||
macro_rules! validate_multiples {
|
||||
($_self:ident, $a:ident, $m:ident) => {
|
||||
debugln!("macro=validate_multiples!;");
|
||||
if $m.contains(&$a.name) && !$a.settings.is_set(ArgSettings::Multiple) {
|
||||
if $m.contains(&$a.b.name) && !$a.b.settings.is_set(ArgSettings::Multiple) {
|
||||
// Not the first time, and we don't allow multiples
|
||||
return Err(Error::unexpected_multiple_usage($a,
|
||||
&*$_self.create_current_usage($m),
|
||||
|
@ -155,16 +142,14 @@ macro_rules! parse_positional {
|
|||
$pos_counter == $_self.positionals.len()) {
|
||||
$_self.trailing_vals = true;
|
||||
}
|
||||
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
|
||||
return Err(e);
|
||||
}
|
||||
let _ = try!($_self.add_val_to_arg($p, &$arg_os, $matcher));
|
||||
|
||||
$matcher.inc_occurrence_of($p.name);
|
||||
let _ = $_self.groups_for_arg($p.name)
|
||||
$matcher.inc_occurrence_of($p.b.name);
|
||||
let _ = $_self.groups_for_arg($p.b.name)
|
||||
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
|
||||
arg_post_processing!($_self, $p, $matcher);
|
||||
// Only increment the positional counter if it doesn't allow multiples
|
||||
if !$p.settings.is_set(ArgSettings::Multiple) {
|
||||
if !$p.b.settings.is_set(ArgSettings::Multiple) {
|
||||
$pos_counter += 1;
|
||||
}
|
||||
};
|
||||
|
@ -174,24 +159,24 @@ macro_rules! find_from {
|
|||
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
|
||||
let mut ret = None;
|
||||
for k in $matcher.arg_names() {
|
||||
if let Some(f) = $_self.find_flag(k) {
|
||||
if let Some(ref v) = f.$from {
|
||||
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
|
||||
if let Some(ref v) = f.$from() {
|
||||
if v.contains($arg_name) {
|
||||
ret = Some(f.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(o) = $_self.find_option(k) {
|
||||
if let Some(ref v) = o.$from {
|
||||
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
|
||||
if let Some(ref v) = o.$from() {
|
||||
if v.contains(&$arg_name) {
|
||||
ret = Some(o.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pos) = $_self.find_positional(k) {
|
||||
if let Some(ref v) = pos.$from {
|
||||
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
|
||||
if let Some(ref v) = pos.$from() {
|
||||
if v.contains($arg_name) {
|
||||
ret = Some(pos.name.to_owned());
|
||||
ret = Some(pos.b.name.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,28 +189,54 @@ macro_rules! find_name_from {
|
|||
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
|
||||
let mut ret = None;
|
||||
for k in $matcher.arg_names() {
|
||||
if let Some(f) = $_self.find_flag(k) {
|
||||
if let Some(ref v) = f.$from {
|
||||
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
|
||||
if let Some(ref v) = f.$from() {
|
||||
if v.contains($arg_name) {
|
||||
ret = Some(f.name);
|
||||
ret = Some(f.b.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(o) = $_self.find_option(k) {
|
||||
if let Some(ref v) = o.$from {
|
||||
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
|
||||
if let Some(ref v) = o.$from() {
|
||||
if v.contains(&$arg_name) {
|
||||
ret = Some(o.name);
|
||||
ret = Some(o.b.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pos) = $_self.find_positional(k) {
|
||||
if let Some(ref v) = pos.$from {
|
||||
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
|
||||
if let Some(ref v) = pos.$from() {
|
||||
if v.contains($arg_name) {
|
||||
ret = Some(pos.name);
|
||||
ret = Some(pos.b.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
// Finds an arg by name
|
||||
macro_rules! find_by_name {
|
||||
($_self:ident, $name:expr, $what:ident, $how:ident) => {
|
||||
$_self.$what.$how().find(|o| &o.b.name == $name)
|
||||
}
|
||||
}
|
||||
|
||||
// Finds an option including if it's aliasesed
|
||||
macro_rules! find_by_long {
|
||||
($_self:ident, $long:expr, $what:ident) => {
|
||||
$_self.$what
|
||||
.iter()
|
||||
.filter(|o| o.s.long.is_some())
|
||||
.find(|o| {
|
||||
&&o.s.long.unwrap() == &$long ||
|
||||
(o.s.aliases.is_some() &&
|
||||
o.s
|
||||
.aliases
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|&(alias, _)| &&alias == &$long))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,8 @@ impl<'b> Default for AppMeta<'b> {
|
|||
}
|
||||
|
||||
impl<'b> AppMeta<'b> {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
pub fn with_name(s: String) -> Self {
|
||||
AppMeta { name: s, ..Default::default() }
|
||||
}
|
||||
pub fn new() -> Self { Default::default() }
|
||||
pub fn with_name(s: String) -> Self { AppMeta { name: s, ..Default::default() } }
|
||||
}
|
||||
|
||||
impl<'b> Clone for AppMeta<'b> {
|
||||
|
|
108
src/app/mod.rs
108
src/app/mod.rs
|
@ -24,7 +24,7 @@ use yaml_rust::Yaml;
|
|||
// Internal
|
||||
use app::help::Help;
|
||||
use app::parser::Parser;
|
||||
use args::{AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings};
|
||||
use args::{ArgKind, AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings};
|
||||
use errors::Error;
|
||||
use errors::Result as ClapResult;
|
||||
pub use self::settings::AppSettings;
|
||||
|
@ -79,19 +79,13 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// let prog = App::new("My Program")
|
||||
/// # ;
|
||||
/// ```
|
||||
pub fn new<S: Into<String>>(n: S) -> Self {
|
||||
App { p: Parser::with_name(n.into()) }
|
||||
}
|
||||
pub fn new<S: Into<String>>(n: S) -> Self { App { p: Parser::with_name(n.into()) } }
|
||||
|
||||
/// Get the name of the app
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.p.meta.name
|
||||
}
|
||||
pub fn get_name(&self) -> &str { &self.p.meta.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())
|
||||
}
|
||||
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.
|
||||
|
@ -155,9 +149,7 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// [`examples/17_yaml.yml`]: https://github.com/kbknapp/clap-rs/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 from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) }
|
||||
|
||||
/// Sets a string of author(s) that will be displayed to the user when they
|
||||
/// request the help information with `--help` or `-h`.
|
||||
|
@ -1193,9 +1185,7 @@ 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<'a> { self.get_matches_from(&mut env::args_os()) }
|
||||
|
||||
/// 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`]
|
||||
|
@ -1508,75 +1498,37 @@ 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() }
|
||||
}
|
||||
fn clone(&self) -> Self { App { p: self.p.clone() } }
|
||||
}
|
||||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
||||
fn name(&self) -> &'n str {
|
||||
unreachable!("App struct does not support AnyArg::name, this is a bug!")
|
||||
}
|
||||
fn overrides(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn requires(&self) -> Option<&[&'e 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 id(&self) -> usize { self.p.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Subcmd }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { None }
|
||||
fn requires(&self) -> Option<&[&'e 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 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<u64> {
|
||||
None
|
||||
}
|
||||
fn num_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||
None
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn short(&self) -> Option<char> {
|
||||
None
|
||||
}
|
||||
fn long(&self) -> Option<&'e str> {
|
||||
None
|
||||
}
|
||||
fn val_delim(&self) -> Option<char> {
|
||||
None
|
||||
}
|
||||
fn takes_value(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn help(&self) -> Option<&'e str> {
|
||||
self.p.meta.about
|
||||
}
|
||||
fn default_val(&self) -> Option<&'n str> {
|
||||
None
|
||||
}
|
||||
fn longest_filter(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn has_switch(&self) -> bool { false }
|
||||
fn max_vals(&self) -> Option<u64> { None }
|
||||
fn num_vals(&self) -> Option<u64> { None }
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> { None }
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
|
||||
fn min_vals(&self) -> Option<u64> { None }
|
||||
fn short(&self) -> Option<char> { None }
|
||||
fn long(&self) -> Option<&'e str> { None }
|
||||
fn val_delim(&self) -> Option<char> { None }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.p.meta.about }
|
||||
fn default_val(&self) -> Option<&'n str> { None }
|
||||
fn longest_filter(&self) -> bool { true }
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||
if let Some(ref aliases) = self.p.meta.aliases {
|
||||
let vis_aliases: Vec<_> =
|
||||
|
@ -1593,7 +1545,5 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
|||
}
|
||||
|
||||
impl<'n, 'e> fmt::Display for App<'n, 'e> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.p.meta.name)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.p.meta.name) }
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -41,9 +41,7 @@ bitflags! {
|
|||
pub struct AppFlags(Flags);
|
||||
|
||||
impl Clone for AppFlags {
|
||||
fn clone(&self) -> Self {
|
||||
AppFlags(self.0)
|
||||
}
|
||||
fn clone(&self) -> Self { AppFlags(self.0) }
|
||||
}
|
||||
|
||||
impl Default for AppFlags {
|
||||
|
@ -53,9 +51,7 @@ impl Default for AppFlags {
|
|||
}
|
||||
|
||||
impl AppFlags {
|
||||
pub fn new() -> Self {
|
||||
AppFlags::default()
|
||||
}
|
||||
pub fn new() -> Self { AppFlags::default() }
|
||||
|
||||
impl_settings! { AppSettings,
|
||||
ArgRequiredElseHelp => A_REQUIRED_ELSE_HELP,
|
||||
|
@ -363,7 +359,7 @@ pub enum AppSettings {
|
|||
/// ```
|
||||
/// [`SubCommand`]: ./struct.SubCommand.html
|
||||
DisableVersion,
|
||||
|
||||
|
||||
/// Displays the arguments and [`SubCommand`]s in the help message in the order that they were
|
||||
/// declared in, and not alphabetically which is the default.
|
||||
///
|
||||
|
@ -667,7 +663,6 @@ pub enum AppSettings {
|
|||
|
||||
#[doc(hidden)]
|
||||
LowIndexMultiplePositional,
|
||||
|
||||
}
|
||||
|
||||
impl FromStr for AppSettings {
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
// Std
|
||||
use std::rc::Rc;
|
||||
use std::fmt as std_fmt;
|
||||
use std::fmt as std_fmt;
|
||||
|
||||
// Third Party
|
||||
use vec_map::VecMap;
|
||||
|
||||
// Internal
|
||||
use args::settings::ArgSettings;
|
||||
use args::ArgKind;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AnyArg<'n, 'e>: std_fmt::Display {
|
||||
fn name(&self) -> &'n str;
|
||||
fn id(&self) -> usize;
|
||||
fn overrides(&self) -> Option<&[&'e str]>;
|
||||
fn aliases(&self) -> Option<Vec<&'e str>>;
|
||||
fn requires(&self) -> Option<&[&'e str]>;
|
||||
|
@ -32,6 +34,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display {
|
|||
fn help(&self) -> Option<&'e str>;
|
||||
fn default_val(&self) -> Option<&'n str>;
|
||||
fn longest_filter(&self) -> bool;
|
||||
fn kind(&self) -> ArgKind;
|
||||
}
|
||||
|
||||
pub trait DispOrder {
|
||||
|
|
|
@ -54,7 +54,7 @@ pub struct Arg<'a, 'b>
|
|||
#[doc(hidden)]
|
||||
pub requires: Option<Vec<&'a str>>,
|
||||
#[doc(hidden)]
|
||||
pub group: Option<Vec<&'a str>>,
|
||||
pub groups: Option<Vec<&'a str>>,
|
||||
#[doc(hidden)]
|
||||
pub val_names: Option<VecMap<&'b str>>,
|
||||
#[doc(hidden)]
|
||||
|
@ -91,7 +91,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> {
|
|||
blacklist: None,
|
||||
possible_vals: None,
|
||||
requires: None,
|
||||
group: None,
|
||||
groups: None,
|
||||
val_names: None,
|
||||
num_vals: None,
|
||||
max_vals: None,
|
||||
|
@ -126,9 +126,7 @@ 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 {
|
||||
Arg { name: n, ..Default::default() }
|
||||
}
|
||||
pub fn with_name(n: &'a str) -> Self { Arg { name: n, ..Default::default() } }
|
||||
|
||||
/// Creates a new instance of [`Arg`] from a .yml (YAML) file.
|
||||
///
|
||||
|
@ -172,6 +170,7 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
"min_values" => yaml_to_u64!(a, v, min_values),
|
||||
"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),
|
||||
"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),
|
||||
|
@ -636,6 +635,62 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Allows values which start with a leading hyphen (`-`)
|
||||
///
|
||||
/// **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 anther 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;
|
||||
/// Arg::with_name("pattern")
|
||||
/// .allow_hyphen_values(true)
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("pattest")
|
||||
/// .arg(Arg::with_name("pat")
|
||||
/// .allow_hyphen_values(true)
|
||||
/// .takes_value(true)
|
||||
/// .long("pattern"))
|
||||
/// .get_matches_from(vec![
|
||||
/// "pattest", "--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("pattest")
|
||||
/// .arg(Arg::with_name("pat")
|
||||
/// .takes_value(true)
|
||||
/// .long("pattern"))
|
||||
/// .get_matches_from_safe(vec![
|
||||
/// "pattest", "--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
|
||||
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).
|
||||
///
|
||||
|
@ -1725,7 +1780,7 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
if let Some(ref mut vec) = self.requires {
|
||||
vec.push(name);
|
||||
} else {
|
||||
self.group = Some(vec![name]);
|
||||
self.groups = Some(vec![name]);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -1760,12 +1815,12 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
/// ```
|
||||
/// [`ArgGroup`]: ./struct.ArgGroup.html
|
||||
pub fn groups(mut self, names: &[&'a str]) -> Self {
|
||||
if let Some(ref mut vec) = self.group {
|
||||
if let Some(ref mut vec) = self.groups {
|
||||
for s in names {
|
||||
vec.push(s);
|
||||
}
|
||||
} else {
|
||||
self.group = Some(names.into_iter().map(|s| *s).collect::<Vec<_>>());
|
||||
self.groups = Some(names.into_iter().map(|s| *s).collect::<Vec<_>>());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -1815,7 +1870,9 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
/// message displayed to the user.
|
||||
///
|
||||
/// **NOTE:** The error message does *not* need to contain the `error:` portion, only the
|
||||
/// message.
|
||||
/// message as all errors will appear as
|
||||
/// `error: Invalid value for '<arg>': <YOUR MESSAGE>` where `<arg>` is replaced by the actual
|
||||
/// arg, and `<YOUR MESSAGE>` is the `String` you return as the error.
|
||||
///
|
||||
/// **NOTE:** There is a small performance hit for using validators, as they are implemented
|
||||
/// with [`Rc`] pointers. And the value to be checked will be allocated an extra time in order
|
||||
|
@ -2465,9 +2522,7 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
|
||||
/// Checks if one of the [`ArgSettings`] settings is set for the argument
|
||||
/// [`ArgSettings`]: ./enum.ArgSettings.html
|
||||
pub fn is_set(&self, s: ArgSettings) -> bool {
|
||||
self.settings.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
|
||||
/// [`ArgSettings`]: ./enum.ArgSettings.html
|
||||
|
@ -2484,14 +2539,10 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn setb(&mut self, s: ArgSettings) {
|
||||
self.settings.set(s);
|
||||
}
|
||||
pub fn setb(&mut self, s: ArgSettings) { self.settings.set(s); }
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn unsetb(&mut self, s: ArgSettings) {
|
||||
self.settings.unset(s);
|
||||
}
|
||||
pub fn unsetb(&mut self, s: ArgSettings) { self.settings.unset(s); }
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> {
|
||||
|
@ -2510,7 +2561,7 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> {
|
|||
min_vals: a.min_vals,
|
||||
max_vals: a.max_vals,
|
||||
val_names: a.val_names.clone(),
|
||||
group: a.group.clone(),
|
||||
groups: a.groups.clone(),
|
||||
validator: a.validator.clone(),
|
||||
overrides: a.overrides.clone(),
|
||||
settings: a.settings,
|
||||
|
@ -2538,7 +2589,7 @@ impl<'a, 'b> Clone for Arg<'a, 'b> {
|
|||
min_vals: self.min_vals,
|
||||
max_vals: self.max_vals,
|
||||
val_names: self.val_names.clone(),
|
||||
group: self.group.clone(),
|
||||
groups: self.groups.clone(),
|
||||
validator: self.validator.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
settings: self.settings,
|
||||
|
|
57
src/args/arg_builder/base.rs
Normal file
57
src/args/arg_builder/base.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use args::{ArgSettings, Arg, ArgFlags, ArgKind};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Base<'a, 'b>
|
||||
where 'a: 'b
|
||||
{
|
||||
pub name: &'a str,
|
||||
pub id: usize,
|
||||
pub kind: ArgKind,
|
||||
pub help: Option<&'b str>,
|
||||
pub blacklist: Option<Vec<&'a str>>,
|
||||
pub settings: ArgFlags,
|
||||
pub r_unless: Option<Vec<&'a str>>,
|
||||
pub overrides: Option<Vec<&'a str>>,
|
||||
pub groups: Option<Vec<&'a str>>,
|
||||
pub requires: Option<Vec<&'a str>>,
|
||||
}
|
||||
|
||||
impl<'n, 'e> Default for Base<'n, 'e> {
|
||||
fn default() -> Self {
|
||||
Base {
|
||||
name: "",
|
||||
id: 0,
|
||||
kind: ArgKind::Pos,
|
||||
help: None,
|
||||
blacklist: None,
|
||||
settings: ArgFlags::new(),
|
||||
r_unless: None,
|
||||
overrides: None,
|
||||
requires: None,
|
||||
groups: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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); }
|
||||
}
|
||||
|
||||
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Base<'n, 'e> {
|
||||
fn from(a: &'z Arg<'n, 'e>) -> Self {
|
||||
Base {
|
||||
name: a.name,
|
||||
help: a.help,
|
||||
id: 0,
|
||||
kind: ArgKind::Pos,
|
||||
blacklist: a.blacklist.clone(),
|
||||
settings: a.settings,
|
||||
r_unless: a.r_unless.clone(),
|
||||
overrides: a.overrides.clone(),
|
||||
requires: a.requires.clone(),
|
||||
groups: a.groups.clone(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,181 +9,73 @@ use vec_map::VecMap;
|
|||
|
||||
// Internal
|
||||
use Arg;
|
||||
use args::{AnyArg, DispOrder};
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
use args::{ArgSettings, ArgKind, Base, Switched, AnyArg, DispOrder};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
#[doc(hidden)]
|
||||
pub struct FlagBuilder<'n, 'e> {
|
||||
pub name: &'n str,
|
||||
pub long: Option<&'e str>,
|
||||
pub aliases: Option<Vec<(&'e str, bool)>>,
|
||||
pub help: Option<&'e str>,
|
||||
pub blacklist: Option<Vec<&'e str>>,
|
||||
pub requires: Option<Vec<&'e str>>,
|
||||
pub short: Option<char>,
|
||||
pub overrides: Option<Vec<&'e str>>,
|
||||
pub settings: ArgFlags,
|
||||
pub disp_ord: usize,
|
||||
pub unified_ord: usize,
|
||||
}
|
||||
|
||||
impl<'n, 'e> Default for FlagBuilder<'n, 'e> {
|
||||
fn default() -> Self {
|
||||
FlagBuilder {
|
||||
name: "",
|
||||
long: None,
|
||||
aliases: None,
|
||||
help: None,
|
||||
blacklist: None,
|
||||
requires: None,
|
||||
short: None,
|
||||
overrides: None,
|
||||
settings: ArgFlags::new(),
|
||||
disp_ord: 999,
|
||||
unified_ord: 999,
|
||||
}
|
||||
}
|
||||
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 { name: name, ..Default::default() }
|
||||
}
|
||||
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 {
|
||||
assert!(a.validator.is_none(),
|
||||
format!("The argument '{}' has a validator set, yet was parsed as a flag. Ensure \
|
||||
.takes_value(true) or .index(u64) is set.",
|
||||
a.name));
|
||||
assert!(a.possible_vals.is_none(),
|
||||
format!("The argument '{}' cannot have a specific value set because it doesn't \
|
||||
have takes_value(true) set",
|
||||
a.name));
|
||||
assert!(!a.is_set(ArgSettings::Required),
|
||||
format!("The argument '{}' cannot be required because it's a flag, perhaps you \
|
||||
forgot takes_value(true)?",
|
||||
a.name));
|
||||
// No need to check for index() or takes_value() as that is handled above
|
||||
|
||||
FlagBuilder {
|
||||
name: a.name,
|
||||
short: a.short,
|
||||
long: a.long,
|
||||
aliases: a.aliases.clone(),
|
||||
help: a.help,
|
||||
blacklist: a.blacklist.clone(),
|
||||
overrides: a.overrides.clone(),
|
||||
requires: a.requires.clone(),
|
||||
settings: a.settings,
|
||||
disp_ord: a.disp_ord,
|
||||
..Default::default()
|
||||
b: Base::from(a),
|
||||
s: Switched::from(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
if let Some(l) = self.long {
|
||||
if let Some(l) = self.s.long {
|
||||
try!(write!(f, "--{}", l));
|
||||
} else {
|
||||
try!(write!(f, "-{}", self.short.unwrap()));
|
||||
try!(write!(f, "-{}", self.s.short.unwrap()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> Clone for FlagBuilder<'n, 'e> {
|
||||
fn clone(&self) -> Self {
|
||||
FlagBuilder {
|
||||
name: self.name,
|
||||
short: self.short,
|
||||
long: self.long,
|
||||
aliases: self.aliases.clone(),
|
||||
help: self.help,
|
||||
blacklist: self.blacklist.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
requires: self.requires.clone(),
|
||||
settings: self.settings,
|
||||
disp_ord: self.disp_ord,
|
||||
unified_ord: self.unified_ord,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
||||
fn name(&self) -> &'n str {
|
||||
self.name
|
||||
}
|
||||
fn overrides(&self) -> Option<&[&'e str]> {
|
||||
self.overrides.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn requires(&self) -> Option<&[&'e str]> {
|
||||
self.requires.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn blacklist(&self) -> Option<&[&'e str]> {
|
||||
self.blacklist.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn is_set(&self, s: ArgSettings) -> bool {
|
||||
self.settings.is_set(s)
|
||||
}
|
||||
fn has_switch(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn takes_value(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn set(&mut self, s: ArgSettings) {
|
||||
self.settings.set(s)
|
||||
}
|
||||
fn max_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> {
|
||||
None
|
||||
}
|
||||
fn num_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> {
|
||||
None
|
||||
}
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||
None
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
fn short(&self) -> Option<char> {
|
||||
self.short
|
||||
}
|
||||
fn long(&self) -> Option<&'e str> {
|
||||
self.long
|
||||
}
|
||||
fn val_delim(&self) -> Option<char> {
|
||||
None
|
||||
}
|
||||
fn help(&self) -> Option<&'e str> {
|
||||
self.help
|
||||
}
|
||||
fn default_val(&self) -> Option<&'n str> {
|
||||
None
|
||||
}
|
||||
fn longest_filter(&self) -> bool {
|
||||
self.long.is_some()
|
||||
}
|
||||
fn name(&self) -> &'n str { self.b.name }
|
||||
fn id(&self) -> usize { self.b.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Flag }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e 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]> { None }
|
||||
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<u64> { None }
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
||||
fn num_vals(&self) -> Option<u64> { None }
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> { None }
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
|
||||
fn min_vals(&self) -> Option<u64> { None }
|
||||
fn short(&self) -> Option<char> { self.s.short }
|
||||
fn long(&self) -> Option<&'e str> { self.s.long }
|
||||
fn val_delim(&self) -> Option<char> { None }
|
||||
fn help(&self) -> Option<&'e str> { self.b.help }
|
||||
fn default_val(&self) -> Option<&'n str> { None }
|
||||
fn longest_filter(&self) -> bool { self.s.long.is_some() }
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||
if let Some(ref aliases) = self.aliases {
|
||||
let vis_aliases: Vec<_> =
|
||||
aliases.iter()
|
||||
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
||||
.collect();
|
||||
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 {
|
||||
|
@ -196,9 +88,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
|||
}
|
||||
|
||||
impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> {
|
||||
fn disp_ord(&self) -> usize {
|
||||
self.disp_ord
|
||||
}
|
||||
fn disp_ord(&self) -> usize { self.s.disp_ord }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -209,13 +99,13 @@ mod test {
|
|||
#[test]
|
||||
fn flagbuilder_display() {
|
||||
let mut f = FlagBuilder::new("flg");
|
||||
f.settings.set(ArgSettings::Multiple);
|
||||
f.long = Some("flag");
|
||||
f.b.settings.set(ArgSettings::Multiple);
|
||||
f.s.long = Some("flag");
|
||||
|
||||
assert_eq!(&*format!("{}", f), "--flag");
|
||||
|
||||
let mut f2 = FlagBuilder::new("flg");
|
||||
f2.short = Some('f');
|
||||
f2.s.short = Some('f');
|
||||
|
||||
assert_eq!(&*format!("{}", f2), "-f");
|
||||
}
|
||||
|
@ -223,8 +113,8 @@ mod test {
|
|||
#[test]
|
||||
fn flagbuilder_display_single_alias() {
|
||||
let mut f = FlagBuilder::new("flg");
|
||||
f.long = Some("flag");
|
||||
f.aliases = Some(vec![("als", true)]);
|
||||
f.s.long = Some("flag");
|
||||
f.s.aliases = Some(vec![("als", true)]);
|
||||
|
||||
assert_eq!(&*format!("{}", f), "--flag");
|
||||
}
|
||||
|
@ -232,13 +122,9 @@ mod test {
|
|||
#[test]
|
||||
fn flagbuilder_display_multiple_aliases() {
|
||||
let mut f = FlagBuilder::new("flg");
|
||||
f.short = Some('f');
|
||||
f.aliases = Some(vec![
|
||||
("alias_not_visible", false),
|
||||
("f2", true),
|
||||
("f3", true),
|
||||
("f4", true)
|
||||
]);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
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;
|
||||
|
|
|
@ -7,113 +7,33 @@ use std::result::Result as StdResult;
|
|||
use vec_map::VecMap;
|
||||
|
||||
// Internal
|
||||
use args::{AnyArg, Arg, DispOrder};
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
use args::{ArgSettings, ArgKind, AnyArg, Base, Switched, Valued, Arg, DispOrder};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[doc(hidden)]
|
||||
pub struct OptBuilder<'n, 'e> {
|
||||
pub name: &'n str,
|
||||
pub short: Option<char>,
|
||||
pub long: Option<&'e str>,
|
||||
pub aliases: Option<Vec<(&'e str, bool)>>,
|
||||
pub help: Option<&'e str>,
|
||||
pub blacklist: Option<Vec<&'e str>>,
|
||||
pub possible_vals: Option<Vec<&'e str>>,
|
||||
pub requires: Option<Vec<&'e str>>,
|
||||
pub num_vals: Option<u64>,
|
||||
pub min_vals: Option<u64>,
|
||||
pub max_vals: Option<u64>,
|
||||
pub val_names: Option<VecMap<&'e str>>,
|
||||
pub validator: Option<Rc<Fn(String) -> StdResult<(), String>>>,
|
||||
pub overrides: Option<Vec<&'e str>>,
|
||||
pub settings: ArgFlags,
|
||||
pub val_delim: Option<char>,
|
||||
pub default_val: Option<&'n str>,
|
||||
pub disp_ord: usize,
|
||||
pub unified_ord: usize,
|
||||
pub r_unless: Option<Vec<&'e str>>,
|
||||
}
|
||||
|
||||
impl<'n, 'e> Default for OptBuilder<'n, 'e> {
|
||||
fn default() -> Self {
|
||||
OptBuilder {
|
||||
name: "",
|
||||
short: None,
|
||||
long: None,
|
||||
aliases: None,
|
||||
help: None,
|
||||
blacklist: None,
|
||||
possible_vals: None,
|
||||
requires: None,
|
||||
num_vals: None,
|
||||
min_vals: None,
|
||||
max_vals: None,
|
||||
val_names: None,
|
||||
validator: None,
|
||||
overrides: None,
|
||||
settings: ArgFlags::new(),
|
||||
val_delim: Some(','),
|
||||
default_val: None,
|
||||
disp_ord: 999,
|
||||
unified_ord: 999,
|
||||
r_unless: None,
|
||||
}
|
||||
}
|
||||
#[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 { name: name, ..Default::default() }
|
||||
}
|
||||
pub fn new(name: &'n str) -> Self { OptBuilder { b: Base::new(name), ..Default::default() } }
|
||||
|
||||
pub fn from_arg(a: &Arg<'n, 'e>, reqs: &mut Vec<&'e str>) -> Self {
|
||||
assert!(a.short.is_some() || a.long.is_some(),
|
||||
format!("Argument \"{}\" has takes_value(true), yet neither a short() or long() \
|
||||
was supplied",
|
||||
a.name));
|
||||
|
||||
// No need to check for .index() as that is handled above
|
||||
let mut ob = OptBuilder {
|
||||
name: a.name,
|
||||
short: a.short,
|
||||
long: a.long,
|
||||
aliases: a.aliases.clone(),
|
||||
help: a.help,
|
||||
num_vals: a.num_vals,
|
||||
min_vals: a.min_vals,
|
||||
max_vals: a.max_vals,
|
||||
val_names: a.val_names.clone(),
|
||||
val_delim: a.val_delim,
|
||||
blacklist: a.blacklist.clone(),
|
||||
overrides: a.overrides.clone(),
|
||||
requires: a.requires.clone(),
|
||||
possible_vals: a.possible_vals.clone(),
|
||||
settings: a.settings,
|
||||
default_val: a.default_val,
|
||||
disp_ord: a.disp_ord,
|
||||
r_unless: a.r_unless.clone(),
|
||||
..Default::default()
|
||||
let ob = OptBuilder {
|
||||
b: Base::from(a),
|
||||
s: Switched::from(a),
|
||||
v: Valued::from(a),
|
||||
};
|
||||
if let Some(ref vec) = ob.val_names {
|
||||
if vec.len() > 1 {
|
||||
ob.num_vals = Some(vec.len() as u64);
|
||||
}
|
||||
}
|
||||
if let Some(ref vec) = ob.val_names {
|
||||
if vec.len() > 1 {
|
||||
ob.num_vals = Some(vec.len() as u64);
|
||||
}
|
||||
}
|
||||
if let Some(ref p) = a.validator {
|
||||
ob.validator = Some(p.clone());
|
||||
}
|
||||
// If the arg is required, add all it's requirements to master required list
|
||||
if a.is_set(ArgSettings::Required) {
|
||||
if let Some(ref areqs) = a.requires {
|
||||
for r in areqs {
|
||||
reqs.push(*r);
|
||||
}
|
||||
reqs.extend_from_slice(areqs);
|
||||
}
|
||||
}
|
||||
ob
|
||||
|
@ -124,14 +44,14 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
debugln!("fn=fmt");
|
||||
// Write the name such --long or -l
|
||||
if let Some(l) = self.long {
|
||||
if let Some(l) = self.s.long {
|
||||
try!(write!(f, "--{} ", l));
|
||||
} else {
|
||||
try!(write!(f, "-{} ", self.short.unwrap()));
|
||||
try!(write!(f, "-{} ", self.s.short.unwrap()));
|
||||
}
|
||||
|
||||
// Write the values such as <name1> <name2>
|
||||
if let Some(ref vec) = self.val_names {
|
||||
if let Some(ref vec) = self.v.val_names {
|
||||
let mut it = vec.iter().peekable();
|
||||
while let Some((_, val)) = it.next() {
|
||||
try!(write!(f, "<{}>", val));
|
||||
|
@ -143,10 +63,10 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
if self.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
try!(write!(f, "..."));
|
||||
}
|
||||
} else if let Some(num) = self.num_vals {
|
||||
} else if let Some(num) = self.v.num_vals {
|
||||
let mut it = (0..num).peekable();
|
||||
while let Some(_) = it.next() {
|
||||
try!(write!(f, "<{}>", self.name));
|
||||
try!(write!(f, "<{}>", self.b.name));
|
||||
if it.peek().is_some() {
|
||||
try!(write!(f, " "));
|
||||
}
|
||||
|
@ -154,7 +74,7 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
} else {
|
||||
try!(write!(f,
|
||||
"<{}>{}",
|
||||
self.name,
|
||||
self.b.name,
|
||||
if self.is_set(ArgSettings::Multiple) {
|
||||
"..."
|
||||
} else {
|
||||
|
@ -166,103 +86,37 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> Clone for OptBuilder<'n, 'e> {
|
||||
fn clone(&self) -> Self {
|
||||
OptBuilder {
|
||||
name: self.name,
|
||||
short: self.short,
|
||||
long: self.long,
|
||||
aliases: self.aliases.clone(),
|
||||
help: self.help,
|
||||
blacklist: self.blacklist.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
requires: self.requires.clone(),
|
||||
settings: self.settings,
|
||||
disp_ord: self.disp_ord,
|
||||
unified_ord: self.unified_ord,
|
||||
num_vals: self.num_vals,
|
||||
min_vals: self.min_vals,
|
||||
max_vals: self.max_vals,
|
||||
val_names: self.val_names.clone(),
|
||||
val_delim: self.val_delim,
|
||||
possible_vals: self.possible_vals.clone(),
|
||||
default_val: self.default_val,
|
||||
validator: self.validator.clone(),
|
||||
r_unless: self.r_unless.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
||||
fn name(&self) -> &'n str {
|
||||
self.name
|
||||
}
|
||||
fn overrides(&self) -> Option<&[&'e str]> {
|
||||
self.overrides.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn requires(&self) -> Option<&[&'e str]> {
|
||||
self.requires.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn blacklist(&self) -> Option<&[&'e str]> {
|
||||
self.blacklist.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
||||
self.r_unless.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> {
|
||||
self.val_names.as_ref()
|
||||
}
|
||||
fn is_set(&self, s: ArgSettings) -> bool {
|
||||
self.settings.is_set(s)
|
||||
}
|
||||
fn has_switch(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn set(&mut self, s: ArgSettings) {
|
||||
self.settings.set(s)
|
||||
}
|
||||
fn max_vals(&self) -> Option<u64> {
|
||||
self.max_vals
|
||||
}
|
||||
fn num_vals(&self) -> Option<u64> {
|
||||
self.num_vals
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> {
|
||||
self.possible_vals.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn name(&self) -> &'n str { self.b.name }
|
||||
fn id(&self) -> usize { self.b.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Opt }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e 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<u64> { self.v.max_vals }
|
||||
fn num_vals(&self) -> Option<u64> { 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<Fn(String) -> StdResult<(), String>>> {
|
||||
self.validator.as_ref()
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> {
|
||||
self.min_vals
|
||||
}
|
||||
fn short(&self) -> Option<char> {
|
||||
self.short
|
||||
}
|
||||
fn long(&self) -> Option<&'e str> {
|
||||
self.long
|
||||
}
|
||||
fn val_delim(&self) -> Option<char> {
|
||||
self.val_delim
|
||||
}
|
||||
fn takes_value(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn help(&self) -> Option<&'e str> {
|
||||
self.help
|
||||
}
|
||||
fn default_val(&self) -> Option<&'n str> {
|
||||
self.default_val
|
||||
}
|
||||
fn longest_filter(&self) -> bool {
|
||||
true
|
||||
self.v.validator.as_ref()
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> { self.v.min_vals }
|
||||
fn short(&self) -> Option<char> { self.s.short }
|
||||
fn long(&self) -> Option<&'e str> { self.s.long }
|
||||
fn val_delim(&self) -> Option<char> { self.v.val_delim }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.b.help }
|
||||
fn default_val(&self) -> Option<&'n str> { self.v.default_val }
|
||||
fn longest_filter(&self) -> bool { true }
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||
if let Some(ref aliases) = self.aliases {
|
||||
let vis_aliases: Vec<_> =
|
||||
aliases.iter()
|
||||
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
||||
.collect();
|
||||
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 {
|
||||
|
@ -275,9 +129,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
|||
}
|
||||
|
||||
impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> {
|
||||
fn disp_ord(&self) -> usize {
|
||||
self.disp_ord
|
||||
}
|
||||
fn disp_ord(&self) -> usize { self.s.disp_ord }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -289,8 +141,8 @@ mod test {
|
|||
#[test]
|
||||
fn optbuilder_display1() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.settings.set(ArgSettings::Multiple);
|
||||
o.s.long = Some("option");
|
||||
o.b.settings.set(ArgSettings::Multiple);
|
||||
|
||||
assert_eq!(&*format!("{}", o), "--option <opt>...");
|
||||
}
|
||||
|
@ -302,8 +154,8 @@ mod test {
|
|||
v_names.insert(1, "name");
|
||||
|
||||
let mut o2 = OptBuilder::new("opt");
|
||||
o2.short = Some('o');
|
||||
o2.val_names = Some(v_names);
|
||||
o2.s.short = Some('o');
|
||||
o2.v.val_names = Some(v_names);
|
||||
|
||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||
}
|
||||
|
@ -315,9 +167,9 @@ mod test {
|
|||
v_names.insert(1, "name");
|
||||
|
||||
let mut o2 = OptBuilder::new("opt");
|
||||
o2.short = Some('o');
|
||||
o2.val_names = Some(v_names);
|
||||
o2.settings.set(ArgSettings::Multiple);
|
||||
o2.s.short = Some('o');
|
||||
o2.v.val_names = Some(v_names);
|
||||
o2.b.settings.set(ArgSettings::Multiple);
|
||||
|
||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||
}
|
||||
|
@ -325,8 +177,8 @@ mod test {
|
|||
#[test]
|
||||
fn optbuilder_display_single_alias() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.aliases = Some(vec![("als", true)]);
|
||||
o.s.long = Some("option");
|
||||
o.s.aliases = Some(vec![("als", true)]);
|
||||
|
||||
assert_eq!(&*format!("{}", o), "--option <opt>");
|
||||
}
|
||||
|
@ -334,13 +186,9 @@ mod test {
|
|||
#[test]
|
||||
fn optbuilder_display_multiple_aliases() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.aliases = Some(vec![
|
||||
("als_not_visible", false),
|
||||
("als2", true),
|
||||
("als3", true),
|
||||
("als4", true)
|
||||
]);
|
||||
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 <opt>");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,112 +9,52 @@ use vec_map::VecMap;
|
|||
|
||||
// Internal
|
||||
use Arg;
|
||||
use args::{AnyArg, DispOrder};
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
use args::{ArgSettings, Base, Valued, ArgKind, AnyArg, DispOrder};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[doc(hidden)]
|
||||
pub struct PosBuilder<'n, 'e> {
|
||||
pub name: &'n str,
|
||||
pub help: Option<&'e str>,
|
||||
pub requires: Option<Vec<&'e str>>,
|
||||
pub blacklist: Option<Vec<&'e str>>,
|
||||
pub possible_vals: Option<Vec<&'e str>>,
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PosBuilder<'n, 'e>
|
||||
where 'n: 'e
|
||||
{
|
||||
pub b: Base<'n, 'e>,
|
||||
pub v: Valued<'n, 'e>,
|
||||
pub index: u64,
|
||||
pub num_vals: Option<u64>,
|
||||
pub max_vals: Option<u64>,
|
||||
pub min_vals: Option<u64>,
|
||||
pub val_names: Option<VecMap<&'e str>>,
|
||||
pub validator: Option<Rc<Fn(String) -> StdResult<(), String>>>,
|
||||
pub overrides: Option<Vec<&'e str>>,
|
||||
pub settings: ArgFlags,
|
||||
pub val_delim: Option<char>,
|
||||
pub default_val: Option<&'n str>,
|
||||
pub disp_ord: usize,
|
||||
pub r_unless: Option<Vec<&'e str>>,
|
||||
}
|
||||
|
||||
impl<'n, 'e> Default for PosBuilder<'n, 'e> {
|
||||
fn default() -> Self {
|
||||
PosBuilder {
|
||||
name: "",
|
||||
help: None,
|
||||
requires: None,
|
||||
blacklist: None,
|
||||
possible_vals: None,
|
||||
index: 0,
|
||||
num_vals: None,
|
||||
min_vals: None,
|
||||
max_vals: None,
|
||||
val_names: None,
|
||||
validator: None,
|
||||
overrides: None,
|
||||
settings: ArgFlags::new(),
|
||||
val_delim: Some(','),
|
||||
default_val: None,
|
||||
disp_ord: 999,
|
||||
r_unless: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> PosBuilder<'n, 'e> {
|
||||
pub fn new(name: &'n str, idx: u64) -> Self {
|
||||
PosBuilder {
|
||||
name: name,
|
||||
b: Base::new(name),
|
||||
index: idx,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_arg(a: &Arg<'n, 'e>, idx: u64, reqs: &mut Vec<&'e str>) -> Self {
|
||||
debug_assert!(a.short.is_none() || a.long.is_none(),
|
||||
format!("Argument \"{}\" has conflicting requirements, both index() and \
|
||||
short(), or long(), were supplied",
|
||||
a.name));
|
||||
|
||||
// Create the Positional Argument Builder with each HashSet = None to only
|
||||
// allocate
|
||||
// those that require it
|
||||
let mut pb = PosBuilder {
|
||||
name: a.name,
|
||||
b: Base::from(a),
|
||||
v: Valued::from(a),
|
||||
index: idx,
|
||||
num_vals: a.num_vals,
|
||||
min_vals: a.min_vals,
|
||||
max_vals: a.max_vals,
|
||||
val_names: a.val_names.clone(),
|
||||
blacklist: a.blacklist.clone(),
|
||||
overrides: a.overrides.clone(),
|
||||
requires: a.requires.clone(),
|
||||
possible_vals: a.possible_vals.clone(),
|
||||
help: a.help,
|
||||
val_delim: a.val_delim,
|
||||
settings: a.settings,
|
||||
default_val: a.default_val,
|
||||
disp_ord: a.disp_ord,
|
||||
r_unless: a.r_unless.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
if a.max_vals.is_some() || a.min_vals.is_some() ||
|
||||
(a.num_vals.is_some() && a.num_vals.unwrap() > 1) {
|
||||
pb.settings.set(ArgSettings::Multiple);
|
||||
}
|
||||
if let Some(ref p) = a.validator {
|
||||
pb.validator = Some(p.clone());
|
||||
pb.b.settings.set(ArgSettings::Multiple);
|
||||
}
|
||||
// If the arg is required, add all it's requirements to master required list
|
||||
if a.is_set(ArgSettings::Required) {
|
||||
if let Some(ref areqs) = a.requires {
|
||||
for r in areqs {
|
||||
reqs.push(*r);
|
||||
}
|
||||
reqs.extend_from_slice(areqs);
|
||||
}
|
||||
}
|
||||
pb
|
||||
}
|
||||
|
||||
pub fn multiple_str(&self) -> &str {
|
||||
if self.settings.is_set(ArgSettings::Multiple) && self.val_names.is_none() {
|
||||
if self.b.settings.is_set(ArgSettings::Multiple) && self.v.val_names.is_none() {
|
||||
"..."
|
||||
} else {
|
||||
""
|
||||
|
@ -122,20 +62,20 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
|
|||
}
|
||||
|
||||
pub fn name_no_brackets(&self) -> Cow<str> {
|
||||
if let Some(ref names) = self.val_names {
|
||||
if let Some(ref names) = self.v.val_names {
|
||||
Cow::Owned(names.values()
|
||||
.map(|n| format!("<{}>", n))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" "))
|
||||
} else {
|
||||
Cow::Borrowed(self.name)
|
||||
Cow::Borrowed(self.b.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
if let Some(ref names) = self.val_names {
|
||||
if let Some(ref names) = self.v.val_names {
|
||||
try!(write!(f,
|
||||
"{}",
|
||||
names.values()
|
||||
|
@ -143,9 +83,9 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
|||
.collect::<Vec<_>>()
|
||||
.join(" ")));
|
||||
} else {
|
||||
try!(write!(f, "<{}>", self.name));
|
||||
try!(write!(f, "<{}>", self.b.name));
|
||||
}
|
||||
if self.settings.is_set(ArgSettings::Multiple) && self.val_names.is_none() {
|
||||
if self.b.settings.is_set(ArgSettings::Multiple) && self.v.val_names.is_none() {
|
||||
try!(write!(f, "..."));
|
||||
}
|
||||
|
||||
|
@ -153,103 +93,37 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> Clone for PosBuilder<'n, 'e> {
|
||||
fn clone(&self) -> Self {
|
||||
PosBuilder {
|
||||
name: self.name,
|
||||
help: self.help,
|
||||
blacklist: self.blacklist.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
requires: self.requires.clone(),
|
||||
settings: self.settings,
|
||||
disp_ord: self.disp_ord,
|
||||
num_vals: self.num_vals,
|
||||
min_vals: self.min_vals,
|
||||
max_vals: self.max_vals,
|
||||
val_names: self.val_names.clone(),
|
||||
val_delim: self.val_delim,
|
||||
possible_vals: self.possible_vals.clone(),
|
||||
default_val: self.default_val,
|
||||
validator: self.validator.clone(),
|
||||
r_unless: self.r_unless.clone(),
|
||||
index: self.index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
|
||||
fn name(&self) -> &'n str {
|
||||
self.name
|
||||
}
|
||||
fn overrides(&self) -> Option<&[&'e str]> {
|
||||
self.overrides.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn requires(&self) -> Option<&[&'e str]> {
|
||||
self.requires.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn blacklist(&self) -> Option<&[&'e str]> {
|
||||
self.blacklist.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
||||
self.r_unless.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn val_names(&self) -> Option<&VecMap<&'e str>> {
|
||||
self.val_names.as_ref()
|
||||
}
|
||||
fn is_set(&self, s: ArgSettings) -> bool {
|
||||
self.settings.is_set(s)
|
||||
}
|
||||
fn set(&mut self, s: ArgSettings) {
|
||||
self.settings.set(s)
|
||||
}
|
||||
fn has_switch(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn max_vals(&self) -> Option<u64> {
|
||||
self.max_vals
|
||||
}
|
||||
fn num_vals(&self) -> Option<u64> {
|
||||
self.num_vals
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'e str]> {
|
||||
self.possible_vals.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn name(&self) -> &'n str { self.b.name }
|
||||
fn id(&self) -> usize { self.b.id }
|
||||
fn kind(&self) -> ArgKind { ArgKind::Pos }
|
||||
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||
fn requires(&self) -> Option<&[&'e 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<u64> { self.v.max_vals }
|
||||
fn num_vals(&self) -> Option<u64> { 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<Fn(String) -> StdResult<(), String>>> {
|
||||
self.validator.as_ref()
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> {
|
||||
self.min_vals
|
||||
}
|
||||
fn short(&self) -> Option<char> {
|
||||
None
|
||||
}
|
||||
fn long(&self) -> Option<&'e str> {
|
||||
None
|
||||
}
|
||||
fn val_delim(&self) -> Option<char> {
|
||||
self.val_delim
|
||||
}
|
||||
fn takes_value(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn help(&self) -> Option<&'e str> {
|
||||
self.help
|
||||
}
|
||||
fn default_val(&self) -> Option<&'n str> {
|
||||
self.default_val
|
||||
}
|
||||
fn longest_filter(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||
None
|
||||
self.v.validator.as_ref()
|
||||
}
|
||||
fn min_vals(&self) -> Option<u64> { self.v.min_vals }
|
||||
fn short(&self) -> Option<char> { None }
|
||||
fn long(&self) -> Option<&'e str> { None }
|
||||
fn val_delim(&self) -> Option<char> { self.v.val_delim }
|
||||
fn takes_value(&self) -> bool { true }
|
||||
fn help(&self) -> Option<&'e str> { self.b.help }
|
||||
fn default_val(&self) -> Option<&'n str> { self.v.default_val }
|
||||
fn longest_filter(&self) -> bool { true }
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> { None }
|
||||
}
|
||||
|
||||
impl<'n, 'e> DispOrder for PosBuilder<'n, 'e> {
|
||||
fn disp_ord(&self) -> usize {
|
||||
self.disp_ord
|
||||
}
|
||||
fn disp_ord(&self) -> usize { self.index as usize }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -261,7 +135,7 @@ mod test {
|
|||
#[test]
|
||||
fn display_mult() {
|
||||
let mut p = PosBuilder::new("pos", 1);
|
||||
p.settings.set(ArgSettings::Multiple);
|
||||
p.b.settings.set(ArgSettings::Multiple);
|
||||
|
||||
assert_eq!(&*format!("{}", p), "<pos>...");
|
||||
}
|
||||
|
@ -269,7 +143,7 @@ mod test {
|
|||
#[test]
|
||||
fn display_required() {
|
||||
let mut p2 = PosBuilder::new("pos", 1);
|
||||
p2.settings.set(ArgSettings::Required);
|
||||
p2.b.settings.set(ArgSettings::Required);
|
||||
|
||||
assert_eq!(&*format!("{}", p2), "<pos>");
|
||||
}
|
||||
|
@ -280,7 +154,7 @@ mod test {
|
|||
let mut vm = VecMap::new();
|
||||
vm.insert(0, "file1");
|
||||
vm.insert(1, "file2");
|
||||
p2.val_names = Some(vm);
|
||||
p2.v.val_names = Some(vm);
|
||||
|
||||
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
||||
}
|
||||
|
@ -288,11 +162,11 @@ mod test {
|
|||
#[test]
|
||||
fn display_val_names_req() {
|
||||
let mut p2 = PosBuilder::new("pos", 1);
|
||||
p2.settings.set(ArgSettings::Required);
|
||||
p2.b.settings.set(ArgSettings::Required);
|
||||
let mut vm = VecMap::new();
|
||||
vm.insert(0, "file1");
|
||||
vm.insert(1, "file2");
|
||||
p2.val_names = Some(vm);
|
||||
p2.v.val_names = Some(vm);
|
||||
|
||||
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
||||
}
|
||||
|
|
46
src/args/arg_builder/switched.rs
Normal file
46
src/args/arg_builder/switched.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use Arg;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Switched<'b> {
|
||||
pub short: Option<char>,
|
||||
pub long: Option<&'b str>,
|
||||
pub aliases: Option<Vec<(&'b str, bool)>>, // (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 {
|
||||
Switched {
|
||||
short: a.short,
|
||||
long: a.long,
|
||||
aliases: a.aliases.clone(),
|
||||
disp_ord: a.disp_ord,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
61
src/args/arg_builder/valued.rs
Normal file
61
src/args/arg_builder/valued.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use vec_map::VecMap;
|
||||
|
||||
use Arg;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[derive(Clone)]
|
||||
pub struct Valued<'a, 'b>
|
||||
where 'a: 'b
|
||||
{
|
||||
pub possible_vals: Option<Vec<&'b str>>,
|
||||
pub val_names: Option<VecMap<&'b str>>,
|
||||
pub num_vals: Option<u64>,
|
||||
pub max_vals: Option<u64>,
|
||||
pub min_vals: Option<u64>,
|
||||
pub validator: Option<Rc<Fn(String) -> Result<(), String>>>,
|
||||
pub val_delim: Option<char>,
|
||||
pub default_val: Option<&'a 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,
|
||||
val_delim: Some(','),
|
||||
default_val: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Valued<'n, 'e> {
|
||||
fn from(a: &'z Arg<'n, 'e>) -> Self {
|
||||
let mut v = Valued {
|
||||
possible_vals: a.possible_vals.clone(),
|
||||
num_vals: a.num_vals,
|
||||
min_vals: a.min_vals,
|
||||
max_vals: a.max_vals,
|
||||
val_names: a.val_names.clone(),
|
||||
validator: a.validator.clone(),
|
||||
val_delim: a.val_delim,
|
||||
default_val: a.default_val,
|
||||
};
|
||||
if let Some(ref vec) = a.val_names {
|
||||
if vec.len() > 1 {
|
||||
v.num_vals = Some(vec.len() as u64);
|
||||
}
|
||||
}
|
||||
if let Some(ref vec) = a.val_names {
|
||||
if vec.len() > 1 {
|
||||
v.num_vals = Some(vec.len() as u64);
|
||||
}
|
||||
}
|
||||
v
|
||||
}
|
||||
}
|
|
@ -16,27 +16,17 @@ use args::settings::ArgSettings;
|
|||
pub struct ArgMatcher<'a>(pub ArgMatches<'a>);
|
||||
|
||||
impl<'a> Default for ArgMatcher<'a> {
|
||||
fn default() -> Self {
|
||||
ArgMatcher(ArgMatches::default())
|
||||
}
|
||||
fn default() -> Self { ArgMatcher(ArgMatches::default()) }
|
||||
}
|
||||
|
||||
impl<'a> ArgMatcher<'a> {
|
||||
pub fn new() -> Self {
|
||||
ArgMatcher::default()
|
||||
}
|
||||
pub fn new() -> Self { ArgMatcher::default() }
|
||||
|
||||
pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> {
|
||||
self.0.args.get_mut(arg)
|
||||
}
|
||||
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 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(&mut self, arg: &str) { self.0.args.remove(arg); }
|
||||
|
||||
pub fn remove_all(&mut self, args: &[&str]) {
|
||||
for &arg in args {
|
||||
|
@ -44,43 +34,26 @@ impl<'a> ArgMatcher<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, name: &'a str) {
|
||||
self.0.args.insert(name, MatchedArg::new());
|
||||
}
|
||||
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 contains(&self, arg: &str) -> bool { self.0.args.contains_key(arg) }
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.args.is_empty()
|
||||
}
|
||||
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 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 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 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(&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 subcommand_name(&self) -> Option<&str> { self.0.subcommand_name() }
|
||||
|
||||
pub fn iter(&self) -> Iter<&str, MatchedArg> {
|
||||
self.0.args.iter()
|
||||
}
|
||||
pub fn iter(&self) -> Iter<&str, MatchedArg> { self.0.args.iter() }
|
||||
|
||||
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
|
||||
debugln!("fn=inc_occurrence_of;");
|
||||
if let Some(a) = self.get_mut(arg) {
|
||||
debugln!("+1 to {}'s occurrences", arg);
|
||||
a.occurs += 1;
|
||||
|
@ -90,6 +63,7 @@ impl<'a> ArgMatcher<'a> {
|
|||
}
|
||||
|
||||
pub fn inc_occurrences_of(&mut self, args: &[&'a str]) {
|
||||
debugln!("fn=inc_occurrences_of;");
|
||||
for arg in args {
|
||||
self.inc_occurrence_of(arg);
|
||||
}
|
||||
|
@ -107,16 +81,20 @@ impl<'a> ArgMatcher<'a> {
|
|||
pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool
|
||||
where A: AnyArg<'a, 'b>
|
||||
{
|
||||
debugln!("fn=needs_more_vals;");
|
||||
if let Some(ma) = self.get(o.name()) {
|
||||
if let Some(num) = o.num_vals() {
|
||||
debugln!("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!("max_vals...{}", num);
|
||||
return !((ma.vals.len() as u64) > num);
|
||||
} else if o.min_vals().is_some() {
|
||||
debugln!("min_vals...true");
|
||||
return true;
|
||||
}
|
||||
return o.is_set(ArgSettings::Multiple);
|
||||
|
@ -126,7 +104,5 @@ impl<'a> ArgMatcher<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Into<ArgMatches<'a>> for ArgMatcher<'a> {
|
||||
fn into(self) -> ArgMatches<'a> {
|
||||
self.0
|
||||
}
|
||||
fn into(self) -> ArgMatches<'a> { self.0 }
|
||||
}
|
||||
|
|
|
@ -82,9 +82,7 @@ impl<'a> Default for ArgMatches<'a> {
|
|||
|
||||
impl<'a> ArgMatches<'a> {
|
||||
#[doc(hidden)]
|
||||
pub fn new() -> Self {
|
||||
ArgMatches { ..Default::default() }
|
||||
}
|
||||
pub fn new() -> Self { ArgMatches { ..Default::default() } }
|
||||
|
||||
/// Gets the value of a specific [option] or [positional] argument (i.e. an argument that takes
|
||||
/// an additional value at runtime). If the option wasn't present at runtime
|
||||
|
@ -214,9 +212,7 @@ impl<'a> ArgMatches<'a> {
|
|||
/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
|
||||
pub fn values_of<S: AsRef<str>>(&'a self, name: S) -> Option<Values<'a>> {
|
||||
if let Some(arg) = self.args.get(name.as_ref()) {
|
||||
fn to_str_slice(o: &OsString) -> &str {
|
||||
o.to_str().expect(INVALID_UTF8)
|
||||
}
|
||||
fn to_str_slice(o: &OsString) -> &str { o.to_str().expect(INVALID_UTF8) }
|
||||
let to_str_slice: fn(&OsString) -> &str = to_str_slice; // coerce to fn pointer
|
||||
return Some(Values { iter: arg.vals.values().map(to_str_slice) });
|
||||
}
|
||||
|
@ -289,9 +285,7 @@ impl<'a> ArgMatches<'a> {
|
|||
/// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html
|
||||
/// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
|
||||
pub fn values_of_os<S: AsRef<str>>(&'a self, name: S) -> Option<OsValues<'a>> {
|
||||
fn to_str_slice(o: &OsString) -> &OsStr {
|
||||
&*o
|
||||
}
|
||||
fn to_str_slice(o: &OsString) -> &OsStr { &*o }
|
||||
let to_str_slice: fn(&'a OsString) -> &'a OsStr = to_str_slice; // coerce to fn pointer
|
||||
if let Some(arg) = self.args.get(name.as_ref()) {
|
||||
return Some(OsValues { iter: arg.vals.values().map(to_str_slice) });
|
||||
|
@ -533,9 +527,7 @@ impl<'a> ArgMatches<'a> {
|
|||
/// ```
|
||||
/// [`Subcommand`]: ./struct.SubCommand.html
|
||||
/// [`App`]: ./struct.App.html
|
||||
pub fn usage(&self) -> &str {
|
||||
self.usage.as_ref().map_or("", |u| &u[..])
|
||||
}
|
||||
pub fn usage(&self) -> &str { self.usage.as_ref().map_or("", |u| &u[..]) }
|
||||
}
|
||||
|
||||
|
||||
|
@ -568,18 +560,12 @@ pub struct Values<'a> {
|
|||
impl<'a> Iterator for Values<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<&'a str> {
|
||||
self.iter.next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
fn next(&mut self) -> Option<&'a str> { self.iter.next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Values<'a> {
|
||||
fn next_back(&mut self) -> Option<&'a str> {
|
||||
self.iter.next_back()
|
||||
}
|
||||
fn next_back(&mut self) -> Option<&'a str> { self.iter.next_back() }
|
||||
}
|
||||
|
||||
/// An iterator over the key-value pairs of a map.
|
||||
|
@ -608,9 +594,7 @@ impl<'a, V> Iterator for Iter<'a, V> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, Some(self.back - self.front))
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(self.back - self.front)) }
|
||||
}
|
||||
|
||||
impl<'a, V> DoubleEndedIterator for Iter<'a, V> {
|
||||
|
@ -659,16 +643,10 @@ pub struct OsValues<'a> {
|
|||
impl<'a> Iterator for OsValues<'a> {
|
||||
type Item = &'a OsStr;
|
||||
|
||||
fn next(&mut self) -> Option<&'a OsStr> {
|
||||
self.iter.next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
fn next(&mut self) -> Option<&'a OsStr> { self.iter.next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for OsValues<'a> {
|
||||
fn next_back(&mut self) -> Option<&'a OsStr> {
|
||||
self.iter.next_back()
|
||||
}
|
||||
fn next_back(&mut self) -> Option<&'a OsStr> { self.iter.next_back() }
|
||||
}
|
||||
|
|
|
@ -128,9 +128,7 @@ 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) -> ArgGroup<'a> { ArgGroup::from(y.as_hash().unwrap()) }
|
||||
|
||||
/// Adds an [argument] to this group by name
|
||||
///
|
||||
|
|
|
@ -23,7 +23,5 @@ impl Default for MatchedArg {
|
|||
}
|
||||
|
||||
impl MatchedArg {
|
||||
pub fn new() -> Self {
|
||||
MatchedArg::default()
|
||||
}
|
||||
pub fn new() -> Self { MatchedArg::default() }
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
pub use self::any_arg::{AnyArg, DispOrder};
|
||||
pub use self::arg::Arg;
|
||||
pub use self::arg_builder::{FlagBuilder, OptBuilder, PosBuilder};
|
||||
pub use self::arg_builder::{Base, Switched, Valued, FlagBuilder, OptBuilder, PosBuilder};
|
||||
pub use self::arg_matcher::ArgMatcher;
|
||||
pub use self::arg_matches::{Values, OsValues, ArgMatches};
|
||||
pub use self::group::ArgGroup;
|
||||
pub use self::matched_arg::MatchedArg;
|
||||
pub use self::settings::ArgSettings;
|
||||
pub use self::settings::{ArgFlags, ArgSettings};
|
||||
pub use self::subcommand::SubCommand;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -19,3 +19,13 @@ mod arg_builder;
|
|||
mod matched_arg;
|
||||
mod group;
|
||||
pub mod settings;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ArgKind {
|
||||
Flag,
|
||||
Opt,
|
||||
Pos,
|
||||
Subcmd,
|
||||
Group,
|
||||
}
|
||||
|
|
|
@ -4,18 +4,19 @@ use std::str::FromStr;
|
|||
|
||||
bitflags! {
|
||||
flags Flags: u16 {
|
||||
const REQUIRED = 0b000000000001,
|
||||
const MULTIPLE = 0b000000000010,
|
||||
const EMPTY_VALS = 0b000000000100,
|
||||
const GLOBAL = 0b000000001000,
|
||||
const HIDDEN = 0b000000010000,
|
||||
const TAKES_VAL = 0b000000100000,
|
||||
const USE_DELIM = 0b000001000000,
|
||||
const NEXT_LINE_HELP = 0b000010000000,
|
||||
const R_UNLESS_ALL = 0b000100000000,
|
||||
const REQ_DELIM = 0b001000000000,
|
||||
const DELIM_NOT_SET = 0b010000000000,
|
||||
const HIDE_POS_VALS = 0b100000000000,
|
||||
const REQUIRED = 0b0000000000001,
|
||||
const MULTIPLE = 0b0000000000010,
|
||||
const EMPTY_VALS = 0b0000000000100,
|
||||
const GLOBAL = 0b0000000001000,
|
||||
const HIDDEN = 0b0000000010000,
|
||||
const TAKES_VAL = 0b0000000100000,
|
||||
const USE_DELIM = 0b0000001000000,
|
||||
const NEXT_LINE_HELP = 0b0000010000000,
|
||||
const R_UNLESS_ALL = 0b0000100000000,
|
||||
const REQ_DELIM = 0b0001000000000,
|
||||
const DELIM_NOT_SET = 0b0010000000000,
|
||||
const HIDE_POS_VALS = 0b0100000000000,
|
||||
const ALLOW_TAC_VALS = 0b1000000000000,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,9 +25,7 @@ bitflags! {
|
|||
pub struct ArgFlags(Flags);
|
||||
|
||||
impl ArgFlags {
|
||||
pub fn new() -> Self {
|
||||
ArgFlags::default()
|
||||
}
|
||||
pub fn new() -> Self { ArgFlags::default() }
|
||||
|
||||
impl_settings!{ArgSettings,
|
||||
Required => REQUIRED,
|
||||
|
@ -40,14 +39,13 @@ impl ArgFlags {
|
|||
RequiredUnlessAll => R_UNLESS_ALL,
|
||||
RequireDelimiter => REQ_DELIM,
|
||||
ValueDelimiterNotSet => DELIM_NOT_SET,
|
||||
HidePossibleValues => HIDE_POS_VALS
|
||||
HidePossibleValues => HIDE_POS_VALS,
|
||||
AllowLeadingHyphen => ALLOW_TAC_VALS
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ArgFlags {
|
||||
fn default() -> Self {
|
||||
ArgFlags(EMPTY_VALS | DELIM_NOT_SET)
|
||||
}
|
||||
fn default() -> Self { ArgFlags(EMPTY_VALS | DELIM_NOT_SET) }
|
||||
}
|
||||
|
||||
/// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
|
||||
|
@ -78,6 +76,8 @@ pub enum ArgSettings {
|
|||
RequireDelimiter,
|
||||
/// Hides the possible values from the help string
|
||||
HidePossibleValues,
|
||||
/// Allows vals that start with a '-'
|
||||
AllowLeadingHyphen,
|
||||
#[doc(hidden)]
|
||||
RequiredUnlessAll,
|
||||
#[doc(hidden)]
|
||||
|
@ -100,7 +100,44 @@ impl FromStr for ArgSettings {
|
|||
"requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
|
||||
"valuedelimiternotset" => Ok(ArgSettings::ValueDelimiterNotSet),
|
||||
"hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues),
|
||||
"allowleadinghyphen" => Ok(ArgSettings::AllowLeadingHyphen),
|
||||
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ArgSettings;
|
||||
|
||||
#[test]
|
||||
fn arg_settings_fromstr() {
|
||||
assert_eq!("allowleadinghyphen".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::AllowLeadingHyphen);
|
||||
assert_eq!("emptyvalues".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::EmptyValues);
|
||||
assert_eq!("global".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::Global);
|
||||
assert_eq!("hidepossiblevalues".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::HidePossibleValues);
|
||||
assert_eq!("hidden".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::Hidden);
|
||||
assert_eq!("multiple".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::Multiple);
|
||||
assert_eq!("nextlinehelp".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::NextLineHelp);
|
||||
assert_eq!("requiredunlessall".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::RequiredUnlessAll);
|
||||
assert_eq!("requiredelimiter".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::RequireDelimiter);
|
||||
assert_eq!("required".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::Required);
|
||||
assert_eq!("takesvalue".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::TakesValue);
|
||||
assert_eq!("usevaluedelimiter".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::UseValueDelimiter);
|
||||
assert_eq!("valuedelimiternotset".parse::<ArgSettings>().unwrap(),
|
||||
ArgSettings::ValueDelimiterNotSet);
|
||||
assert!("hahahaha".parse::<ArgSettings>().is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,7 @@ impl<'a> SubCommand<'a> {
|
|||
/// SubCommand::with_name("config"))
|
||||
/// # ;
|
||||
/// ```
|
||||
pub fn with_name<'b>(name: &str) -> App<'a, 'b> {
|
||||
App::new(name)
|
||||
}
|
||||
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
|
||||
///
|
||||
|
@ -66,7 +64,5 @@ impl<'a> SubCommand<'a> {
|
|||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "yaml")]
|
||||
pub fn from_yaml(yaml: &Yaml) -> App {
|
||||
App::from_yaml(yaml)
|
||||
}
|
||||
pub fn from_yaml(yaml: &Yaml) -> App { App::from_yaml(yaml) }
|
||||
}
|
||||
|
|
|
@ -13,13 +13,11 @@ pub struct BashGen<'a, 'b>
|
|||
}
|
||||
|
||||
impl<'a, 'b> BashGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||
BashGen { p: p }
|
||||
}
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self { BashGen { p: p } }
|
||||
|
||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||
|
||||
w!(buf,
|
||||
w!(buf,
|
||||
format!("_{name}() {{
|
||||
local i cur prev opts cmds
|
||||
COMPREPLY=()
|
||||
|
@ -141,8 +139,8 @@ complete -F _{name} {name}
|
|||
.p;
|
||||
}
|
||||
let mut opts = String::new();
|
||||
for o in &p.opts {
|
||||
if let Some(l) = o.long {
|
||||
for o in p.opts() {
|
||||
if let Some(l) = o.s.long {
|
||||
opts = format!("{}
|
||||
--{})
|
||||
COMPREPLY=({})
|
||||
|
@ -152,7 +150,7 @@ complete -F _{name} {name}
|
|||
l,
|
||||
self.vals_for(o));
|
||||
}
|
||||
if let Some(s) = o.short {
|
||||
if let Some(s) = o.s.short {
|
||||
opts = format!("{}
|
||||
-{})
|
||||
COMPREPLY=({})
|
||||
|
@ -177,9 +175,9 @@ complete -F _{name} {name}
|
|||
let mut it = vec.iter().peekable();
|
||||
while let Some((_, val)) = it.next() {
|
||||
ret = format!("{}<{}>{}",
|
||||
ret,
|
||||
val,
|
||||
if it.peek().is_some() { " " } else { "" });
|
||||
ret,
|
||||
val,
|
||||
if it.peek().is_some() { " " } else { "" });
|
||||
}
|
||||
let num = vec.len();
|
||||
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
|
@ -189,9 +187,9 @@ complete -F _{name} {name}
|
|||
let mut it = (0..num).peekable();
|
||||
while let Some(_) = it.next() {
|
||||
ret = format!("{}<{}>{}",
|
||||
ret,
|
||||
o.name(),
|
||||
if it.peek().is_some() { " " } else { "" });
|
||||
ret,
|
||||
o.name(),
|
||||
if it.peek().is_some() { " " } else { "" });
|
||||
}
|
||||
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
||||
ret = format!("{}...", ret);
|
||||
|
|
|
@ -12,16 +12,13 @@ pub struct FishGen<'a, 'b>
|
|||
}
|
||||
|
||||
impl<'a, 'b> FishGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||
FishGen { p: p }
|
||||
}
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self { FishGen { p: p } }
|
||||
|
||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||
let command = self.p.meta.bin_name.as_ref().unwrap();
|
||||
|
||||
// function to detect subcommand
|
||||
let detect_subcommand_function =
|
||||
r#"function __fish_using_command
|
||||
let detect_subcommand_function = r#"function __fish_using_command
|
||||
set cmd (commandline -opc)
|
||||
if [ (count $cmd) -eq (count $argv) ]
|
||||
for i in (seq (count $argv))
|
||||
|
@ -34,7 +31,8 @@ r#"function __fish_using_command
|
|||
return 1
|
||||
end
|
||||
|
||||
"#.to_string();
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
let mut buffer = detect_subcommand_function;
|
||||
gen_fish_inner(command, self, &command.to_string(), &mut buffer);
|
||||
|
@ -42,10 +40,7 @@ end
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_fish_inner(root_command: &str,
|
||||
comp_gen: &FishGen,
|
||||
parent_cmds: &str,
|
||||
buffer: &mut String) {
|
||||
fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, parent_cmds: &str, buffer: &mut String) {
|
||||
// example :
|
||||
//
|
||||
// complete
|
||||
|
@ -59,36 +54,36 @@ fn gen_fish_inner(root_command: &str,
|
|||
// -n "__fish_using_command myprog subcmd1" # complete for command "myprog subcmd1"
|
||||
|
||||
let basic_template = format!("complete -c {} -n \"__fish_using_command {}\"",
|
||||
root_command,
|
||||
parent_cmds);
|
||||
root_command,
|
||||
parent_cmds);
|
||||
|
||||
for option in &comp_gen.p.opts {
|
||||
for option in comp_gen.p.opts() {
|
||||
let mut template = basic_template.clone();
|
||||
if let Some(data) = option.short {
|
||||
if let Some(data) = option.s.short {
|
||||
template.push_str(format!(" -s {}", data).as_str());
|
||||
}
|
||||
if let Some(data) = option.long {
|
||||
if let Some(data) = option.s.long {
|
||||
template.push_str(format!(" -l {}", data).as_str());
|
||||
}
|
||||
if let Some(data) = option.help {
|
||||
if let Some(data) = option.b.help {
|
||||
template.push_str(format!(" -d \"{}\"", data).as_str());
|
||||
}
|
||||
if let Some(ref data) = option.possible_vals {
|
||||
if let Some(ref data) = option.v.possible_vals {
|
||||
template.push_str(format!(" -r -f -a \"{}\"", data.join(" ")).as_str());
|
||||
}
|
||||
buffer.push_str(template.as_str());
|
||||
buffer.push_str("\n");
|
||||
}
|
||||
|
||||
for flag in &comp_gen.p.flags {
|
||||
for flag in comp_gen.p.flags() {
|
||||
let mut template = basic_template.clone();
|
||||
if let Some(data) = flag.short {
|
||||
if let Some(data) = flag.s.short {
|
||||
template.push_str(format!(" -s {}", data).as_str());
|
||||
}
|
||||
if let Some(data) = flag.long {
|
||||
if let Some(data) = flag.s.long {
|
||||
template.push_str(format!(" -l {}", data).as_str());
|
||||
}
|
||||
if let Some(data) = flag.help {
|
||||
if let Some(data) = flag.b.help {
|
||||
template.push_str(format!(" -d \"{}\"", data).as_str());
|
||||
}
|
||||
buffer.push_str(template.as_str());
|
||||
|
@ -112,9 +107,6 @@ fn gen_fish_inner(root_command: &str,
|
|||
sub_parent_cmds.push_str(" ");
|
||||
}
|
||||
sub_parent_cmds.push_str(&subcommand.p.meta.name);
|
||||
gen_fish_inner(root_command,
|
||||
&sub_comp_gen,
|
||||
&sub_parent_cmds,
|
||||
buffer);
|
||||
gen_fish_inner(root_command, &sub_comp_gen, &sub_parent_cmds, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,4 +26,4 @@ macro_rules! get_zsh_arg_conflicts {
|
|||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,22 +24,20 @@ pub struct ComplGen<'a, 'b>
|
|||
}
|
||||
|
||||
impl<'a, 'b> ComplGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||
ComplGen { p: p }
|
||||
}
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self { ComplGen { p: p } }
|
||||
|
||||
pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
|
||||
match for_shell {
|
||||
Shell::Bash => BashGen::new(self.p).generate_to(buf),
|
||||
Shell::Fish => FishGen::new(self.p).generate_to(buf),
|
||||
Shell::Zsh => ZshGen::new(self.p).generate_to(buf),
|
||||
Shell::Bash => BashGen::new(self.p).generate_to(buf),
|
||||
Shell::Fish => FishGen::new(self.p).generate_to(buf),
|
||||
Shell::Zsh => ZshGen::new(self.p).generate_to(buf),
|
||||
Shell::PowerShell => PowerShellGen::new(self.p).generate_to(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gets all subcommands including child subcommands in the form of 'name' where the name
|
||||
// is a single word (i.e. "install") of the path to said subcommand (i.e.
|
||||
// is a single word (i.e. "install") of the path to said subcommand (i.e.
|
||||
// "rustup toolchain install")
|
||||
//
|
||||
// Also note, aliases are treated as their own subcommands but duplicates of whatever they're
|
||||
|
@ -55,7 +53,7 @@ pub fn all_subcommand_names(p: &Parser) -> Vec<String> {
|
|||
}
|
||||
|
||||
// Gets all subcommands including child subcommands in the form of ('name', 'bin_name') where the name
|
||||
// is a single word (i.e. "install") of the path and full bin_name of said subcommand (i.e.
|
||||
// is a single word (i.e. "install") of the path and full bin_name of said subcommand (i.e.
|
||||
// "rustup toolchain install")
|
||||
//
|
||||
// Also note, aliases are treated as their own subcommands but duplicates of whatever they're
|
||||
|
@ -75,7 +73,9 @@ pub fn all_subcommands(p: &Parser) -> Vec<(String, String)> {
|
|||
// Also note, aliases are treated as their own subcommands but duplicates of whatever they're
|
||||
// aliasing.
|
||||
pub fn subcommands_of(p: &Parser) -> Vec<(String, String)> {
|
||||
debugln!("fn=subcommands_of;name={};bin_name={}", p.meta.name, p.meta.bin_name.as_ref().unwrap());
|
||||
debugln!("fn=subcommands_of;name={};bin_name={}",
|
||||
p.meta.name,
|
||||
p.meta.bin_name.as_ref().unwrap());
|
||||
let mut subcmds = vec![];
|
||||
|
||||
debug!("Has subcommands...");
|
||||
|
@ -86,7 +86,8 @@ pub fn subcommands_of(p: &Parser) -> Vec<(String, String)> {
|
|||
if let Some(ref aliases) = p.meta.aliases {
|
||||
for &(n, _) in aliases {
|
||||
debugln!("Found alias...{}", n);
|
||||
let mut als_bin_name: Vec<_> = p.meta.bin_name.as_ref().unwrap().split(' ').collect();
|
||||
let mut als_bin_name: Vec<_> =
|
||||
p.meta.bin_name.as_ref().unwrap().split(' ').collect();
|
||||
als_bin_name.push(n);
|
||||
let old = als_bin_name.len() - 2;
|
||||
als_bin_name.swap_remove(old);
|
||||
|
@ -94,16 +95,19 @@ pub fn subcommands_of(p: &Parser) -> Vec<(String, String)> {
|
|||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
sdebugln!("Yes");
|
||||
for sc in &p.subcommands {
|
||||
debugln!("iter;name={};bin_name={}", sc.p.meta.name, sc.p.meta.bin_name.as_ref().unwrap());
|
||||
debugln!("iter;name={};bin_name={}",
|
||||
sc.p.meta.name,
|
||||
sc.p.meta.bin_name.as_ref().unwrap());
|
||||
|
||||
debugln!("Looking for aliases...");
|
||||
if let Some(ref aliases) = sc.p.meta.aliases {
|
||||
for &(n, _) in aliases {
|
||||
debugln!("Found alias...{}", n);
|
||||
let mut als_bin_name: Vec<_> = p.meta.bin_name.as_ref().unwrap().split(' ').collect();
|
||||
let mut als_bin_name: Vec<_> =
|
||||
p.meta.bin_name.as_ref().unwrap().split(' ').collect();
|
||||
als_bin_name.push(n);
|
||||
let old = als_bin_name.len() - 2;
|
||||
als_bin_name.swap_remove(old);
|
||||
|
|
|
@ -12,9 +12,7 @@ pub struct PowerShellGen<'a, 'b>
|
|||
}
|
||||
|
||||
impl<'a, 'b> PowerShellGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||
PowerShellGen { p: p }
|
||||
}
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self { PowerShellGen { p: p } }
|
||||
|
||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||
let bin_name = self.p.meta.bin_name.as_ref().unwrap();
|
||||
|
@ -32,7 +30,9 @@ impl<'a, 'b> PowerShellGen<'a, 'b> {
|
|||
bin_names.push(format!(r"./{0}.exe", bin_name));
|
||||
}
|
||||
|
||||
let bin_names = bin_names.iter().fold(String::new(), |previous, current| format!("{0}, '{1}'", previous, current));
|
||||
let bin_names = bin_names.iter().fold(String::new(), |previous, current| {
|
||||
format!("{0}, '{1}'", previous, current)
|
||||
});
|
||||
let bin_names = bin_names.trim_left_matches(", ");
|
||||
|
||||
let result = format!(r#"
|
||||
|
@ -75,18 +75,17 @@ impl<'a, 'b> PowerShellGen<'a, 'b> {
|
|||
fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (String, String) {
|
||||
let command_name = format!("{}_{}", previous_command_name, &p.meta.name);
|
||||
|
||||
let mut subcommands_detection_cases =
|
||||
if previous_command_name == "" {
|
||||
String::new()
|
||||
}
|
||||
else {
|
||||
format!(r"
|
||||
let mut subcommands_detection_cases = if previous_command_name == "" {
|
||||
String::new()
|
||||
} else {
|
||||
format!(r"
|
||||
'{0}' {{
|
||||
$command += '_{0}'
|
||||
break
|
||||
}}
|
||||
", &p.meta.name)
|
||||
};
|
||||
",
|
||||
&p.meta.name)
|
||||
};
|
||||
|
||||
let mut completions = String::new();
|
||||
for subcommand in &p.subcommands {
|
||||
|
@ -103,10 +102,13 @@ fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (S
|
|||
'{}' {{
|
||||
$completions = @({})
|
||||
}}
|
||||
", &command_name, completions.trim_right_matches(", "));
|
||||
",
|
||||
&command_name,
|
||||
completions.trim_right_matches(", "));
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let (subcommand_subcommands_detection_cases, subcommand_subcommands_cases) = generate_inner(&subcommand.p, &command_name);
|
||||
let (subcommand_subcommands_detection_cases, subcommand_subcommands_cases) =
|
||||
generate_inner(&subcommand.p, &command_name);
|
||||
subcommands_detection_cases.push_str(&subcommand_subcommands_detection_cases);
|
||||
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
||||
}
|
||||
|
|
|
@ -18,14 +18,7 @@ pub enum Shell {
|
|||
|
||||
impl Shell {
|
||||
/// A list of possible variants in `&'static str` form
|
||||
pub fn variants() -> [&'static str; 4] {
|
||||
[
|
||||
"zsh",
|
||||
"bash",
|
||||
"fish",
|
||||
"powershell"
|
||||
]
|
||||
}
|
||||
pub fn variants() -> [&'static str; 4] { ["zsh", "bash", "fish", "powershell"] }
|
||||
}
|
||||
|
||||
impl FromStr for Shell {
|
||||
|
@ -38,9 +31,7 @@ impl FromStr for Shell {
|
|||
"FISH" | _ if s.eq_ignore_ascii_case("fish") => Ok(Shell::Fish),
|
||||
"BASH" | _ if s.eq_ignore_ascii_case("bash") => Ok(Shell::Bash),
|
||||
"POWERSHELL" | _ if s.eq_ignore_ascii_case("powershell") => Ok(Shell::PowerShell),
|
||||
_ => Err(
|
||||
String::from("[valid values: bash, fish, zsh, powershell]")
|
||||
),
|
||||
_ => Err(String::from("[valid values: bash, fish, zsh, powershell]")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,11 +39,10 @@ impl FromStr for Shell {
|
|||
impl fmt::Display for Shell {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Shell::Bash => write!(f, "BASH"),
|
||||
Shell::Fish => write!(f, "FISH"),
|
||||
Shell::Zsh => write!(f, "ZSH"),
|
||||
Shell::Bash => write!(f, "BASH"),
|
||||
Shell::Fish => write!(f, "FISH"),
|
||||
Shell::Zsh => write!(f, "ZSH"),
|
||||
Shell::PowerShell => write!(f, "POWERSHELL"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ pub struct ZshGen<'a, 'b>
|
|||
impl<'a, 'b> ZshGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||
debugln!("fn=ZshGen::new;");
|
||||
ZshGen {
|
||||
p: p,
|
||||
}
|
||||
ZshGen { p: p }
|
||||
}
|
||||
|
||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||
|
@ -72,11 +70,11 @@ _{name} \"$@\"",
|
|||
// local commands; commands=(
|
||||
// 'show:Show the active and installed toolchains'
|
||||
// 'update:Update Rust toolchains'
|
||||
// # ... snip for brevity
|
||||
// # ... snip for brevity
|
||||
// 'help:Prints this message or the help of the given subcommand(s)'
|
||||
// )
|
||||
// _describe -t commands 'rustup commands' commands "$@"
|
||||
//
|
||||
//
|
||||
fn subcommand_details(p: &Parser) -> String {
|
||||
debugln!("fn=subcommand_details");
|
||||
// First we do ourself
|
||||
|
@ -114,7 +112,7 @@ _{bin_name_underscore}_commands() {{
|
|||
ret.join("\n")
|
||||
}
|
||||
|
||||
// Generates subcommand and positional argument completions in form of
|
||||
// Generates subcommand and positional argument completions in form of
|
||||
//
|
||||
// '[arg_name]:[arg_help]'
|
||||
//
|
||||
|
@ -142,7 +140,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
|||
debugln!("iter;subcommand={}", sc.p.meta.name);
|
||||
add_sc(sc, &sc.p.meta.name, &mut ret);
|
||||
if let Some(ref v) = sc.p.meta.aliases {
|
||||
for alias in v.iter().filter(|&&(_, vis)| vis).map(|&(n,_)| n) {
|
||||
for alias in v.iter().filter(|&&(_, vis)| vis).map(|&(n, _)| n) {
|
||||
add_sc(sc, alias, &mut ret);
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +148,11 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
|||
|
||||
// Then the positional args
|
||||
for arg in p.positionals() {
|
||||
debugln!("iter;arg={}", arg.name);
|
||||
debugln!("iter;arg={}", arg.b.name);
|
||||
let a = format!("\"{name}:{help}\" \\",
|
||||
name = arg.name.to_ascii_uppercase(),
|
||||
help = arg.help.unwrap_or(""));
|
||||
|
||||
name = arg.b.name.to_ascii_uppercase(),
|
||||
help = arg.b.help.unwrap_or(""));
|
||||
|
||||
if !a.is_empty() {
|
||||
ret.push(a);
|
||||
}
|
||||
|
@ -184,7 +182,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
|||
//
|
||||
// esac
|
||||
// ;;
|
||||
// esac",
|
||||
// esac",
|
||||
//
|
||||
// Where the following variables are present:
|
||||
// [name] = The subcommand name in the form of "install" for "rustup toolchain install"
|
||||
|
@ -193,7 +191,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
|||
// [repeat] = From the same recursive calls, but for all subcommands
|
||||
// [subcommand_args] = The same as zsh::get_args_of
|
||||
fn get_subcommands_of(p: &Parser) -> String {
|
||||
debugln!("fn=get_subcommands");
|
||||
debugln!("fn=get_subcommands_of");
|
||||
|
||||
debug!("Has subcommands...");
|
||||
if !p.has_subcommands() {
|
||||
|
@ -236,10 +234,13 @@ esac",
|
|||
|
||||
fn parser_of<'a, 'b>(p: &'b Parser<'a, 'b>, sc: &str) -> &'b Parser<'a, 'b> {
|
||||
debugln!("fn=parser_of;sc={}", sc);
|
||||
if sc == p.meta.bin_name.as_ref().unwrap_or(&String::new()) {
|
||||
return p;
|
||||
}
|
||||
&p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG).p
|
||||
}
|
||||
|
||||
// Writes out the args section, which ends up being the flags and opts, and a jump to
|
||||
// Writes out the args section, which ends up being the flags and opts, and a jump to
|
||||
// another ZSH function if there are positional args or subcommands.
|
||||
// The structer works like this:
|
||||
// ([conflicting_args]) [multiple] arg [takes_value] [[help]] [: :(possible_values)]
|
||||
|
@ -265,15 +266,15 @@ fn get_args_of(p: &Parser) -> String {
|
|||
let opts = write_opts_of(p);
|
||||
let flags = write_flags_of(p);
|
||||
let sc_or_a = if p.has_subcommands() || p.has_positionals() {
|
||||
format!("\"1:: :_{name}_commands\" \\",
|
||||
format!("\"1:: :_{name}_commands\" \\",
|
||||
name = p.meta.bin_name.as_ref().unwrap().replace(" ", "_"))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sc = if p.has_subcommands() {
|
||||
format!("\"*:: :->{name}\" \\", name = p.meta.name)
|
||||
} else {
|
||||
String::new()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sc = if p.has_subcommands() {
|
||||
format!("\"*:: :->{name}\" \\", name = p.meta.name)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if !opts.is_empty() {
|
||||
|
@ -299,12 +300,20 @@ fn write_opts_of(p: &Parser) -> String {
|
|||
for o in p.opts() {
|
||||
debugln!("iter;o={}", o.name());
|
||||
let help = o.help().unwrap_or("");
|
||||
let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
|
||||
conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };
|
||||
let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
|
||||
conflicts = if conflicts.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("({})", conflicts)
|
||||
};
|
||||
|
||||
let multiple = if o.is_set(ArgSettings::Multiple) { "*" } else { "" };
|
||||
let multiple = if o.is_set(ArgSettings::Multiple) {
|
||||
"*"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let pv = if let Some(pv_vec) = o.possible_vals() {
|
||||
format!(": :({})", pv_vec.join(" "))
|
||||
format!(": :({})", pv_vec.join(" "))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
@ -341,10 +350,18 @@ fn write_flags_of(p: &Parser) -> String {
|
|||
for f in p.flags() {
|
||||
debugln!("iter;f={}", f.name());
|
||||
let help = f.help().unwrap_or("");
|
||||
let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
|
||||
conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };
|
||||
let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
|
||||
conflicts = if conflicts.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("({})", conflicts)
|
||||
};
|
||||
|
||||
let multiple = if f.is_set(ArgSettings::Multiple) { "*" } else { "" };
|
||||
let multiple = if f.is_set(ArgSettings::Multiple) {
|
||||
"*"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if let Some(short) = f.short() {
|
||||
let s = format!("\"{conflicts}{multiple}-{arg}[{help}]\" \\",
|
||||
multiple = multiple,
|
||||
|
@ -369,4 +386,4 @@ fn write_flags_of(p: &Parser) -> String {
|
|||
}
|
||||
|
||||
ret.join("\n")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::process;
|
|||
use std::result::Result as StdResult;
|
||||
|
||||
// Internal
|
||||
use args::any_arg::AnyArg;
|
||||
use args::{FlagBuilder, AnyArg};
|
||||
use fmt;
|
||||
use suggestions;
|
||||
|
||||
|
@ -52,7 +52,7 @@ pub enum ErrorKind {
|
|||
UnknownArgument,
|
||||
|
||||
/// Occurs when the user provides an unrecognized [`SubCommand`] which meets the threshold for
|
||||
/// being similar enough to an existing subcommand.
|
||||
/// being similar enough to an existing subcommand.
|
||||
/// If it doesn't meet the threshold, or the 'suggestions' feature is disabled,
|
||||
/// the more general [`UnknownArgument`] error is returned.
|
||||
///
|
||||
|
@ -77,7 +77,7 @@ pub enum ErrorKind {
|
|||
|
||||
/// Occurs when the user provides an unrecognized [`SubCommand`] which either
|
||||
/// doesn't meet the threshold for being similar enough to an existing subcommand,
|
||||
/// or the 'sggestions' feature is disabled.
|
||||
/// or the 'sggestions' feature is disabled.
|
||||
/// Otherwise the more detailed [`InvalidSubcommand`] error is returned.
|
||||
///
|
||||
/// This error typically happens when passing additional subcommand names to the `help`
|
||||
|
@ -402,9 +402,7 @@ impl Error {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
write!(w, "{}", self.message)
|
||||
}
|
||||
pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> { write!(w, "{}", self.message) }
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A,
|
||||
|
@ -703,13 +701,22 @@ impl Error {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn value_validation(err: String, color: fmt::ColorWhen) -> Self {
|
||||
pub fn value_validation<'a, 'b, A>(arg: Option<&A>, err: String, color: fmt::ColorWhen) -> Self
|
||||
where A: AnyArg<'a, 'b> + Display
|
||||
{
|
||||
let c = fmt::Colorizer {
|
||||
use_stderr: true,
|
||||
when: color,
|
||||
};
|
||||
Error {
|
||||
message: format!("{} {}", c.error("error:"), err),
|
||||
message: format!("{} Invalid value{}: {}",
|
||||
c.error("error:"),
|
||||
if let Some(a) = arg {
|
||||
format!(" for '{}'", c.warning(a.to_string()))
|
||||
} else {
|
||||
"".to_string()
|
||||
},
|
||||
err),
|
||||
kind: ErrorKind::ValueValidation,
|
||||
info: None,
|
||||
}
|
||||
|
@ -717,7 +724,8 @@ impl Error {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn value_validation_auto(err: String) -> Self {
|
||||
Error::value_validation(err, fmt::ColorWhen::Auto)
|
||||
let n: Option<&FlagBuilder> = None;
|
||||
Error::value_validation(n, err, fmt::ColorWhen::Auto)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -858,21 +866,15 @@ impl Error {
|
|||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
&*self.message
|
||||
}
|
||||
fn description(&self) -> &str { &*self.message }
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result {
|
||||
writeln!(f, "{}", self.message)
|
||||
}
|
||||
fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result { writeln!(f, "{}", self.message) }
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::with_description(e.description(), ErrorKind::Io)
|
||||
}
|
||||
fn from(e: io::Error) -> Self { Error::with_description(e.description(), ErrorKind::Io) }
|
||||
}
|
||||
|
||||
impl From<std_fmt::Error> for Error {
|
||||
|
|
|
@ -142,16 +142,12 @@ impl<T: fmt::Display> Format<T> {
|
|||
|
||||
#[cfg(all(feature = "color", not(target_os = "windows")))]
|
||||
impl<T: AsRef<str>> fmt::Display for Format<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", &self.format())
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &self.format()) }
|
||||
}
|
||||
|
||||
#[cfg(any(not(feature = "color"), target_os = "windows"))]
|
||||
impl<T: fmt::Display> fmt::Display for Format<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", &self.format())
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &self.format()) }
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "color", not(target_os = "windows")))]
|
||||
|
|
484
src/lib.rs
484
src/lib.rs
|
@ -3,52 +3,42 @@
|
|||
// (see LICENSE or <http://opensource.org/licenses/MIT>) All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! A simple-to-use, efficient, and full-featured library for parsing command line arguments and subcommands when writing console/terminal applications.
|
||||
//! `clap` is a simple-to-use, efficient, and full-featured library for parsing command line
|
||||
//! arguments and subcommands when writing console/terminal applications.
|
||||
//!
|
||||
//! ## About
|
||||
//!
|
||||
//! `clap` is used to parse *and validate* the string of command line arguments provided by the 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.
|
||||
//! `clap` is used to parse *and validate* the string of command line arguments provided by the 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.
|
||||
//!
|
||||
//! `clap` also provides the traditional version and help switches (or flags) 'for free' meaning automatically with no configuration. It does this by checking list of valid possibilities you supplied and adding only the ones you haven't already defined. If you are using subcommands, `clap` will also auto-generate a `help` subcommand for you in addition to the traditional flags.
|
||||
//! `clap` also provides the traditional version and help switches (or flags) 'for free' meaning
|
||||
//! automatically with no configuration. It does this by checking list of valid possibilities you
|
||||
//! supplied and adding only the ones you haven't already defined. If you are using subcommands,
|
||||
//! `clap` will also auto-generate a `help` subcommand for you in addition to the traditional flags.
|
||||
//!
|
||||
//! Once `clap` parses the user provided string of arguments, it returns the matches along with any applicable values. If the user made an error or typo, `clap` informs them of the mistake and exits gracefully (or returns a `Result` type and allows you to perform any clean up prior to exit). Because of this, you can make reasonable assumptions in your code about the validity of the arguments.
|
||||
//! Once `clap` parses the user provided string of arguments, it returns the matches along with any
|
||||
//! applicable values. If the user made an error or typo, `clap` informs them of the mistake and
|
||||
//! exits gracefully (or returns a `Result` type and allows you to perform any clean up prior to
|
||||
//! exit). Because of this, you can make reasonable assumptions in your code about the validity of
|
||||
//! the arguments.
|
||||
//!
|
||||
//! ## FAQ
|
||||
//!
|
||||
//! For a full FAQ and more in depth details, see [the wiki page](https://github.com/kbknapp/clap-rs/wiki/FAQ)
|
||||
//!
|
||||
//! ### Comparisons
|
||||
//!
|
||||
//! 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 [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.
|
||||
//!
|
||||
//! `getopts` also doesn't allocate much, or at all. This gives it a very small performance boost. Although, as you start implementing additional features, that boost quickly disappears.
|
||||
//!
|
||||
//! Personally, I find many, many uses of `getopts` are manually implementing features that `clap` provides by default. Using `clap` simplifies your codebase allowing you to focus on your application, and not argument parsing.
|
||||
//!
|
||||
//! #### How does `clap` compare to [docopt.rs](https://github.com/docopt/docopt.rs)?
|
||||
//!
|
||||
//! I first want to say I'm a big a fan of BurntSushi's work, the creator of `Docopt.rs`. I aspire to produce the quality of libraries that this man does! When it comes to comparing these two libraries they are very different. `docopt` tasks you with writing a help message, and then it parsers that message for you to determine all valid arguments and their use. Some people LOVE this approach, others do not. If you're willing to write a detailed help message, it's nice that you can stick that in your program and have `docopt` do the rest. On the downside, it's far less flexible.
|
||||
//!
|
||||
//! `docopt` is also excellent at translating arguments into Rust types automatically. There is even a syntax extension which will do all this for you, if you're willing to use a nightly compiler (use of a stable compiler requires you to somewhat manually translate from arguments to Rust types). To use BurntSushi's words, `docopt` is also a sort of black box. You get what you get, and it's hard to tweak implementation or customize the experience for your use case.
|
||||
//!
|
||||
//! Because `docopt` is doing a ton of work to parse your help messages and determine what you were trying to communicate as valid arguments, it's also one of the more heavy weight parsers performance-wise. For most applications this isn't a concern and this isn't to say `docopt` is slow, in fact from it. This is just something to keep in mind while comparing.
|
||||
//!
|
||||
//! #### All else being equal, what are some reasons to use `clap`?
|
||||
//!
|
||||
//! `clap` is as fast, and as lightweight as possible while still giving all the features you'd expect from a modern argument parser. In fact, for the amount and type of features `clap` offers it remains about as fast as `getopts`. If you use `clap` when just need some simple arguments parsed, you'll find its a walk in the park. `clap` also makes it possible to represent extremely complex, and advanced requirements, without too much thought. `clap` aims to be intuitive, easy to use, and fully capable for wide variety use cases and needs.
|
||||
//!
|
||||
//! ## Quick Example
|
||||
//!
|
||||
//! The following examples show a quick example of some of the very basic functionality of `clap`. For more advanced usage, such as requirements, conflicts, groups, multiple values and occurrences see the [documentation](http://kbknapp.github.io/clap-rs/clap/index.html), [examples/](examples) directory of this repository or the [video tutorials](https://www.youtube.com/playlist?list=PLza5oFLQGTl0Bc_EU_pBNcX-rhVqDTRxv) (which are quite outdated by now).
|
||||
//! The following examples show a quick example of some of the very basic functionality of `clap`.
|
||||
//! For more advanced usage, such as requirements, conflicts, groups, multiple values and
|
||||
//! occurrences see the [documentation](https://docs.rs/clap/), [examples/](examples) directory of
|
||||
//! this repository or the [video tutorials](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U).
|
||||
//!
|
||||
//! **NOTE:** All these examples are functionally the same, but show three different styles in which to use `clap`
|
||||
//!
|
||||
//! The following example is show a method that 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.
|
||||
//! **NOTE:** All of these examples are functionally the same, but show different styles in which to
|
||||
//! use `clap`
|
||||
//!
|
||||
//! The first example shows a method that 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.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // (Full example with detailed comments in examples/01b_quick_example.rs)
|
||||
//! //
|
||||
|
@ -57,7 +47,7 @@
|
|||
//! // to generate arguments dynamically.
|
||||
//! extern crate clap;
|
||||
//! use clap::{Arg, App, SubCommand};
|
||||
//!
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let matches = App::new("My Super Program")
|
||||
//! .version("1.0")
|
||||
|
@ -85,15 +75,15 @@
|
|||
//! .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);
|
||||
//!
|
||||
//!
|
||||
//! // 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());
|
||||
//!
|
||||
//!
|
||||
//! // 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") {
|
||||
|
@ -102,7 +92,7 @@
|
|||
//! 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
|
||||
//! if let Some(matches) = matches.subcommand_matches("test") {
|
||||
|
@ -112,15 +102,15 @@
|
|||
//! println!("Printing normally...");
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//!
|
||||
//! // more program logic goes here...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The following example is functionally the same as the one above,
|
||||
//! but shows a far less verbose method but sacrifices some of the advanced configuration options
|
||||
//! (not shown in this small example).
|
||||
//!
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // (Full example with detailed comments in examples/01a_quick_example.rs)
|
||||
//! //
|
||||
|
@ -128,7 +118,7 @@
|
|||
//! // which is less verbose
|
||||
//! extern crate clap;
|
||||
//! use clap::{Arg, App, SubCommand};
|
||||
//!
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let matches = App::new("myapp")
|
||||
//! .version("1.0")
|
||||
|
@ -144,46 +134,21 @@
|
|||
//! .author("Someone E. <someone_else@other.com>")
|
||||
//! .arg_from_usage("-d, --debug 'Print debug information'"))
|
||||
//! .get_matches();
|
||||
//!
|
||||
//!
|
||||
//! // Same as previous example...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! The following combines the previous two examples by using the less verbose `from_usage` methods and the performance of the Builder Pattern.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // (Full example with detailed comments in examples/01c_quick_example.rs)
|
||||
//! //
|
||||
//! // This example demonstrates clap's "usage strings" method of creating arguments
|
||||
//! // which is less less verbose
|
||||
//! #[macro_use]
|
||||
//! extern crate clap;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let matches = clap_app!(myapp =>
|
||||
//! (version: "1.0")
|
||||
//! (author: "Kevin K. <kbknapp@gmail.com>")
|
||||
//! (about: "Does awesome things")
|
||||
//! (@arg config: -c --config +takes_value "Sets a custom config file")
|
||||
//! (@arg INPUT: +required "Sets the input file to use")
|
||||
//! (@arg verbose: -v ... "Sets the level of verbosity")
|
||||
//! (@subcommand test =>
|
||||
//! (about: "controls testing features")
|
||||
//! (version: "1.3")
|
||||
//! (author: "Someone E. <someone_else@other.com>")
|
||||
//! (@arg verbose: -d --debug "Print debug information")
|
||||
//! )
|
||||
//! ).get_matches();
|
||||
//!
|
||||
//! // Same as previous examples...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This final 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 (we'll use the same both examples above to keep it functionally equivalent):
|
||||
//!
|
||||
//! First, create the `cli.yml` file to hold your CLI options, but it could be called anything we
|
||||
//! like:
|
||||
//!
|
||||
//! ```yaml
|
||||
//! name: myapp
|
||||
//! version: 1.0
|
||||
//! version: "1.0"
|
||||
//! author: Kevin K. <kbknapp@gmail.com>
|
||||
//! about: Does awesome things
|
||||
//! args:
|
||||
|
@ -204,19 +169,21 @@
|
|||
//! subcommands:
|
||||
//! - test:
|
||||
//! about: controls testing features
|
||||
//! version: 1.3
|
||||
//! version: "1.3"
|
||||
//! author: Someone E. <someone_else@other.com>
|
||||
//! args:
|
||||
//! - debug:
|
||||
//! short: d
|
||||
//! help: print debug information
|
||||
//! ```
|
||||
//!
|
||||
//! 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:
|
||||
//!
|
||||
//! Because this feature is not compiled in by default we need to enable a feature flag in Cargo.toml:
|
||||
//! Simply change your `clap = "2"` to `clap = {version = "2", features = ["yaml"]}`.
|
||||
//! Simply change your `clap = "~2.19.0"` to `clap = {version = "~2.19.0", features = ["yaml"]}`.
|
||||
//!
|
||||
//! At last we create our `main.rs` file just like we would have with the previous two examples:
|
||||
//!
|
||||
//!
|
||||
//! ```ignore
|
||||
//! // (Full example with detailed comments in examples/17_yaml.rs)
|
||||
//! //
|
||||
|
@ -225,179 +192,322 @@
|
|||
//! #[macro_use]
|
||||
//! extern crate clap;
|
||||
//! use clap::App;
|
||||
//!
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // The YAML file is found relative to the current file, similar to how modules are found
|
||||
//! let yaml = load_yaml!("cli.yml");
|
||||
//! let matches = App::from_yaml(yaml).get_matches();
|
||||
//!
|
||||
//!
|
||||
//! // Same as previous examples...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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
|
||||
//!
|
||||
//!
|
||||
//! Finally there is a macro version, which is like a hybrid approach offering the speed of the
|
||||
//! builder pattern (the first example), but without all the verbosity.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! #[macro_use]
|
||||
//! extern crate clap;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let matches = clap_app!(myapp =>
|
||||
//! (version: "1.0")
|
||||
//! (author: "Kevin K. <kbknapp@gmail.com>")
|
||||
//! (about: "Does awesome things")
|
||||
//! (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
|
||||
//! (@arg INPUT: +required "Sets the input file to use")
|
||||
//! (@arg debug: -d ... "Sets the level of debugging information")
|
||||
//! (@subcommand test =>
|
||||
//! (about: "controls testing features")
|
||||
//! (version: "1.3")
|
||||
//! (author: "Someone E. <someone_else@other.com>")
|
||||
//! (@arg verbose: -v --verbose "Print test information verbosely")
|
||||
//! )
|
||||
//! ).get_matches();
|
||||
//!
|
||||
//! // Same as before...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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
|
||||
//!
|
||||
//! ```text
|
||||
//! $ myprog --help
|
||||
//! My Super Program 1.0
|
||||
//! Kevin K. <kbknapp@gmail.com>
|
||||
//! Does awesome things
|
||||
//!
|
||||
//!
|
||||
//! USAGE:
|
||||
//! MyApp [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND]
|
||||
//!
|
||||
//!
|
||||
//! FLAGS:
|
||||
//! -h, --help Prints this message
|
||||
//! -v Sets the level of verbosity
|
||||
//! -V, --version Prints version information
|
||||
//!
|
||||
//!
|
||||
//! OPTIONS:
|
||||
//! -c, --config <FILE> Sets a custom config file
|
||||
//!
|
||||
//!
|
||||
//! ARGS:
|
||||
//! INPUT The input file to use
|
||||
//!
|
||||
//!
|
||||
//! SUBCOMMANDS:
|
||||
//! help Prints this message
|
||||
//! test Controls testing features
|
||||
//! ```
|
||||
//!
|
||||
//! **NOTE:** You could also run `myapp test --help` to see similar output and options for the `test` subcommand.
|
||||
//!
|
||||
//!
|
||||
//! **NOTE:** You could also run `myapp test --help` to see similar output and options for the
|
||||
//! `test` subcommand.
|
||||
//!
|
||||
//! ## Try it!
|
||||
//!
|
||||
//!
|
||||
//! ### Pre-Built Test
|
||||
//!
|
||||
//!
|
||||
//! To try out the pre-built example, use the following steps:
|
||||
//!
|
||||
//!
|
||||
//! * Clone the repository `$ git clone https://github.com/kbknapp/clap-rs && cd clap-rs/tests`
|
||||
//! * Compile the example `$ cargo build --release`
|
||||
//! * Run the help info `$ ./target/release/claptests --help`
|
||||
//! * Play with the arguments!
|
||||
//!
|
||||
//!
|
||||
//! ### BYOB (Build Your Own Binary)
|
||||
//!
|
||||
//! To test `clap`'s default auto-generated help/version, follow these steps:
|
||||
//!
|
||||
//! * Create a new cargo project:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! cargo new fake --bin && cd fake
|
||||
//! ```
|
||||
//!
|
||||
//! * Add `clap` to your `Cargo.toml`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! clap = "2"
|
||||
//! ```
|
||||
//!
|
||||
//! * Add the following to your `src/main.rs`:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate clap;
|
||||
//! use clap::App;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! App::new("fake").version("v1.0-beta").get_matches();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! * Build your program, then run it:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! cargo build --release
|
||||
//! ./target/release/fake --help
|
||||
//! ./target/release/fake --version
|
||||
//! ```
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! For full usage, add `clap` as a dependency in your `Cargo.toml` file to use from crates.io:
|
||||
//!
|
||||
//! To test out `clap`'s default auto-generated help/version follow these steps:
|
||||
//! * Create a new cargo project `$ cargo new fake --bin && cd fake`
|
||||
//! * Add `clap` to your `Cargo.toml`
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! clap = "2"
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! * Add the following to your `src/main.rs`
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate clap;
|
||||
//! use clap::App;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! App::new("fake").version("v1.0-beta").get_matches();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! * Build your program `$ cargo build --release`
|
||||
//! * Run with help or version `$ ./target/release/fake --help` or `$ ./target/release/fake
|
||||
//! --version`
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! For full usage, add `clap` as a dependency in your `Cargo.toml` (it is **highly** recommended to
|
||||
//! use the `~major.minor.patch` style versions in your `Cargo.toml`, for more information see
|
||||
//! [Compatibility Policy](#compatibility-policy)) to use from crates.io:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! clap = "~2.19.0"
|
||||
//! ```
|
||||
//!
|
||||
//! Or get the latest changes from the master branch at github:
|
||||
//!
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies.clap]
|
||||
//! git = "https://github.com/kbknapp/clap-rs.git"
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! Add `extern crate clap;` to your crate root.
|
||||
//!
|
||||
//! Define a list of valid arguments for your program (see the [documentation](https://kbknapp.github.io/clap-rs/index.html) or [examples/](examples) directory of this repo)
|
||||
//!
|
||||
//!
|
||||
//! Define a list of valid arguments for your program (see the
|
||||
//! [documentation](https://docs.rs/clap/) or [examples/](examples) directory of this repo)
|
||||
//!
|
||||
//! Then run `cargo build` or `cargo update && cargo build` for your project.
|
||||
//!
|
||||
//!
|
||||
//! ### Optional Dependencies / Features
|
||||
//!
|
||||
//!
|
||||
//! #### Features enabled by default
|
||||
//!
|
||||
//! * **"suggestions"**: Turns on the `Did you mean '--myoption'?` feature for when users make typos.
|
||||
//! * **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs.
|
||||
//! * **"wrap_help"**: Wraps the help at the actual terminal width when available, instead of 120 chracters.
|
||||
//!
|
||||
//!
|
||||
//! * **"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` and `libc`)
|
||||
//! * **"wrap_help"**: Wraps the help at the actual terminal width when available, instead of 120 chracters. (builds dependency `term_size`, and `libc`)
|
||||
//!
|
||||
//! To disable these, add this to your `Cargo.toml`:
|
||||
//!
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies.clap]
|
||||
//! version = "2"
|
||||
//! version = "~2.19.0"
|
||||
//! default-features = false
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! You can also selectively enable only the features you'd like to include, by adding:
|
||||
//!
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies.clap]
|
||||
//! version = "2"
|
||||
//! version = "~2.19.0"
|
||||
//! default-features = false
|
||||
//!
|
||||
//!
|
||||
//! # Cherry-pick the features you'd like to use
|
||||
//! features = [ "suggestions", "color" ]
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! #### Opt-in features
|
||||
//!
|
||||
//! * **"yaml"**: Enables building CLIs from YAML documents.
|
||||
//!
|
||||
//!
|
||||
//! * **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
|
||||
//! * **"unstable"**: Enables unstable `clap` features that may change from release to release
|
||||
//!
|
||||
//! ### 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)
|
||||
//!
|
||||
//! ### More Information
|
||||
//!
|
||||
//! You can find complete documentation on the [github-pages site](http://kbknapp.github.io/clap-rs/clap/index.html) for this project.
|
||||
//!
|
||||
//!
|
||||
//! You can find complete documentation on the [docs.rs](https://docs.rs/clap/) for this project.
|
||||
//!
|
||||
//! You can also find usage examples in the [examples/](examples) directory of this repo.
|
||||
//!
|
||||
//!
|
||||
//! #### Video Tutorials
|
||||
//!
|
||||
//! There's also the video tutorial series [Argument Parsing with Rust v2](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U).
|
||||
//!
|
||||
//! These videos slowly trickle out as I finish them and currently a work in progress.
|
||||
//!
|
||||
//! ## How to Contribute
|
||||
//!
|
||||
//! Contributions are always welcome! And there is a multitude of ways in which you can help
|
||||
//! depending on what you like to do, or are good at. Anything from documentation, code cleanup,
|
||||
//! issue completion, new features, you name it, even filing issues is contributing and greatly
|
||||
//! appreciated!
|
||||
//!
|
||||
//! There's also the video tutorial series [Argument Parsing with Rust](https://www.youtube.com/playlist?list=PLza5oFLQGTl0Bc_EU_pBNcX-rhVqDTRxv) that I've been working on.
|
||||
//!
|
||||
//! *Note*: Two new videos have just been added ([08 From Usage](https://youtu.be/xc6VdedFrG0), and [09 Typed Values](https://youtu.be/mZn3C1DnD90)), if you're already familiar with `clap` but want to know more about these two details you can check out those videos without watching the previous few.
|
||||
//!
|
||||
//! *Note*: Apologies for the resolution of the first video, it will be updated to a better resolution soon. The other videos have a proper resolution.
|
||||
//!
|
||||
//! ### For clap contributors
|
||||
//!
|
||||
//! If contributing, you can run theese commands to test everything:
|
||||
//!
|
||||
//! ```sh
|
||||
//! Another really great way to help is if you find an interesting, or helpful way in which to use
|
||||
//! `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell
|
||||
//! me. I'm all about giving credit where credit is due :)
|
||||
//!
|
||||
//! Please read [CONTRIBUTING.md](.github/CONTRIBUTING.md) before you start contributing.
|
||||
//!
|
||||
//!
|
||||
//! ### Testing Code
|
||||
//!
|
||||
//! To test with all features both enabled and disabled, you can run theese commands:
|
||||
//!
|
||||
//! ```text
|
||||
//! $ cargo test --no-default-features
|
||||
//! $ cargo test --features yaml
|
||||
//! $ cargo test --features "yaml unstable"
|
||||
//! ```
|
||||
//!
|
||||
//! Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the
|
||||
//! prebuilt recipies. *Not* using `just` is prfeclty fine as well, it simply bundles commands
|
||||
//! automatically.
|
||||
//!
|
||||
//! For example, to test the code, as above simply run:
|
||||
//!
|
||||
//! ```text
|
||||
//! $ just run-tests`
|
||||
//! ```
|
||||
//!
|
||||
//! From here on, I will lis the appropriate `cargo` command as well as the `just` command.
|
||||
//!
|
||||
//! Sometimes it's helpful to only run a subset of the tests, which can be done via:
|
||||
//!
|
||||
//! ```text
|
||||
//! $ cargo test --test <test_name>
|
||||
//!
|
||||
//! # Or
|
||||
//!
|
||||
//! $ just run-test <test_name>
|
||||
//! ```
|
||||
//!
|
||||
//! ### Linting Code
|
||||
//!
|
||||
//! 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:
|
||||
//!
|
||||
//! ```text
|
||||
//! $ rustup override add nightly
|
||||
//! $ cargo build --features lints
|
||||
//! $ rustup override remove
|
||||
//!
|
||||
//! # Or
|
||||
//!
|
||||
//! $ just lint
|
||||
//! ```
|
||||
//!
|
||||
//! ### Debugging Code
|
||||
//!
|
||||
//! Another helpful technique is to see the `clap` debug output while developing features. In order
|
||||
//! to see the debug output while running the full test suite or individual tests, run:
|
||||
//!
|
||||
//! ```text
|
||||
//! $ cargo test --features debug
|
||||
//!
|
||||
//! # Or for individual tests
|
||||
//! $ cargo test --test <test_name> --features debug
|
||||
//!
|
||||
//! # The corresponding just command for individual debugging tests is:
|
||||
//! $ just debug <test_name>
|
||||
//! ```
|
||||
//!
|
||||
//! ### Goals
|
||||
//!
|
||||
//! There are a few goals of `clap` that I'd like to maintain throughout contributions. If your
|
||||
//! proposed changes break, or go against any of these goals we'll discuss the changes further
|
||||
//! before merging (but will *not* be ignored, all contributes are welcome!). These are by no means
|
||||
//! hard-and-fast rules, as I'm no expert and break them myself from time to time (even if by
|
||||
//! mistake or ignorance).
|
||||
//!
|
||||
//! * Remain backwards compatible when possible
|
||||
//! - If backwards compatibility *must* be broken, use deprecation warnings if at all possible before
|
||||
//! removing legacy code - This does not apply for security concerns
|
||||
//! * Parse arguments quickly
|
||||
//! - Parsing of arguments shouldn't slow down usage of the main program - This is also true of
|
||||
//! generating help and usage information (although *slightly* less stringent, as the program is about
|
||||
//! to exit)
|
||||
//! * Try to be cognizant of memory usage
|
||||
//! - Once parsing is complete, the memory footprint of `clap` should be low since the main program
|
||||
//! is the star of the show
|
||||
//! * `panic!` on *developer* error, exit gracefully on *end-user* error
|
||||
//!
|
||||
//! ### Compatibility Policy
|
||||
//!
|
||||
//! Because `clap` takes SemVer and compatibility seriously, this is the official policy regarding
|
||||
//! breaking changes and previous versions of Rust.
|
||||
//!
|
||||
//! `clap` will pin the minimum required version of Rust to the CI builds. Bumping the minimum
|
||||
//! version of Rust is considered a minor breaking change, meaning *at a minimum* the minor version
|
||||
//! of `clap` will be bumped.
|
||||
//!
|
||||
//! In order to keep from being suprised of breaking changes, it is **highly** recommended to use
|
||||
//! the `~major.minor.patch` style in your `Cargo.toml`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies] clap = "~2.19.0"
|
||||
//! ```
|
||||
//!
|
||||
//! If you have a nightly compiler you can append `--features lints` to both commands
|
||||
//! to get style warnings and code smells; If you get one from code you think is fine,
|
||||
//! you can ignore it by prepending `#[cfg_attr(feature="lints", allow(lint_name))]`
|
||||
//! to the function or impl block.
|
||||
//! 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.
|
||||
//!
|
||||
//! If you are debugging (or just trying to understand the code) you can enable the
|
||||
//! "debug" feature which will trace function calls and brances in some parts of the code.
|
||||
//! #### 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.13.0,
|
||||
//! meaning `clap` is garunteed to compile with 1.11.0 and beyond. At the 1.14.0 release, `clap`
|
||||
//! will be garunteed to compile with 2.12.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`
|
||||
//!
|
||||
//! ## License
|
||||
//!
|
||||
//! `clap` is licensed under the MIT license. Please read the [LICENSE-MIT](LICENSE-MIT) file in this repository for more information.
|
||||
//!
|
||||
//! `clap` is licensed under the MIT license. Please read the [LICENSE-MIT](LICENSE-MIT) file in
|
||||
//! this repository for more information.
|
||||
|
||||
#![crate_type= "lib"]
|
||||
#![deny(
|
||||
|
|
|
@ -28,19 +28,13 @@ impl OsStrExt3 for OsStr {
|
|||
use std::mem;
|
||||
unsafe { mem::transmute(b) }
|
||||
}
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8)
|
||||
}
|
||||
fn as_bytes(&self) -> &[u8] { self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8) }
|
||||
}
|
||||
|
||||
impl OsStrExt2 for OsStr {
|
||||
fn starts_with(&self, s: &[u8]) -> bool {
|
||||
self.as_bytes().starts_with(s)
|
||||
}
|
||||
fn starts_with(&self, s: &[u8]) -> bool { self.as_bytes().starts_with(s) }
|
||||
|
||||
fn is_empty_(&self) -> bool {
|
||||
self.as_bytes().is_empty()
|
||||
}
|
||||
fn is_empty_(&self) -> bool { self.as_bytes().is_empty() }
|
||||
|
||||
fn contains_byte(&self, byte: u8) -> bool {
|
||||
for b in self.as_bytes() {
|
||||
|
@ -74,9 +68,7 @@ impl OsStrExt2 for OsStr {
|
|||
(OsStr::from_bytes(&self.as_bytes()[..i]), OsStr::from_bytes(&self.as_bytes()[i..]))
|
||||
}
|
||||
|
||||
fn len_(&self) -> usize {
|
||||
self.as_bytes().len()
|
||||
}
|
||||
fn len_(&self) -> usize { self.as_bytes().len() }
|
||||
|
||||
fn split(&self, b: u8) -> OsSplit {
|
||||
OsSplit {
|
||||
|
|
|
@ -111,7 +111,7 @@ impl<'a> UsageParser<'a> {
|
|||
{
|
||||
debugln!("fn=stop_at;");
|
||||
self.start = self.pos;
|
||||
self.pos += self.usage[self.start..].bytes().take_while(|&b| f(b) ).count();
|
||||
self.pos += self.usage[self.start..].bytes().take_while(|&b| f(b)).count();
|
||||
}
|
||||
|
||||
fn short_or_long(&mut self, arg: &mut Arg<'a, 'a>) {
|
||||
|
@ -192,14 +192,10 @@ impl<'a> UsageParser<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn name_end(b: u8) -> bool {
|
||||
b != b']' && b != b'>'
|
||||
}
|
||||
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'-'
|
||||
}
|
||||
fn token(b: u8) -> bool { b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'-' }
|
||||
|
||||
#[inline]
|
||||
fn long_end(b: u8) -> bool {
|
||||
|
@ -207,9 +203,7 @@ fn long_end(b: u8) -> bool {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn help_start(b: u8) -> bool {
|
||||
b != b'\''
|
||||
}
|
||||
fn help_start(b: u8) -> bool { b != b'\'' }
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
@ -990,7 +984,8 @@ mod test {
|
|||
assert!(d.is_set(ArgSettings::Multiple));
|
||||
assert!(d.is_set(ArgSettings::TakesValue));
|
||||
assert!(d.is_set(ArgSettings::Required));
|
||||
assert_eq!(d.val_names.unwrap().values().collect::<Vec<_>>(), [&"option"]);
|
||||
assert_eq!(d.val_names.unwrap().values().collect::<Vec<_>>(),
|
||||
[&"option"]);
|
||||
assert!(d.num_vals.is_none());
|
||||
}
|
||||
|
||||
|
@ -1199,7 +1194,8 @@ mod test {
|
|||
let a = Arg::from_usage("[ñämê] --ôpt=[üñíčöĐ€] 'hælp'");
|
||||
assert_eq!(a.name, "ñämê");
|
||||
assert_eq!(a.long, Some("ôpt"));
|
||||
assert_eq!(a.val_names.unwrap().values().collect::<Vec<_>>(), [&"üñíčöĐ€"]);
|
||||
assert_eq!(a.val_names.unwrap().values().collect::<Vec<_>>(),
|
||||
[&"üñíčöĐ€"]);
|
||||
assert_eq!(a.help, Some("hælp"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,6 +225,66 @@ fn require_delims() {
|
|||
assert_eq!(m.value_of("file").unwrap(), "some");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_hyphen_pass() {
|
||||
let r = App::new("mvae")
|
||||
.arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
|
||||
.get_matches_from_safe(vec!["", "-o", "-2", "3"]);
|
||||
assert!(r.is_ok());
|
||||
let m = r.unwrap();
|
||||
assert!(m.is_present("o"));
|
||||
assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2", "3"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_hyphen_fail() {
|
||||
let r = App::new("mvae")
|
||||
.arg( Arg::from_usage("-o [opt] 'some opt'"))
|
||||
.get_matches_from_safe(vec!["", "-o", "-2"]);
|
||||
assert!(r.is_err());
|
||||
let m = r.unwrap_err();
|
||||
assert_eq!(m.kind, ErrorKind::UnknownArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_hyphen_with_flag_after() {
|
||||
let r = App::new("mvae")
|
||||
.arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
|
||||
.arg_from_usage("-f 'some flag'")
|
||||
.get_matches_from_safe(vec!["", "-o", "-2", "-f"]);
|
||||
assert!(r.is_ok());
|
||||
let m = r.unwrap();
|
||||
assert!(m.is_present("o"));
|
||||
assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2", "-f"]);
|
||||
assert!(!m.is_present("f"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_hyphen_with_flag_before() {
|
||||
let r = App::new("mvae")
|
||||
.arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
|
||||
.arg_from_usage("-f 'some flag'")
|
||||
.get_matches_from_safe(vec!["", "-f", "-o", "-2"]);
|
||||
assert!(r.is_ok());
|
||||
let m = r.unwrap();
|
||||
assert!(m.is_present("o"));
|
||||
assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2"]);
|
||||
assert!(m.is_present("f"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_hyphen_with_only_pos_follows() {
|
||||
let r = App::new("mvae")
|
||||
.arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
|
||||
.arg_from_usage("[arg] 'some arg'")
|
||||
.get_matches_from_safe(vec!["", "-o", "-2", "--", "val"]);
|
||||
assert!(r.is_ok());
|
||||
let m = r.unwrap();
|
||||
assert!(m.is_present("o"));
|
||||
assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2"]);
|
||||
assert_eq!(m.value_of("arg"), Some("val"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature="suggestions")]
|
||||
fn did_you_mean() {
|
||||
|
|
|
@ -174,6 +174,23 @@ fn arg_require_group_3() {
|
|||
|
||||
// REQUIRED_UNLESS
|
||||
|
||||
#[test]
|
||||
fn issue_753() {
|
||||
let m = App::new("test")
|
||||
.arg(Arg::from_usage("-l, --list 'List available interfaces (and stop there)'"))
|
||||
.arg(Arg::from_usage("-i, --iface=[INTERFACE] 'Ethernet interface for fetching NTP packets'")
|
||||
.required_unless("list"))
|
||||
.arg(Arg::from_usage("-f, --file=[TESTFILE] 'Fetch NTP packets from pcap file'")
|
||||
.conflicts_with("iface")
|
||||
.required_unless("list"))
|
||||
.arg(Arg::from_usage("-s, --server=[SERVER_IP] 'NTP server IP address'")
|
||||
.required_unless("list"))
|
||||
.arg(Arg::from_usage("-p, --port=[SERVER_PORT] 'NTP server port'")
|
||||
.default_value("123"))
|
||||
.get_matches_from_safe(vec!["test", "--list"]);
|
||||
assert!(m.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn required_unless() {
|
||||
let res = App::new("unlesstest")
|
||||
|
|
Loading…
Reference in a new issue