mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +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
|
- `chore` - Catch all or things that have to do with the build system, etc
|
||||||
- `examples` - Changes to existing example, or a new example
|
- `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
|
* 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)
|
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`)
|
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.)
|
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
|
* Remain backwards compatible when possible
|
||||||
- If backwards compatibility *must* be broken, use deprecation warnings if at all possible before removing legacy code
|
- If backwards compatibility *must* be broken, use deprecation warnings if at all possible before removing legacy code
|
||||||
- This does not apply for security concerns
|
- 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
|
* Parse arguments quickly
|
||||||
- Parsing of arguments shouldn't slow down usage of the main program
|
- 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)
|
- 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
|
cache: cargo
|
||||||
rust:
|
rust:
|
||||||
- nightly
|
- nightly
|
||||||
- nightly-2016-11-06
|
- nightly-2016-11-20
|
||||||
- beta
|
- beta
|
||||||
- stable
|
- stable
|
||||||
|
- 1.11.0
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- rust: nightly
|
- 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>
|
<a name="v2.18.0"></a>
|
||||||
## v2.18.0 (2016-11-05)
|
## 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 }
|
term_size = { version = "~0.2.0", optional = true }
|
||||||
libc = { version = "~0.2.9", optional = true }
|
libc = { version = "~0.2.9", optional = true }
|
||||||
yaml-rust = { version = "~0.3.2", 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]
|
[dev-dependencies]
|
||||||
regex = "~0.1.69"
|
regex = "~0.1.80"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["suggestions", "color", "wrap_help"]
|
default = ["suggestions", "color", "wrap_help"]
|
||||||
|
|
477
README.md
477
README.md
|
@ -31,8 +31,12 @@ Table of Contents
|
||||||
* [More Information](#more-information)
|
* [More Information](#more-information)
|
||||||
* [Video Tutorials](#video-tutorials)
|
* [Video Tutorials](#video-tutorials)
|
||||||
* [How to Contribute](#how-to-contribute)
|
* [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)
|
* [Goals](#goals)
|
||||||
|
* [Compatibility Policy](#compatibility-policy)
|
||||||
|
* [Minimum Version of Rust](#minimum-version-of-rust)
|
||||||
* [License](#license)
|
* [License](#license)
|
||||||
* [Recent Breaking Changes](#recent-breaking-changes)
|
* [Recent Breaking Changes](#recent-breaking-changes)
|
||||||
* [Deprecations](#deprecations)
|
* [Deprecations](#deprecations)
|
||||||
|
@ -41,288 +45,95 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
|
||||||
|
|
||||||
## What's New
|
## 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)
|
* **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>`)
|
* 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`
|
* 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
|
* 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:
|
* **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!
|
Here's a gif of them in action!
|
||||||
|
|
||||||
![zsh-comppletions](http://i.imgur.com/rwlMbAv.gif)
|
![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.
|
![screenshot](http://i.imgur.com/PAJzJJG.png)
|
||||||
* 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
|
|
||||||
|
|
||||||
An example of the optional colored help:
|
An example of the optional colored help:
|
||||||
|
|
||||||
![screenshot](http://i.imgur.com/7fs2h5j.png)
|
![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)
|
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).
|
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
|
```rust
|
||||||
// (Full example with detailed comments in examples/01b_quick_example.rs)
|
// (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
|
```rust
|
||||||
// (Full example with detailed comments in examples/01a_quick_example.rs)
|
// (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.
|
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
|
```yaml
|
||||||
name: myapp
|
name: myapp
|
||||||
|
@ -555,8 +366,9 @@ subcommands:
|
||||||
help: print debug information
|
help: print debug information
|
||||||
```
|
```
|
||||||
|
|
||||||
Since this feature is not compiled in by default we need to enable a feature flag in Cargo.toml:
|
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"` 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:
|
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
|
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
|
```sh
|
||||||
|
@ -645,11 +483,11 @@ fn main() {
|
||||||
|
|
||||||
## Usage
|
## 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
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2"
|
clap = "~2.19.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or get the latest changes from the master branch at github:
|
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
|
```toml
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "2"
|
version = "~2.19.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -685,7 +523,7 @@ You can also selectively enable only the features you'd like to include, by addi
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "2"
|
version = "~2.19.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
# Cherry-pick the features you'd like to use
|
# Cherry-pick the features you'd like to use
|
||||||
|
@ -695,6 +533,7 @@ features = [ "suggestions", "color" ]
|
||||||
#### Opt-in features
|
#### Opt-in features
|
||||||
|
|
||||||
* **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
|
* **"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
|
### 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.
|
* **Blue** Color: Dev dependency, only used while developing.
|
||||||
|
|
||||||
![clap dependencies](clap_dep_graph.png)
|
![clap dependencies](clap_dep_graph.png)
|
||||||
|
|
||||||
### More Information
|
### More Information
|
||||||
|
|
||||||
You can find complete documentation on the [docs.rs](https://docs.rs/clap/) for this project.
|
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.
|
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:
|
To test with all features both enabled and disabled, you can run theese commands:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ cargo test --no-default-features
|
$ 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
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
If you are debugging (or just trying to understand the code) you can enable the
|
For example, to test the code, as above simply run:
|
||||||
"debug" feature which will trace function calls and brances in some parts of the code.
|
|
||||||
|
```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
|
### 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
|
- 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
|
* `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
|
## 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.
|
||||||
|
|
21
justfile
21
justfile
|
@ -11,9 +11,26 @@
|
||||||
run-test TEST:
|
run-test TEST:
|
||||||
cargo test --test {{TEST}}
|
cargo test --test {{TEST}}
|
||||||
|
|
||||||
|
debug TEST:
|
||||||
|
cargo test --test {{TEST}} --features debug
|
||||||
|
|
||||||
run-tests:
|
run-tests:
|
||||||
cargo test --features "yaml unstable"
|
cargo test --features "yaml unstable"
|
||||||
|
|
||||||
lint:
|
@bench: nightly
|
||||||
|
cargo bench && just remove-nightly
|
||||||
|
|
||||||
|
nightly:
|
||||||
rustup override add 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
|
format_strings = false
|
||||||
chain_overflow_last = false
|
chain_overflow_last = false
|
||||||
same_line_if_else = true
|
same_line_if_else = true
|
||||||
|
fn_single_line = true
|
||||||
|
|
|
@ -21,14 +21,10 @@ use vec_map::VecMap;
|
||||||
|
|
||||||
#[cfg(not(feature = "wrap_help"))]
|
#[cfg(not(feature = "wrap_help"))]
|
||||||
mod term_size {
|
mod term_size {
|
||||||
pub fn dimensions() -> Option<(usize, usize)> {
|
pub fn dimensions() -> Option<(usize, usize)> { None }
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_width(s: &str) -> usize {
|
fn str_width(s: &str) -> usize { UnicodeWidthStr::width(s) }
|
||||||
UnicodeWidthStr::width(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
const TAB: &'static str = " ";
|
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
|
impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
|
||||||
where T: ArgWithDisplay<'b, 'c> + DispOrder
|
where T: ArgWithDisplay<'b, 'c> + DispOrder
|
||||||
{
|
{
|
||||||
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> { self }
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
|
fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> { x }
|
||||||
x
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'b, 'c> DispOrder for App<'b, 'c> {
|
impl<'b, 'c> DispOrder for App<'b, 'c> {
|
||||||
fn disp_ord(&self) -> usize {
|
fn disp_ord(&self) -> usize { 999 }
|
||||||
999
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! color {
|
macro_rules! color {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
macro_rules! remove_overriden {
|
macro_rules! remove_overriden {
|
||||||
(@remove $_self:ident, $v:ident, $a:ident.$ov:ident) => {
|
(@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);
|
vec_remove_all!($_self.$v, ora);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -11,11 +11,11 @@ macro_rules! remove_overriden {
|
||||||
};
|
};
|
||||||
($_self:ident, $name:expr) => {
|
($_self:ident, $name:expr) => {
|
||||||
debugln!("macro=remove_overriden!;");
|
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);
|
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);
|
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);
|
remove_overriden!(@arg $_self, p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -55,20 +55,7 @@ macro_rules! arg_post_processing {
|
||||||
if $matcher.contains(c) {
|
if $matcher.contains(c) {
|
||||||
sdebugln!("Yes");
|
sdebugln!("Yes");
|
||||||
// find who blacklisted us...
|
// find who blacklisted us...
|
||||||
$me.blacklist.push(&$arg.name);
|
$me.blacklist.push(&$arg.b.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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
sdebugln!("No");
|
sdebugln!("No");
|
||||||
}
|
}
|
||||||
|
@ -130,7 +117,7 @@ macro_rules! _handle_group_reqs{
|
||||||
macro_rules! validate_multiples {
|
macro_rules! validate_multiples {
|
||||||
($_self:ident, $a:ident, $m:ident) => {
|
($_self:ident, $a:ident, $m:ident) => {
|
||||||
debugln!("macro=validate_multiples!;");
|
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
|
// Not the first time, and we don't allow multiples
|
||||||
return Err(Error::unexpected_multiple_usage($a,
|
return Err(Error::unexpected_multiple_usage($a,
|
||||||
&*$_self.create_current_usage($m),
|
&*$_self.create_current_usage($m),
|
||||||
|
@ -155,16 +142,14 @@ macro_rules! parse_positional {
|
||||||
$pos_counter == $_self.positionals.len()) {
|
$pos_counter == $_self.positionals.len()) {
|
||||||
$_self.trailing_vals = true;
|
$_self.trailing_vals = true;
|
||||||
}
|
}
|
||||||
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
|
let _ = try!($_self.add_val_to_arg($p, &$arg_os, $matcher));
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
$matcher.inc_occurrence_of($p.name);
|
$matcher.inc_occurrence_of($p.b.name);
|
||||||
let _ = $_self.groups_for_arg($p.name)
|
let _ = $_self.groups_for_arg($p.b.name)
|
||||||
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
|
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
|
||||||
arg_post_processing!($_self, $p, $matcher);
|
arg_post_processing!($_self, $p, $matcher);
|
||||||
// Only increment the positional counter if it doesn't allow multiples
|
// 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;
|
$pos_counter += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -174,24 +159,24 @@ macro_rules! find_from {
|
||||||
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
|
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
for k in $matcher.arg_names() {
|
for k in $matcher.arg_names() {
|
||||||
if let Some(f) = $_self.find_flag(k) {
|
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
|
||||||
if let Some(ref v) = f.$from {
|
if let Some(ref v) = f.$from() {
|
||||||
if v.contains($arg_name) {
|
if v.contains($arg_name) {
|
||||||
ret = Some(f.to_string());
|
ret = Some(f.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(o) = $_self.find_option(k) {
|
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
|
||||||
if let Some(ref v) = o.$from {
|
if let Some(ref v) = o.$from() {
|
||||||
if v.contains(&$arg_name) {
|
if v.contains(&$arg_name) {
|
||||||
ret = Some(o.to_string());
|
ret = Some(o.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(pos) = $_self.find_positional(k) {
|
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
|
||||||
if let Some(ref v) = pos.$from {
|
if let Some(ref v) = pos.$from() {
|
||||||
if v.contains($arg_name) {
|
if v.contains($arg_name) {
|
||||||
ret = Some(pos.name.to_owned());
|
ret = Some(pos.b.name.to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,24 +189,24 @@ macro_rules! find_name_from {
|
||||||
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
|
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
for k in $matcher.arg_names() {
|
for k in $matcher.arg_names() {
|
||||||
if let Some(f) = $_self.find_flag(k) {
|
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
|
||||||
if let Some(ref v) = f.$from {
|
if let Some(ref v) = f.$from() {
|
||||||
if v.contains($arg_name) {
|
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(o) = find_by_name!($_self, &k, opts, iter) {
|
||||||
if let Some(ref v) = o.$from {
|
if let Some(ref v) = o.$from() {
|
||||||
if v.contains(&$arg_name) {
|
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(pos) = find_by_name!($_self, &k, positionals, values) {
|
||||||
if let Some(ref v) = pos.$from {
|
if let Some(ref v) = pos.$from() {
|
||||||
if v.contains($arg_name) {
|
if v.contains($arg_name) {
|
||||||
ret = Some(pos.name);
|
ret = Some(pos.b.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,3 +214,29 @@ macro_rules! find_name_from {
|
||||||
ret
|
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> {
|
impl<'b> AppMeta<'b> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Default::default() }
|
||||||
Default::default()
|
pub fn with_name(s: String) -> Self { AppMeta { name: s, ..Default::default() } }
|
||||||
}
|
|
||||||
pub fn with_name(s: String) -> Self {
|
|
||||||
AppMeta { name: s, ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b> Clone for AppMeta<'b> {
|
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
|
// Internal
|
||||||
use app::help::Help;
|
use app::help::Help;
|
||||||
use app::parser::Parser;
|
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::Error;
|
||||||
use errors::Result as ClapResult;
|
use errors::Result as ClapResult;
|
||||||
pub use self::settings::AppSettings;
|
pub use self::settings::AppSettings;
|
||||||
|
@ -79,19 +79,13 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
/// let prog = App::new("My Program")
|
/// let prog = App::new("My Program")
|
||||||
/// # ;
|
/// # ;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new<S: Into<String>>(n: S) -> Self {
|
pub fn new<S: Into<String>>(n: S) -> Self { App { p: Parser::with_name(n.into()) } }
|
||||||
App { p: Parser::with_name(n.into()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the name of the app
|
/// Get the name of the app
|
||||||
pub fn get_name(&self) -> &str {
|
pub fn get_name(&self) -> &str { &self.p.meta.name }
|
||||||
&self.p.meta.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the name of the binary
|
/// Get the name of the binary
|
||||||
pub fn get_bin_name(&self) -> Option<&str> {
|
pub fn get_bin_name(&self) -> Option<&str> { self.p.meta.bin_name.as_ref().map(|s| s.as_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!`]
|
/// 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.
|
/// 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
|
/// [`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
|
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> {
|
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) }
|
||||||
App::from(yaml)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a string of author(s) that will be displayed to the user when they
|
/// Sets a string of author(s) that will be displayed to the user when they
|
||||||
/// request the help information with `--help` or `-h`.
|
/// request the help information with `--help` or `-h`.
|
||||||
|
@ -1193,9 +1185,7 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
/// .get_matches();
|
/// .get_matches();
|
||||||
/// ```
|
/// ```
|
||||||
/// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html
|
/// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html
|
||||||
pub fn get_matches(self) -> ArgMatches<'a> {
|
pub fn get_matches(self) -> ArgMatches<'a> { self.get_matches_from(&mut env::args_os()) }
|
||||||
self.get_matches_from(&mut env::args_os())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Starts the parsing process. This method will return a [`clap::Result`] type instead of exiting
|
/// 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`]
|
/// 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> {
|
impl<'a, 'b> Clone for App<'a, 'b> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self { App { p: self.p.clone() } }
|
||||||
App { p: self.p.clone() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
||||||
fn name(&self) -> &'n str {
|
fn name(&self) -> &'n str {
|
||||||
unreachable!("App struct does not support AnyArg::name, this is a bug!")
|
unreachable!("App struct does not support AnyArg::name, this is a bug!")
|
||||||
}
|
}
|
||||||
fn overrides(&self) -> Option<&[&'e str]> {
|
fn id(&self) -> usize { self.p.id }
|
||||||
None
|
fn kind(&self) -> ArgKind { ArgKind::Subcmd }
|
||||||
}
|
fn overrides(&self) -> Option<&[&'e str]> { None }
|
||||||
fn requires(&self) -> Option<&[&'e str]> {
|
fn requires(&self) -> Option<&[&'e str]> { None }
|
||||||
None
|
fn blacklist(&self) -> Option<&[&'e str]> { None }
|
||||||
}
|
fn required_unless(&self) -> Option<&[&'e str]> { None }
|
||||||
fn blacklist(&self) -> Option<&[&'e str]> {
|
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
||||||
None
|
fn is_set(&self, _: ArgSettings) -> bool { false }
|
||||||
}
|
|
||||||
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) {
|
fn set(&mut self, _: ArgSettings) {
|
||||||
unreachable!("App struct does not support AnyArg::set, this is a bug!")
|
unreachable!("App struct does not support AnyArg::set, this is a bug!")
|
||||||
}
|
}
|
||||||
fn has_switch(&self) -> bool {
|
fn has_switch(&self) -> bool { false }
|
||||||
false
|
fn max_vals(&self) -> Option<u64> { None }
|
||||||
}
|
fn num_vals(&self) -> Option<u64> { None }
|
||||||
fn max_vals(&self) -> Option<u64> {
|
fn possible_vals(&self) -> Option<&[&'e str]> { None }
|
||||||
None
|
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
|
||||||
}
|
fn min_vals(&self) -> Option<u64> { None }
|
||||||
fn num_vals(&self) -> Option<u64> {
|
fn short(&self) -> Option<char> { None }
|
||||||
None
|
fn long(&self) -> Option<&'e str> { None }
|
||||||
}
|
fn val_delim(&self) -> Option<char> { None }
|
||||||
fn possible_vals(&self) -> Option<&[&'e str]> {
|
fn takes_value(&self) -> bool { true }
|
||||||
None
|
fn help(&self) -> Option<&'e str> { self.p.meta.about }
|
||||||
}
|
fn default_val(&self) -> Option<&'n str> { None }
|
||||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
fn longest_filter(&self) -> bool { true }
|
||||||
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>> {
|
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||||
if let Some(ref aliases) = self.p.meta.aliases {
|
if let Some(ref aliases) = self.p.meta.aliases {
|
||||||
let vis_aliases: Vec<_> =
|
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> {
|
impl<'n, 'e> fmt::Display for App<'n, 'e> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.p.meta.name) }
|
||||||
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);
|
pub struct AppFlags(Flags);
|
||||||
|
|
||||||
impl Clone for AppFlags {
|
impl Clone for AppFlags {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self { AppFlags(self.0) }
|
||||||
AppFlags(self.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppFlags {
|
impl Default for AppFlags {
|
||||||
|
@ -53,9 +51,7 @@ impl Default for AppFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppFlags {
|
impl AppFlags {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { AppFlags::default() }
|
||||||
AppFlags::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_settings! { AppSettings,
|
impl_settings! { AppSettings,
|
||||||
ArgRequiredElseHelp => A_REQUIRED_ELSE_HELP,
|
ArgRequiredElseHelp => A_REQUIRED_ELSE_HELP,
|
||||||
|
@ -667,7 +663,6 @@ pub enum AppSettings {
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
LowIndexMultiplePositional,
|
LowIndexMultiplePositional,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for AppSettings {
|
impl FromStr for AppSettings {
|
||||||
|
|
|
@ -7,10 +7,12 @@ use vec_map::VecMap;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
use args::settings::ArgSettings;
|
use args::settings::ArgSettings;
|
||||||
|
use args::ArgKind;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait AnyArg<'n, 'e>: std_fmt::Display {
|
pub trait AnyArg<'n, 'e>: std_fmt::Display {
|
||||||
fn name(&self) -> &'n str;
|
fn name(&self) -> &'n str;
|
||||||
|
fn id(&self) -> usize;
|
||||||
fn overrides(&self) -> Option<&[&'e str]>;
|
fn overrides(&self) -> Option<&[&'e str]>;
|
||||||
fn aliases(&self) -> Option<Vec<&'e str>>;
|
fn aliases(&self) -> Option<Vec<&'e str>>;
|
||||||
fn requires(&self) -> Option<&[&'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 help(&self) -> Option<&'e str>;
|
||||||
fn default_val(&self) -> Option<&'n str>;
|
fn default_val(&self) -> Option<&'n str>;
|
||||||
fn longest_filter(&self) -> bool;
|
fn longest_filter(&self) -> bool;
|
||||||
|
fn kind(&self) -> ArgKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DispOrder {
|
pub trait DispOrder {
|
||||||
|
|
|
@ -54,7 +54,7 @@ pub struct Arg<'a, 'b>
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub requires: Option<Vec<&'a str>>,
|
pub requires: Option<Vec<&'a str>>,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub group: Option<Vec<&'a str>>,
|
pub groups: Option<Vec<&'a str>>,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub val_names: Option<VecMap<&'b str>>,
|
pub val_names: Option<VecMap<&'b str>>,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -91,7 +91,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> {
|
||||||
blacklist: None,
|
blacklist: None,
|
||||||
possible_vals: None,
|
possible_vals: None,
|
||||||
requires: None,
|
requires: None,
|
||||||
group: None,
|
groups: None,
|
||||||
val_names: None,
|
val_names: None,
|
||||||
num_vals: None,
|
num_vals: None,
|
||||||
max_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::takes_value(true)`]: ./struct.Arg.html#method.takes_value
|
||||||
/// [`Arg`]: ./struct.Arg.html
|
/// [`Arg`]: ./struct.Arg.html
|
||||||
pub fn with_name(n: &'a str) -> Self {
|
pub fn with_name(n: &'a str) -> Self { Arg { name: n, ..Default::default() } }
|
||||||
Arg { name: n, ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new instance of [`Arg`] from a .yml (YAML) file.
|
/// 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),
|
"min_values" => yaml_to_u64!(a, v, min_values),
|
||||||
"value_name" => yaml_to_str!(a, v, value_name),
|
"value_name" => yaml_to_str!(a, v, value_name),
|
||||||
"use_delimiter" => yaml_to_bool!(a, v, use_delimiter),
|
"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),
|
"require_delimiter" => yaml_to_bool!(a, v, require_delimiter),
|
||||||
"value_delimiter" => yaml_to_str!(a, v, value_delimiter),
|
"value_delimiter" => yaml_to_str!(a, v, value_delimiter),
|
||||||
"required_unless" => yaml_to_str!(a, v, required_unless),
|
"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
|
/// Sets an arg that override this arg's required setting. (i.e. this arg will be required
|
||||||
/// unless this other argument is present).
|
/// unless this other argument is present).
|
||||||
///
|
///
|
||||||
|
@ -1725,7 +1780,7 @@ impl<'a, 'b> Arg<'a, 'b> {
|
||||||
if let Some(ref mut vec) = self.requires {
|
if let Some(ref mut vec) = self.requires {
|
||||||
vec.push(name);
|
vec.push(name);
|
||||||
} else {
|
} else {
|
||||||
self.group = Some(vec![name]);
|
self.groups = Some(vec![name]);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -1760,12 +1815,12 @@ impl<'a, 'b> Arg<'a, 'b> {
|
||||||
/// ```
|
/// ```
|
||||||
/// [`ArgGroup`]: ./struct.ArgGroup.html
|
/// [`ArgGroup`]: ./struct.ArgGroup.html
|
||||||
pub fn groups(mut self, names: &[&'a str]) -> Self {
|
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 {
|
for s in names {
|
||||||
vec.push(s);
|
vec.push(s);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.group = Some(names.into_iter().map(|s| *s).collect::<Vec<_>>());
|
self.groups = Some(names.into_iter().map(|s| *s).collect::<Vec<_>>());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -1815,7 +1870,9 @@ impl<'a, 'b> Arg<'a, 'b> {
|
||||||
/// message displayed to the user.
|
/// message displayed to the user.
|
||||||
///
|
///
|
||||||
/// **NOTE:** The error message does *not* need to contain the `error:` portion, only the
|
/// **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
|
/// **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
|
/// 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
|
/// Checks if one of the [`ArgSettings`] settings is set for the argument
|
||||||
/// [`ArgSettings`]: ./enum.ArgSettings.html
|
/// [`ArgSettings`]: ./enum.ArgSettings.html
|
||||||
pub fn is_set(&self, s: ArgSettings) -> bool {
|
pub fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
|
||||||
self.settings.is_set(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets one of the [`ArgSettings`] settings for the argument
|
/// Sets one of the [`ArgSettings`] settings for the argument
|
||||||
/// [`ArgSettings`]: ./enum.ArgSettings.html
|
/// [`ArgSettings`]: ./enum.ArgSettings.html
|
||||||
|
@ -2484,14 +2539,10 @@ impl<'a, 'b> Arg<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn setb(&mut self, s: ArgSettings) {
|
pub fn setb(&mut self, s: ArgSettings) { self.settings.set(s); }
|
||||||
self.settings.set(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn unsetb(&mut self, s: ArgSettings) {
|
pub fn unsetb(&mut self, s: ArgSettings) { self.settings.unset(s); }
|
||||||
self.settings.unset(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> {
|
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,
|
min_vals: a.min_vals,
|
||||||
max_vals: a.max_vals,
|
max_vals: a.max_vals,
|
||||||
val_names: a.val_names.clone(),
|
val_names: a.val_names.clone(),
|
||||||
group: a.group.clone(),
|
groups: a.groups.clone(),
|
||||||
validator: a.validator.clone(),
|
validator: a.validator.clone(),
|
||||||
overrides: a.overrides.clone(),
|
overrides: a.overrides.clone(),
|
||||||
settings: a.settings,
|
settings: a.settings,
|
||||||
|
@ -2538,7 +2589,7 @@ impl<'a, 'b> Clone for Arg<'a, 'b> {
|
||||||
min_vals: self.min_vals,
|
min_vals: self.min_vals,
|
||||||
max_vals: self.max_vals,
|
max_vals: self.max_vals,
|
||||||
val_names: self.val_names.clone(),
|
val_names: self.val_names.clone(),
|
||||||
group: self.group.clone(),
|
groups: self.groups.clone(),
|
||||||
validator: self.validator.clone(),
|
validator: self.validator.clone(),
|
||||||
overrides: self.overrides.clone(),
|
overrides: self.overrides.clone(),
|
||||||
settings: self.settings,
|
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
|
// Internal
|
||||||
use Arg;
|
use Arg;
|
||||||
use args::{AnyArg, DispOrder};
|
use args::{ArgSettings, ArgKind, Base, Switched, AnyArg, DispOrder};
|
||||||
use args::settings::{ArgFlags, ArgSettings};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct FlagBuilder<'n, 'e> {
|
pub struct FlagBuilder<'n, 'e>
|
||||||
pub name: &'n str,
|
where 'n: 'e
|
||||||
pub long: Option<&'e str>,
|
{
|
||||||
pub aliases: Option<Vec<(&'e str, bool)>>,
|
pub b: Base<'n, 'e>,
|
||||||
pub help: Option<&'e str>,
|
pub s: Switched<'e>,
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> FlagBuilder<'n, 'e> {
|
impl<'n, 'e> FlagBuilder<'n, 'e> {
|
||||||
pub fn new(name: &'n str) -> Self {
|
pub fn new(name: &'n str) -> Self { FlagBuilder { b: Base::new(name), ..Default::default() } }
|
||||||
FlagBuilder { name: name, ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> {
|
impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> {
|
||||||
fn from(a: &'z Arg<'a, 'b>) -> Self {
|
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
|
// No need to check for index() or takes_value() as that is handled above
|
||||||
|
|
||||||
FlagBuilder {
|
FlagBuilder {
|
||||||
name: a.name,
|
b: Base::from(a),
|
||||||
short: a.short,
|
s: Switched::from(a),
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
|
impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
if let Some(l) = self.long {
|
if let Some(l) = self.s.long {
|
||||||
try!(write!(f, "--{}", l));
|
try!(write!(f, "--{}", l));
|
||||||
} else {
|
} else {
|
||||||
try!(write!(f, "-{}", self.short.unwrap()));
|
try!(write!(f, "-{}", self.s.short.unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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> {
|
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
||||||
fn name(&self) -> &'n str {
|
fn name(&self) -> &'n str { self.b.name }
|
||||||
self.name
|
fn id(&self) -> usize { self.b.id }
|
||||||
}
|
fn kind(&self) -> ArgKind { ArgKind::Flag }
|
||||||
fn overrides(&self) -> Option<&[&'e str]> {
|
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||||
self.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 requires(&self) -> Option<&[&'e str]> {
|
fn required_unless(&self) -> Option<&[&'e str]> { None }
|
||||||
self.requires.as_ref().map(|o| &o[..])
|
fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) }
|
||||||
}
|
fn has_switch(&self) -> bool { true }
|
||||||
fn blacklist(&self) -> Option<&[&'e str]> {
|
fn takes_value(&self) -> bool { false }
|
||||||
self.blacklist.as_ref().map(|o| &o[..])
|
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
|
||||||
}
|
fn max_vals(&self) -> Option<u64> { None }
|
||||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
||||||
None
|
fn num_vals(&self) -> Option<u64> { None }
|
||||||
}
|
fn possible_vals(&self) -> Option<&[&'e str]> { None }
|
||||||
fn is_set(&self, s: ArgSettings) -> bool {
|
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
|
||||||
self.settings.is_set(s)
|
fn min_vals(&self) -> Option<u64> { None }
|
||||||
}
|
fn short(&self) -> Option<char> { self.s.short }
|
||||||
fn has_switch(&self) -> bool {
|
fn long(&self) -> Option<&'e str> { self.s.long }
|
||||||
true
|
fn val_delim(&self) -> Option<char> { None }
|
||||||
}
|
fn help(&self) -> Option<&'e str> { self.b.help }
|
||||||
fn takes_value(&self) -> bool {
|
fn default_val(&self) -> Option<&'n str> { None }
|
||||||
false
|
fn longest_filter(&self) -> bool { self.s.long.is_some() }
|
||||||
}
|
|
||||||
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 aliases(&self) -> Option<Vec<&'e str>> {
|
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||||
if let Some(ref aliases) = self.aliases {
|
if let Some(ref aliases) = self.s.aliases {
|
||||||
let vis_aliases: Vec<_> =
|
let vis_aliases: Vec<_> = aliases.iter()
|
||||||
aliases.iter()
|
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
||||||
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
.collect();
|
||||||
.collect();
|
|
||||||
if vis_aliases.is_empty() {
|
if vis_aliases.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -196,9 +88,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> {
|
impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> {
|
||||||
fn disp_ord(&self) -> usize {
|
fn disp_ord(&self) -> usize { self.s.disp_ord }
|
||||||
self.disp_ord
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -209,13 +99,13 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn flagbuilder_display() {
|
fn flagbuilder_display() {
|
||||||
let mut f = FlagBuilder::new("flg");
|
let mut f = FlagBuilder::new("flg");
|
||||||
f.settings.set(ArgSettings::Multiple);
|
f.b.settings.set(ArgSettings::Multiple);
|
||||||
f.long = Some("flag");
|
f.s.long = Some("flag");
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", f), "--flag");
|
assert_eq!(&*format!("{}", f), "--flag");
|
||||||
|
|
||||||
let mut f2 = FlagBuilder::new("flg");
|
let mut f2 = FlagBuilder::new("flg");
|
||||||
f2.short = Some('f');
|
f2.s.short = Some('f');
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", f2), "-f");
|
assert_eq!(&*format!("{}", f2), "-f");
|
||||||
}
|
}
|
||||||
|
@ -223,8 +113,8 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn flagbuilder_display_single_alias() {
|
fn flagbuilder_display_single_alias() {
|
||||||
let mut f = FlagBuilder::new("flg");
|
let mut f = FlagBuilder::new("flg");
|
||||||
f.long = Some("flag");
|
f.s.long = Some("flag");
|
||||||
f.aliases = Some(vec![("als", true)]);
|
f.s.aliases = Some(vec![("als", true)]);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", f), "--flag");
|
assert_eq!(&*format!("{}", f), "--flag");
|
||||||
}
|
}
|
||||||
|
@ -232,13 +122,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn flagbuilder_display_multiple_aliases() {
|
fn flagbuilder_display_multiple_aliases() {
|
||||||
let mut f = FlagBuilder::new("flg");
|
let mut f = FlagBuilder::new("flg");
|
||||||
f.short = Some('f');
|
f.s.short = Some('f');
|
||||||
f.aliases = Some(vec![
|
f.s.aliases =
|
||||||
("alias_not_visible", false),
|
Some(vec![("alias_not_visible", false), ("f2", true), ("f3", true), ("f4", true)]);
|
||||||
("f2", true),
|
|
||||||
("f3", true),
|
|
||||||
("f4", true)
|
|
||||||
]);
|
|
||||||
assert_eq!(&*format!("{}", f), "-f");
|
assert_eq!(&*format!("{}", f), "-f");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
pub use self::flag::FlagBuilder;
|
pub use self::flag::FlagBuilder;
|
||||||
pub use self::option::OptBuilder;
|
pub use self::option::OptBuilder;
|
||||||
pub use self::positional::PosBuilder;
|
pub use self::positional::PosBuilder;
|
||||||
|
pub use self::base::Base;
|
||||||
|
pub use self::switched::Switched;
|
||||||
|
pub use self::valued::Valued;
|
||||||
|
|
||||||
mod flag;
|
mod flag;
|
||||||
mod positional;
|
mod positional;
|
||||||
mod option;
|
mod option;
|
||||||
|
mod base;
|
||||||
|
mod valued;
|
||||||
|
mod switched;
|
||||||
|
|
|
@ -7,113 +7,33 @@ use std::result::Result as StdResult;
|
||||||
use vec_map::VecMap;
|
use vec_map::VecMap;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
use args::{AnyArg, Arg, DispOrder};
|
use args::{ArgSettings, ArgKind, AnyArg, Base, Switched, Valued, Arg, DispOrder};
|
||||||
use args::settings::{ArgFlags, ArgSettings};
|
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct OptBuilder<'n, 'e> {
|
#[derive(Default, Clone)]
|
||||||
pub name: &'n str,
|
pub struct OptBuilder<'n, 'e>
|
||||||
pub short: Option<char>,
|
where 'n: 'e
|
||||||
pub long: Option<&'e str>,
|
{
|
||||||
pub aliases: Option<Vec<(&'e str, bool)>>,
|
pub b: Base<'n, 'e>,
|
||||||
pub help: Option<&'e str>,
|
pub s: Switched<'e>,
|
||||||
pub blacklist: Option<Vec<&'e str>>,
|
pub v: Valued<'n, 'e>,
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> OptBuilder<'n, 'e> {
|
impl<'n, 'e> OptBuilder<'n, 'e> {
|
||||||
pub fn new(name: &'n str) -> Self {
|
pub fn new(name: &'n str) -> Self { OptBuilder { b: Base::new(name), ..Default::default() } }
|
||||||
OptBuilder { name: name, ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_arg(a: &Arg<'n, 'e>, reqs: &mut Vec<&'e str>) -> Self {
|
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
|
// No need to check for .index() as that is handled above
|
||||||
let mut ob = OptBuilder {
|
let ob = OptBuilder {
|
||||||
name: a.name,
|
b: Base::from(a),
|
||||||
short: a.short,
|
s: Switched::from(a),
|
||||||
long: a.long,
|
v: Valued::from(a),
|
||||||
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()
|
|
||||||
};
|
};
|
||||||
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 the arg is required, add all it's requirements to master required list
|
||||||
if a.is_set(ArgSettings::Required) {
|
if a.is_set(ArgSettings::Required) {
|
||||||
if let Some(ref areqs) = a.requires {
|
if let Some(ref areqs) = a.requires {
|
||||||
for r in areqs {
|
reqs.extend_from_slice(areqs);
|
||||||
reqs.push(*r);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ob
|
ob
|
||||||
|
@ -124,14 +44,14 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
debugln!("fn=fmt");
|
debugln!("fn=fmt");
|
||||||
// Write the name such --long or -l
|
// Write the name such --long or -l
|
||||||
if let Some(l) = self.long {
|
if let Some(l) = self.s.long {
|
||||||
try!(write!(f, "--{} ", l));
|
try!(write!(f, "--{} ", l));
|
||||||
} else {
|
} else {
|
||||||
try!(write!(f, "-{} ", self.short.unwrap()));
|
try!(write!(f, "-{} ", self.s.short.unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the values such as <name1> <name2>
|
// 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();
|
let mut it = vec.iter().peekable();
|
||||||
while let Some((_, val)) = it.next() {
|
while let Some((_, val)) = it.next() {
|
||||||
try!(write!(f, "<{}>", val));
|
try!(write!(f, "<{}>", val));
|
||||||
|
@ -143,10 +63,10 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
||||||
if self.is_set(ArgSettings::Multiple) && num == 1 {
|
if self.is_set(ArgSettings::Multiple) && num == 1 {
|
||||||
try!(write!(f, "..."));
|
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();
|
let mut it = (0..num).peekable();
|
||||||
while let Some(_) = it.next() {
|
while let Some(_) = it.next() {
|
||||||
try!(write!(f, "<{}>", self.name));
|
try!(write!(f, "<{}>", self.b.name));
|
||||||
if it.peek().is_some() {
|
if it.peek().is_some() {
|
||||||
try!(write!(f, " "));
|
try!(write!(f, " "));
|
||||||
}
|
}
|
||||||
|
@ -154,7 +74,7 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
||||||
} else {
|
} else {
|
||||||
try!(write!(f,
|
try!(write!(f,
|
||||||
"<{}>{}",
|
"<{}>{}",
|
||||||
self.name,
|
self.b.name,
|
||||||
if self.is_set(ArgSettings::Multiple) {
|
if self.is_set(ArgSettings::Multiple) {
|
||||||
"..."
|
"..."
|
||||||
} else {
|
} 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> {
|
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
||||||
fn name(&self) -> &'n str {
|
fn name(&self) -> &'n str { self.b.name }
|
||||||
self.name
|
fn id(&self) -> usize { self.b.id }
|
||||||
}
|
fn kind(&self) -> ArgKind { ArgKind::Opt }
|
||||||
fn overrides(&self) -> Option<&[&'e str]> {
|
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||||
self.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 requires(&self) -> Option<&[&'e str]> {
|
fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) }
|
||||||
self.requires.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 blacklist(&self) -> Option<&[&'e str]> {
|
fn has_switch(&self) -> bool { true }
|
||||||
self.blacklist.as_ref().map(|o| &o[..])
|
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
|
||||||
}
|
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
|
||||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
fn num_vals(&self) -> Option<u64> { self.v.num_vals }
|
||||||
self.r_unless.as_ref().map(|o| &o[..])
|
fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.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 validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||||
self.validator.as_ref()
|
self.v.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
|
|
||||||
}
|
}
|
||||||
|
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>> {
|
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||||
if let Some(ref aliases) = self.aliases {
|
if let Some(ref aliases) = self.s.aliases {
|
||||||
let vis_aliases: Vec<_> =
|
let vis_aliases: Vec<_> = aliases.iter()
|
||||||
aliases.iter()
|
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
||||||
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
.collect();
|
||||||
.collect();
|
|
||||||
if vis_aliases.is_empty() {
|
if vis_aliases.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -275,9 +129,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> {
|
impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> {
|
||||||
fn disp_ord(&self) -> usize {
|
fn disp_ord(&self) -> usize { self.s.disp_ord }
|
||||||
self.disp_ord
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -289,8 +141,8 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn optbuilder_display1() {
|
fn optbuilder_display1() {
|
||||||
let mut o = OptBuilder::new("opt");
|
let mut o = OptBuilder::new("opt");
|
||||||
o.long = Some("option");
|
o.s.long = Some("option");
|
||||||
o.settings.set(ArgSettings::Multiple);
|
o.b.settings.set(ArgSettings::Multiple);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", o), "--option <opt>...");
|
assert_eq!(&*format!("{}", o), "--option <opt>...");
|
||||||
}
|
}
|
||||||
|
@ -302,8 +154,8 @@ mod test {
|
||||||
v_names.insert(1, "name");
|
v_names.insert(1, "name");
|
||||||
|
|
||||||
let mut o2 = OptBuilder::new("opt");
|
let mut o2 = OptBuilder::new("opt");
|
||||||
o2.short = Some('o');
|
o2.s.short = Some('o');
|
||||||
o2.val_names = Some(v_names);
|
o2.v.val_names = Some(v_names);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||||
}
|
}
|
||||||
|
@ -315,9 +167,9 @@ mod test {
|
||||||
v_names.insert(1, "name");
|
v_names.insert(1, "name");
|
||||||
|
|
||||||
let mut o2 = OptBuilder::new("opt");
|
let mut o2 = OptBuilder::new("opt");
|
||||||
o2.short = Some('o');
|
o2.s.short = Some('o');
|
||||||
o2.val_names = Some(v_names);
|
o2.v.val_names = Some(v_names);
|
||||||
o2.settings.set(ArgSettings::Multiple);
|
o2.b.settings.set(ArgSettings::Multiple);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||||
}
|
}
|
||||||
|
@ -325,8 +177,8 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn optbuilder_display_single_alias() {
|
fn optbuilder_display_single_alias() {
|
||||||
let mut o = OptBuilder::new("opt");
|
let mut o = OptBuilder::new("opt");
|
||||||
o.long = Some("option");
|
o.s.long = Some("option");
|
||||||
o.aliases = Some(vec![("als", true)]);
|
o.s.aliases = Some(vec![("als", true)]);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", o), "--option <opt>");
|
assert_eq!(&*format!("{}", o), "--option <opt>");
|
||||||
}
|
}
|
||||||
|
@ -334,13 +186,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn optbuilder_display_multiple_aliases() {
|
fn optbuilder_display_multiple_aliases() {
|
||||||
let mut o = OptBuilder::new("opt");
|
let mut o = OptBuilder::new("opt");
|
||||||
o.long = Some("option");
|
o.s.long = Some("option");
|
||||||
o.aliases = Some(vec![
|
o.s.aliases =
|
||||||
("als_not_visible", false),
|
Some(vec![("als_not_visible", false), ("als2", true), ("als3", true), ("als4", true)]);
|
||||||
("als2", true),
|
|
||||||
("als3", true),
|
|
||||||
("als4", true)
|
|
||||||
]);
|
|
||||||
assert_eq!(&*format!("{}", o), "--option <opt>");
|
assert_eq!(&*format!("{}", o), "--option <opt>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,112 +9,52 @@ use vec_map::VecMap;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
use Arg;
|
use Arg;
|
||||||
use args::{AnyArg, DispOrder};
|
use args::{ArgSettings, Base, Valued, ArgKind, AnyArg, DispOrder};
|
||||||
use args::settings::{ArgFlags, ArgSettings};
|
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct PosBuilder<'n, 'e> {
|
#[derive(Clone, Default)]
|
||||||
pub name: &'n str,
|
pub struct PosBuilder<'n, 'e>
|
||||||
pub help: Option<&'e str>,
|
where 'n: 'e
|
||||||
pub requires: Option<Vec<&'e str>>,
|
{
|
||||||
pub blacklist: Option<Vec<&'e str>>,
|
pub b: Base<'n, 'e>,
|
||||||
pub possible_vals: Option<Vec<&'e str>>,
|
pub v: Valued<'n, 'e>,
|
||||||
pub index: u64,
|
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> {
|
impl<'n, 'e> PosBuilder<'n, 'e> {
|
||||||
pub fn new(name: &'n str, idx: u64) -> Self {
|
pub fn new(name: &'n str, idx: u64) -> Self {
|
||||||
PosBuilder {
|
PosBuilder {
|
||||||
name: name,
|
b: Base::new(name),
|
||||||
index: idx,
|
index: idx,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_arg(a: &Arg<'n, 'e>, idx: u64, reqs: &mut Vec<&'e str>) -> Self {
|
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
|
// Create the Positional Argument Builder with each HashSet = None to only
|
||||||
// allocate
|
// allocate
|
||||||
// those that require it
|
// those that require it
|
||||||
let mut pb = PosBuilder {
|
let mut pb = PosBuilder {
|
||||||
name: a.name,
|
b: Base::from(a),
|
||||||
|
v: Valued::from(a),
|
||||||
index: idx,
|
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() ||
|
if a.max_vals.is_some() || a.min_vals.is_some() ||
|
||||||
(a.num_vals.is_some() && a.num_vals.unwrap() > 1) {
|
(a.num_vals.is_some() && a.num_vals.unwrap() > 1) {
|
||||||
pb.settings.set(ArgSettings::Multiple);
|
pb.b.settings.set(ArgSettings::Multiple);
|
||||||
}
|
|
||||||
if let Some(ref p) = a.validator {
|
|
||||||
pb.validator = Some(p.clone());
|
|
||||||
}
|
}
|
||||||
// If the arg is required, add all it's requirements to master required list
|
// If the arg is required, add all it's requirements to master required list
|
||||||
if a.is_set(ArgSettings::Required) {
|
if a.is_set(ArgSettings::Required) {
|
||||||
if let Some(ref areqs) = a.requires {
|
if let Some(ref areqs) = a.requires {
|
||||||
for r in areqs {
|
reqs.extend_from_slice(areqs);
|
||||||
reqs.push(*r);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pb
|
pb
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiple_str(&self) -> &str {
|
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 {
|
} else {
|
||||||
""
|
""
|
||||||
|
@ -122,20 +62,20 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name_no_brackets(&self) -> Cow<str> {
|
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()
|
Cow::Owned(names.values()
|
||||||
.map(|n| format!("<{}>", n))
|
.map(|n| format!("<{}>", n))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" "))
|
.join(" "))
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed(self.name)
|
Cow::Borrowed(self.b.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
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,
|
try!(write!(f,
|
||||||
"{}",
|
"{}",
|
||||||
names.values()
|
names.values()
|
||||||
|
@ -143,9 +83,9 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")));
|
.join(" ")));
|
||||||
} else {
|
} 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, "..."));
|
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> {
|
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
|
||||||
fn name(&self) -> &'n str {
|
fn name(&self) -> &'n str { self.b.name }
|
||||||
self.name
|
fn id(&self) -> usize { self.b.id }
|
||||||
}
|
fn kind(&self) -> ArgKind { ArgKind::Pos }
|
||||||
fn overrides(&self) -> Option<&[&'e str]> {
|
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
|
||||||
self.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 requires(&self) -> Option<&[&'e str]> {
|
fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) }
|
||||||
self.requires.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 blacklist(&self) -> Option<&[&'e str]> {
|
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
|
||||||
self.blacklist.as_ref().map(|o| &o[..])
|
fn has_switch(&self) -> bool { false }
|
||||||
}
|
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
|
||||||
fn required_unless(&self) -> Option<&[&'e str]> {
|
fn num_vals(&self) -> Option<u64> { self.v.num_vals }
|
||||||
self.r_unless.as_ref().map(|o| &o[..])
|
fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.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 validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||||
self.validator.as_ref()
|
self.v.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
|
|
||||||
}
|
}
|
||||||
|
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> {
|
impl<'n, 'e> DispOrder for PosBuilder<'n, 'e> {
|
||||||
fn disp_ord(&self) -> usize {
|
fn disp_ord(&self) -> usize { self.index as usize }
|
||||||
self.disp_ord
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -261,7 +135,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn display_mult() {
|
fn display_mult() {
|
||||||
let mut p = PosBuilder::new("pos", 1);
|
let mut p = PosBuilder::new("pos", 1);
|
||||||
p.settings.set(ArgSettings::Multiple);
|
p.b.settings.set(ArgSettings::Multiple);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", p), "<pos>...");
|
assert_eq!(&*format!("{}", p), "<pos>...");
|
||||||
}
|
}
|
||||||
|
@ -269,7 +143,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn display_required() {
|
fn display_required() {
|
||||||
let mut p2 = PosBuilder::new("pos", 1);
|
let mut p2 = PosBuilder::new("pos", 1);
|
||||||
p2.settings.set(ArgSettings::Required);
|
p2.b.settings.set(ArgSettings::Required);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", p2), "<pos>");
|
assert_eq!(&*format!("{}", p2), "<pos>");
|
||||||
}
|
}
|
||||||
|
@ -280,7 +154,7 @@ mod test {
|
||||||
let mut vm = VecMap::new();
|
let mut vm = VecMap::new();
|
||||||
vm.insert(0, "file1");
|
vm.insert(0, "file1");
|
||||||
vm.insert(1, "file2");
|
vm.insert(1, "file2");
|
||||||
p2.val_names = Some(vm);
|
p2.v.val_names = Some(vm);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
||||||
}
|
}
|
||||||
|
@ -288,11 +162,11 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn display_val_names_req() {
|
fn display_val_names_req() {
|
||||||
let mut p2 = PosBuilder::new("pos", 1);
|
let mut p2 = PosBuilder::new("pos", 1);
|
||||||
p2.settings.set(ArgSettings::Required);
|
p2.b.settings.set(ArgSettings::Required);
|
||||||
let mut vm = VecMap::new();
|
let mut vm = VecMap::new();
|
||||||
vm.insert(0, "file1");
|
vm.insert(0, "file1");
|
||||||
vm.insert(1, "file2");
|
vm.insert(1, "file2");
|
||||||
p2.val_names = Some(vm);
|
p2.v.val_names = Some(vm);
|
||||||
|
|
||||||
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
|
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>);
|
pub struct ArgMatcher<'a>(pub ArgMatches<'a>);
|
||||||
|
|
||||||
impl<'a> Default for ArgMatcher<'a> {
|
impl<'a> Default for ArgMatcher<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self { ArgMatcher(ArgMatches::default()) }
|
||||||
ArgMatcher(ArgMatches::default())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArgMatcher<'a> {
|
impl<'a> ArgMatcher<'a> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { ArgMatcher::default() }
|
||||||
ArgMatcher::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> {
|
pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> { self.0.args.get_mut(arg) }
|
||||||
self.0.args.get_mut(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, arg: &str) -> Option<&MatchedArg> {
|
pub fn get(&self, arg: &str) -> Option<&MatchedArg> { self.0.args.get(arg) }
|
||||||
self.0.args.get(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, arg: &str) {
|
pub fn remove(&mut self, arg: &str) { self.0.args.remove(arg); }
|
||||||
self.0.args.remove(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_all(&mut self, args: &[&str]) {
|
pub fn remove_all(&mut self, args: &[&str]) {
|
||||||
for &arg in args {
|
for &arg in args {
|
||||||
|
@ -44,43 +34,26 @@ impl<'a> ArgMatcher<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, name: &'a str) {
|
pub fn insert(&mut self, name: &'a str) { self.0.args.insert(name, MatchedArg::new()); }
|
||||||
self.0.args.insert(name, MatchedArg::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, arg: &str) -> bool {
|
pub fn contains(&self, arg: &str) -> bool { self.0.args.contains_key(arg) }
|
||||||
self.0.args.contains_key(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool { self.0.args.is_empty() }
|
||||||
self.0.args.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn usage(&mut self, usage: String) {
|
pub fn usage(&mut self, usage: String) { self.0.usage = Some(usage); }
|
||||||
self.0.usage = Some(usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arg_names(&'a self) -> Vec<&'a str> {
|
pub fn arg_names(&'a self) -> Vec<&'a str> { self.0.args.keys().map(Deref::deref).collect() }
|
||||||
self.0.args.keys().map(Deref::deref).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entry(&mut self, arg: &'a str) -> Entry<&'a str, MatchedArg> {
|
pub fn entry(&mut self, arg: &'a str) -> Entry<&'a str, MatchedArg> { self.0.args.entry(arg) }
|
||||||
self.0.args.entry(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subcommand(&mut self, sc: SubCommand<'a>) {
|
pub fn subcommand(&mut self, sc: SubCommand<'a>) { self.0.subcommand = Some(Box::new(sc)); }
|
||||||
self.0.subcommand = Some(Box::new(sc));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subcommand_name(&self) -> Option<&str> {
|
pub fn subcommand_name(&self) -> Option<&str> { self.0.subcommand_name() }
|
||||||
self.0.subcommand_name()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> Iter<&str, MatchedArg> {
|
pub fn iter(&self) -> Iter<&str, MatchedArg> { self.0.args.iter() }
|
||||||
self.0.args.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
|
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
|
||||||
|
debugln!("fn=inc_occurrence_of;");
|
||||||
if let Some(a) = self.get_mut(arg) {
|
if let Some(a) = self.get_mut(arg) {
|
||||||
debugln!("+1 to {}'s occurrences", arg);
|
debugln!("+1 to {}'s occurrences", arg);
|
||||||
a.occurs += 1;
|
a.occurs += 1;
|
||||||
|
@ -90,6 +63,7 @@ impl<'a> ArgMatcher<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc_occurrences_of(&mut self, args: &[&'a str]) {
|
pub fn inc_occurrences_of(&mut self, args: &[&'a str]) {
|
||||||
|
debugln!("fn=inc_occurrences_of;");
|
||||||
for arg in args {
|
for arg in args {
|
||||||
self.inc_occurrence_of(arg);
|
self.inc_occurrence_of(arg);
|
||||||
}
|
}
|
||||||
|
@ -107,16 +81,20 @@ impl<'a> ArgMatcher<'a> {
|
||||||
pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool
|
pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool
|
||||||
where A: AnyArg<'a, 'b>
|
where A: AnyArg<'a, 'b>
|
||||||
{
|
{
|
||||||
|
debugln!("fn=needs_more_vals;");
|
||||||
if let Some(ma) = self.get(o.name()) {
|
if let Some(ma) = self.get(o.name()) {
|
||||||
if let Some(num) = o.num_vals() {
|
if let Some(num) = o.num_vals() {
|
||||||
|
debugln!("num_vals...{}", num);
|
||||||
return if o.is_set(ArgSettings::Multiple) {
|
return if o.is_set(ArgSettings::Multiple) {
|
||||||
((ma.vals.len() as u64) % num) != 0
|
((ma.vals.len() as u64) % num) != 0
|
||||||
} else {
|
} else {
|
||||||
num != (ma.vals.len() as u64)
|
num != (ma.vals.len() as u64)
|
||||||
};
|
};
|
||||||
} else if let Some(num) = o.max_vals() {
|
} else if let Some(num) = o.max_vals() {
|
||||||
|
debugln!("max_vals...{}", num);
|
||||||
return !((ma.vals.len() as u64) > num);
|
return !((ma.vals.len() as u64) > num);
|
||||||
} else if o.min_vals().is_some() {
|
} else if o.min_vals().is_some() {
|
||||||
|
debugln!("min_vals...true");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return o.is_set(ArgSettings::Multiple);
|
return o.is_set(ArgSettings::Multiple);
|
||||||
|
@ -126,7 +104,5 @@ impl<'a> ArgMatcher<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<ArgMatches<'a>> for ArgMatcher<'a> {
|
impl<'a> Into<ArgMatches<'a>> for ArgMatcher<'a> {
|
||||||
fn into(self) -> ArgMatches<'a> {
|
fn into(self) -> ArgMatches<'a> { self.0 }
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,9 +82,7 @@ impl<'a> Default for ArgMatches<'a> {
|
||||||
|
|
||||||
impl<'a> ArgMatches<'a> {
|
impl<'a> ArgMatches<'a> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { ArgMatches { ..Default::default() } }
|
||||||
ArgMatches { ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the value of a specific [option] or [positional] argument (i.e. an argument that takes
|
/// 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
|
/// 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
|
/// [`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>> {
|
pub fn values_of<S: AsRef<str>>(&'a self, name: S) -> Option<Values<'a>> {
|
||||||
if let Some(arg) = self.args.get(name.as_ref()) {
|
if let Some(arg) = self.args.get(name.as_ref()) {
|
||||||
fn to_str_slice(o: &OsString) -> &str {
|
fn to_str_slice(o: &OsString) -> &str { o.to_str().expect(INVALID_UTF8) }
|
||||||
o.to_str().expect(INVALID_UTF8)
|
|
||||||
}
|
|
||||||
let to_str_slice: fn(&OsString) -> &str = to_str_slice; // coerce to fn pointer
|
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) });
|
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
|
/// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html
|
||||||
/// [`String`]: https://doc.rust-lang.org/std/string/struct.String.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>> {
|
pub fn values_of_os<S: AsRef<str>>(&'a self, name: S) -> Option<OsValues<'a>> {
|
||||||
fn to_str_slice(o: &OsString) -> &OsStr {
|
fn to_str_slice(o: &OsString) -> &OsStr { &*o }
|
||||||
&*o
|
|
||||||
}
|
|
||||||
let to_str_slice: fn(&'a OsString) -> &'a OsStr = to_str_slice; // coerce to fn pointer
|
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()) {
|
if let Some(arg) = self.args.get(name.as_ref()) {
|
||||||
return Some(OsValues { iter: arg.vals.values().map(to_str_slice) });
|
return Some(OsValues { iter: arg.vals.values().map(to_str_slice) });
|
||||||
|
@ -533,9 +527,7 @@ impl<'a> ArgMatches<'a> {
|
||||||
/// ```
|
/// ```
|
||||||
/// [`Subcommand`]: ./struct.SubCommand.html
|
/// [`Subcommand`]: ./struct.SubCommand.html
|
||||||
/// [`App`]: ./struct.App.html
|
/// [`App`]: ./struct.App.html
|
||||||
pub fn usage(&self) -> &str {
|
pub fn usage(&self) -> &str { self.usage.as_ref().map_or("", |u| &u[..]) }
|
||||||
self.usage.as_ref().map_or("", |u| &u[..])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -568,18 +560,12 @@ pub struct Values<'a> {
|
||||||
impl<'a> Iterator for Values<'a> {
|
impl<'a> Iterator for Values<'a> {
|
||||||
type Item = &'a str;
|
type Item = &'a str;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a str> {
|
fn next(&mut self) -> Option<&'a str> { self.iter.next() }
|
||||||
self.iter.next()
|
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||||
}
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
self.iter.size_hint()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for Values<'a> {
|
impl<'a> DoubleEndedIterator for Values<'a> {
|
||||||
fn next_back(&mut self) -> Option<&'a str> {
|
fn next_back(&mut self) -> Option<&'a str> { self.iter.next_back() }
|
||||||
self.iter.next_back()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over the key-value pairs of a map.
|
/// An iterator over the key-value pairs of a map.
|
||||||
|
@ -608,9 +594,7 @@ impl<'a, V> Iterator for Iter<'a, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(self.back - self.front)) }
|
||||||
(0, Some(self.back - self.front))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, V> DoubleEndedIterator for Iter<'a, V> {
|
impl<'a, V> DoubleEndedIterator for Iter<'a, V> {
|
||||||
|
@ -659,16 +643,10 @@ pub struct OsValues<'a> {
|
||||||
impl<'a> Iterator for OsValues<'a> {
|
impl<'a> Iterator for OsValues<'a> {
|
||||||
type Item = &'a OsStr;
|
type Item = &'a OsStr;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a OsStr> {
|
fn next(&mut self) -> Option<&'a OsStr> { self.iter.next() }
|
||||||
self.iter.next()
|
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||||
}
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
self.iter.size_hint()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for OsValues<'a> {
|
impl<'a> DoubleEndedIterator for OsValues<'a> {
|
||||||
fn next_back(&mut self) -> Option<&'a OsStr> {
|
fn next_back(&mut self) -> Option<&'a OsStr> { self.iter.next_back() }
|
||||||
self.iter.next_back()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,9 +128,7 @@ impl<'a> ArgGroup<'a> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> {
|
pub fn from_yaml(y: &'a Yaml) -> ArgGroup<'a> { ArgGroup::from(y.as_hash().unwrap()) }
|
||||||
ArgGroup::from(y.as_hash().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds an [argument] to this group by name
|
/// Adds an [argument] to this group by name
|
||||||
///
|
///
|
||||||
|
|
|
@ -23,7 +23,5 @@ impl Default for MatchedArg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchedArg {
|
impl MatchedArg {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { MatchedArg::default() }
|
||||||
MatchedArg::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
pub use self::any_arg::{AnyArg, DispOrder};
|
pub use self::any_arg::{AnyArg, DispOrder};
|
||||||
pub use self::arg::Arg;
|
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_matcher::ArgMatcher;
|
||||||
pub use self::arg_matches::{Values, OsValues, ArgMatches};
|
pub use self::arg_matches::{Values, OsValues, ArgMatches};
|
||||||
pub use self::group::ArgGroup;
|
pub use self::group::ArgGroup;
|
||||||
pub use self::matched_arg::MatchedArg;
|
pub use self::matched_arg::MatchedArg;
|
||||||
pub use self::settings::ArgSettings;
|
pub use self::settings::{ArgFlags, ArgSettings};
|
||||||
pub use self::subcommand::SubCommand;
|
pub use self::subcommand::SubCommand;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -19,3 +19,13 @@ mod arg_builder;
|
||||||
mod matched_arg;
|
mod matched_arg;
|
||||||
mod group;
|
mod group;
|
||||||
pub mod settings;
|
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! {
|
bitflags! {
|
||||||
flags Flags: u16 {
|
flags Flags: u16 {
|
||||||
const REQUIRED = 0b000000000001,
|
const REQUIRED = 0b0000000000001,
|
||||||
const MULTIPLE = 0b000000000010,
|
const MULTIPLE = 0b0000000000010,
|
||||||
const EMPTY_VALS = 0b000000000100,
|
const EMPTY_VALS = 0b0000000000100,
|
||||||
const GLOBAL = 0b000000001000,
|
const GLOBAL = 0b0000000001000,
|
||||||
const HIDDEN = 0b000000010000,
|
const HIDDEN = 0b0000000010000,
|
||||||
const TAKES_VAL = 0b000000100000,
|
const TAKES_VAL = 0b0000000100000,
|
||||||
const USE_DELIM = 0b000001000000,
|
const USE_DELIM = 0b0000001000000,
|
||||||
const NEXT_LINE_HELP = 0b000010000000,
|
const NEXT_LINE_HELP = 0b0000010000000,
|
||||||
const R_UNLESS_ALL = 0b000100000000,
|
const R_UNLESS_ALL = 0b0000100000000,
|
||||||
const REQ_DELIM = 0b001000000000,
|
const REQ_DELIM = 0b0001000000000,
|
||||||
const DELIM_NOT_SET = 0b010000000000,
|
const DELIM_NOT_SET = 0b0010000000000,
|
||||||
const HIDE_POS_VALS = 0b100000000000,
|
const HIDE_POS_VALS = 0b0100000000000,
|
||||||
|
const ALLOW_TAC_VALS = 0b1000000000000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,9 +25,7 @@ bitflags! {
|
||||||
pub struct ArgFlags(Flags);
|
pub struct ArgFlags(Flags);
|
||||||
|
|
||||||
impl ArgFlags {
|
impl ArgFlags {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { ArgFlags::default() }
|
||||||
ArgFlags::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_settings!{ArgSettings,
|
impl_settings!{ArgSettings,
|
||||||
Required => REQUIRED,
|
Required => REQUIRED,
|
||||||
|
@ -40,14 +39,13 @@ impl ArgFlags {
|
||||||
RequiredUnlessAll => R_UNLESS_ALL,
|
RequiredUnlessAll => R_UNLESS_ALL,
|
||||||
RequireDelimiter => REQ_DELIM,
|
RequireDelimiter => REQ_DELIM,
|
||||||
ValueDelimiterNotSet => DELIM_NOT_SET,
|
ValueDelimiterNotSet => DELIM_NOT_SET,
|
||||||
HidePossibleValues => HIDE_POS_VALS
|
HidePossibleValues => HIDE_POS_VALS,
|
||||||
|
AllowLeadingHyphen => ALLOW_TAC_VALS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ArgFlags {
|
impl Default for ArgFlags {
|
||||||
fn default() -> Self {
|
fn default() -> Self { ArgFlags(EMPTY_VALS | DELIM_NOT_SET) }
|
||||||
ArgFlags(EMPTY_VALS | DELIM_NOT_SET)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
|
/// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
|
||||||
|
@ -78,6 +76,8 @@ pub enum ArgSettings {
|
||||||
RequireDelimiter,
|
RequireDelimiter,
|
||||||
/// Hides the possible values from the help string
|
/// Hides the possible values from the help string
|
||||||
HidePossibleValues,
|
HidePossibleValues,
|
||||||
|
/// Allows vals that start with a '-'
|
||||||
|
AllowLeadingHyphen,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
RequiredUnlessAll,
|
RequiredUnlessAll,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -100,7 +100,44 @@ impl FromStr for ArgSettings {
|
||||||
"requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
|
"requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
|
||||||
"valuedelimiternotset" => Ok(ArgSettings::ValueDelimiterNotSet),
|
"valuedelimiternotset" => Ok(ArgSettings::ValueDelimiterNotSet),
|
||||||
"hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues),
|
"hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues),
|
||||||
|
"allowleadinghyphen" => Ok(ArgSettings::AllowLeadingHyphen),
|
||||||
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
|
_ => 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"))
|
/// SubCommand::with_name("config"))
|
||||||
/// # ;
|
/// # ;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_name<'b>(name: &str) -> App<'a, 'b> {
|
pub fn with_name<'b>(name: &str) -> App<'a, 'b> { App::new(name) }
|
||||||
App::new(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new instance of a subcommand from a YAML (.yml) document
|
/// Creates a new instance of a subcommand from a YAML (.yml) document
|
||||||
///
|
///
|
||||||
|
@ -66,7 +64,5 @@ impl<'a> SubCommand<'a> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
pub fn from_yaml(yaml: &Yaml) -> App {
|
pub fn from_yaml(yaml: &Yaml) -> App { App::from_yaml(yaml) }
|
||||||
App::from_yaml(yaml)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,11 @@ pub struct BashGen<'a, 'b>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> BashGen<'a, 'b> {
|
impl<'a, 'b> BashGen<'a, 'b> {
|
||||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
pub fn new(p: &'b Parser<'a, 'b>) -> Self { BashGen { p: p } }
|
||||||
BashGen { p: p }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||||
|
|
||||||
w!(buf,
|
w!(buf,
|
||||||
format!("_{name}() {{
|
format!("_{name}() {{
|
||||||
local i cur prev opts cmds
|
local i cur prev opts cmds
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
|
@ -141,8 +139,8 @@ complete -F _{name} {name}
|
||||||
.p;
|
.p;
|
||||||
}
|
}
|
||||||
let mut opts = String::new();
|
let mut opts = String::new();
|
||||||
for o in &p.opts {
|
for o in p.opts() {
|
||||||
if let Some(l) = o.long {
|
if let Some(l) = o.s.long {
|
||||||
opts = format!("{}
|
opts = format!("{}
|
||||||
--{})
|
--{})
|
||||||
COMPREPLY=({})
|
COMPREPLY=({})
|
||||||
|
@ -152,7 +150,7 @@ complete -F _{name} {name}
|
||||||
l,
|
l,
|
||||||
self.vals_for(o));
|
self.vals_for(o));
|
||||||
}
|
}
|
||||||
if let Some(s) = o.short {
|
if let Some(s) = o.s.short {
|
||||||
opts = format!("{}
|
opts = format!("{}
|
||||||
-{})
|
-{})
|
||||||
COMPREPLY=({})
|
COMPREPLY=({})
|
||||||
|
@ -177,9 +175,9 @@ complete -F _{name} {name}
|
||||||
let mut it = vec.iter().peekable();
|
let mut it = vec.iter().peekable();
|
||||||
while let Some((_, val)) = it.next() {
|
while let Some((_, val)) = it.next() {
|
||||||
ret = format!("{}<{}>{}",
|
ret = format!("{}<{}>{}",
|
||||||
ret,
|
ret,
|
||||||
val,
|
val,
|
||||||
if it.peek().is_some() { " " } else { "" });
|
if it.peek().is_some() { " " } else { "" });
|
||||||
}
|
}
|
||||||
let num = vec.len();
|
let num = vec.len();
|
||||||
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
||||||
|
@ -189,9 +187,9 @@ complete -F _{name} {name}
|
||||||
let mut it = (0..num).peekable();
|
let mut it = (0..num).peekable();
|
||||||
while let Some(_) = it.next() {
|
while let Some(_) = it.next() {
|
||||||
ret = format!("{}<{}>{}",
|
ret = format!("{}<{}>{}",
|
||||||
ret,
|
ret,
|
||||||
o.name(),
|
o.name(),
|
||||||
if it.peek().is_some() { " " } else { "" });
|
if it.peek().is_some() { " " } else { "" });
|
||||||
}
|
}
|
||||||
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
if o.is_set(ArgSettings::Multiple) && num == 1 {
|
||||||
ret = format!("{}...", ret);
|
ret = format!("{}...", ret);
|
||||||
|
|
|
@ -12,16 +12,13 @@ pub struct FishGen<'a, 'b>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> FishGen<'a, 'b> {
|
impl<'a, 'b> FishGen<'a, 'b> {
|
||||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
pub fn new(p: &'b Parser<'a, 'b>) -> Self { FishGen { p: p } }
|
||||||
FishGen { p: p }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||||
let command = self.p.meta.bin_name.as_ref().unwrap();
|
let command = self.p.meta.bin_name.as_ref().unwrap();
|
||||||
|
|
||||||
// function to detect subcommand
|
// function to detect subcommand
|
||||||
let detect_subcommand_function =
|
let detect_subcommand_function = r#"function __fish_using_command
|
||||||
r#"function __fish_using_command
|
|
||||||
set cmd (commandline -opc)
|
set cmd (commandline -opc)
|
||||||
if [ (count $cmd) -eq (count $argv) ]
|
if [ (count $cmd) -eq (count $argv) ]
|
||||||
for i in (seq (count $argv))
|
for i in (seq (count $argv))
|
||||||
|
@ -34,7 +31,8 @@ r#"function __fish_using_command
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
"#.to_string();
|
"#
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let mut buffer = detect_subcommand_function;
|
let mut buffer = detect_subcommand_function;
|
||||||
gen_fish_inner(command, self, &command.to_string(), &mut buffer);
|
gen_fish_inner(command, self, &command.to_string(), &mut buffer);
|
||||||
|
@ -42,10 +40,7 @@ end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_fish_inner(root_command: &str,
|
fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, parent_cmds: &str, buffer: &mut String) {
|
||||||
comp_gen: &FishGen,
|
|
||||||
parent_cmds: &str,
|
|
||||||
buffer: &mut String) {
|
|
||||||
// example :
|
// example :
|
||||||
//
|
//
|
||||||
// complete
|
// complete
|
||||||
|
@ -59,36 +54,36 @@ fn gen_fish_inner(root_command: &str,
|
||||||
// -n "__fish_using_command myprog subcmd1" # complete for command "myprog subcmd1"
|
// -n "__fish_using_command myprog subcmd1" # complete for command "myprog subcmd1"
|
||||||
|
|
||||||
let basic_template = format!("complete -c {} -n \"__fish_using_command {}\"",
|
let basic_template = format!("complete -c {} -n \"__fish_using_command {}\"",
|
||||||
root_command,
|
root_command,
|
||||||
parent_cmds);
|
parent_cmds);
|
||||||
|
|
||||||
for option in &comp_gen.p.opts {
|
for option in comp_gen.p.opts() {
|
||||||
let mut template = basic_template.clone();
|
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());
|
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());
|
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());
|
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());
|
template.push_str(format!(" -r -f -a \"{}\"", data.join(" ")).as_str());
|
||||||
}
|
}
|
||||||
buffer.push_str(template.as_str());
|
buffer.push_str(template.as_str());
|
||||||
buffer.push_str("\n");
|
buffer.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in &comp_gen.p.flags {
|
for flag in comp_gen.p.flags() {
|
||||||
let mut template = basic_template.clone();
|
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());
|
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());
|
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());
|
template.push_str(format!(" -d \"{}\"", data).as_str());
|
||||||
}
|
}
|
||||||
buffer.push_str(template.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(" ");
|
||||||
}
|
}
|
||||||
sub_parent_cmds.push_str(&subcommand.p.meta.name);
|
sub_parent_cmds.push_str(&subcommand.p.meta.name);
|
||||||
gen_fish_inner(root_command,
|
gen_fish_inner(root_command, &sub_comp_gen, &sub_parent_cmds, buffer);
|
||||||
&sub_comp_gen,
|
|
||||||
&sub_parent_cmds,
|
|
||||||
buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,15 +24,13 @@ pub struct ComplGen<'a, 'b>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ComplGen<'a, 'b> {
|
impl<'a, 'b> ComplGen<'a, 'b> {
|
||||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
pub fn new(p: &'b Parser<'a, 'b>) -> Self { ComplGen { p: p } }
|
||||||
ComplGen { p: p }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
|
pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
|
||||||
match for_shell {
|
match for_shell {
|
||||||
Shell::Bash => BashGen::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::Fish => FishGen::new(self.p).generate_to(buf),
|
||||||
Shell::Zsh => ZshGen::new(self.p).generate_to(buf),
|
Shell::Zsh => ZshGen::new(self.p).generate_to(buf),
|
||||||
Shell::PowerShell => PowerShellGen::new(self.p).generate_to(buf),
|
Shell::PowerShell => PowerShellGen::new(self.p).generate_to(buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
// Also note, aliases are treated as their own subcommands but duplicates of whatever they're
|
||||||
// aliasing.
|
// aliasing.
|
||||||
pub fn subcommands_of(p: &Parser) -> Vec<(String, String)> {
|
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![];
|
let mut subcmds = vec![];
|
||||||
|
|
||||||
debug!("Has subcommands...");
|
debug!("Has subcommands...");
|
||||||
|
@ -86,7 +86,8 @@ pub fn subcommands_of(p: &Parser) -> Vec<(String, String)> {
|
||||||
if let Some(ref aliases) = p.meta.aliases {
|
if let Some(ref aliases) = p.meta.aliases {
|
||||||
for &(n, _) in aliases {
|
for &(n, _) in aliases {
|
||||||
debugln!("Found alias...{}", n);
|
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);
|
als_bin_name.push(n);
|
||||||
let old = als_bin_name.len() - 2;
|
let old = als_bin_name.len() - 2;
|
||||||
als_bin_name.swap_remove(old);
|
als_bin_name.swap_remove(old);
|
||||||
|
@ -97,13 +98,16 @@ pub fn subcommands_of(p: &Parser) -> Vec<(String, String)> {
|
||||||
}
|
}
|
||||||
sdebugln!("Yes");
|
sdebugln!("Yes");
|
||||||
for sc in &p.subcommands {
|
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...");
|
debugln!("Looking for aliases...");
|
||||||
if let Some(ref aliases) = sc.p.meta.aliases {
|
if let Some(ref aliases) = sc.p.meta.aliases {
|
||||||
for &(n, _) in aliases {
|
for &(n, _) in aliases {
|
||||||
debugln!("Found alias...{}", n);
|
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);
|
als_bin_name.push(n);
|
||||||
let old = als_bin_name.len() - 2;
|
let old = als_bin_name.len() - 2;
|
||||||
als_bin_name.swap_remove(old);
|
als_bin_name.swap_remove(old);
|
||||||
|
|
|
@ -12,9 +12,7 @@ pub struct PowerShellGen<'a, 'b>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> PowerShellGen<'a, 'b> {
|
impl<'a, 'b> PowerShellGen<'a, 'b> {
|
||||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
pub fn new(p: &'b Parser<'a, 'b>) -> Self { PowerShellGen { p: p } }
|
||||||
PowerShellGen { p: p }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||||
let bin_name = self.p.meta.bin_name.as_ref().unwrap();
|
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));
|
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 bin_names = bin_names.trim_left_matches(", ");
|
||||||
|
|
||||||
let result = format!(r#"
|
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) {
|
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 command_name = format!("{}_{}", previous_command_name, &p.meta.name);
|
||||||
|
|
||||||
let mut subcommands_detection_cases =
|
let mut subcommands_detection_cases = if previous_command_name == "" {
|
||||||
if previous_command_name == "" {
|
String::new()
|
||||||
String::new()
|
} else {
|
||||||
}
|
format!(r"
|
||||||
else {
|
|
||||||
format!(r"
|
|
||||||
'{0}' {{
|
'{0}' {{
|
||||||
$command += '_{0}'
|
$command += '_{0}'
|
||||||
break
|
break
|
||||||
}}
|
}}
|
||||||
", &p.meta.name)
|
",
|
||||||
};
|
&p.meta.name)
|
||||||
|
};
|
||||||
|
|
||||||
let mut completions = String::new();
|
let mut completions = String::new();
|
||||||
for subcommand in &p.subcommands {
|
for subcommand in &p.subcommands {
|
||||||
|
@ -103,10 +102,13 @@ fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (S
|
||||||
'{}' {{
|
'{}' {{
|
||||||
$completions = @({})
|
$completions = @({})
|
||||||
}}
|
}}
|
||||||
", &command_name, completions.trim_right_matches(", "));
|
",
|
||||||
|
&command_name,
|
||||||
|
completions.trim_right_matches(", "));
|
||||||
|
|
||||||
for subcommand in &p.subcommands {
|
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_detection_cases.push_str(&subcommand_subcommands_detection_cases);
|
||||||
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,7 @@ pub enum Shell {
|
||||||
|
|
||||||
impl Shell {
|
impl Shell {
|
||||||
/// A list of possible variants in `&'static str` form
|
/// A list of possible variants in `&'static str` form
|
||||||
pub fn variants() -> [&'static str; 4] {
|
pub fn variants() -> [&'static str; 4] { ["zsh", "bash", "fish", "powershell"] }
|
||||||
[
|
|
||||||
"zsh",
|
|
||||||
"bash",
|
|
||||||
"fish",
|
|
||||||
"powershell"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Shell {
|
impl FromStr for Shell {
|
||||||
|
@ -38,9 +31,7 @@ impl FromStr for Shell {
|
||||||
"FISH" | _ if s.eq_ignore_ascii_case("fish") => Ok(Shell::Fish),
|
"FISH" | _ if s.eq_ignore_ascii_case("fish") => Ok(Shell::Fish),
|
||||||
"BASH" | _ if s.eq_ignore_ascii_case("bash") => Ok(Shell::Bash),
|
"BASH" | _ if s.eq_ignore_ascii_case("bash") => Ok(Shell::Bash),
|
||||||
"POWERSHELL" | _ if s.eq_ignore_ascii_case("powershell") => Ok(Shell::PowerShell),
|
"POWERSHELL" | _ if s.eq_ignore_ascii_case("powershell") => Ok(Shell::PowerShell),
|
||||||
_ => Err(
|
_ => Err(String::from("[valid values: bash, fish, zsh, powershell]")),
|
||||||
String::from("[valid values: bash, fish, zsh, powershell]")
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,11 +39,10 @@ impl FromStr for Shell {
|
||||||
impl fmt::Display for Shell {
|
impl fmt::Display for Shell {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Shell::Bash => write!(f, "BASH"),
|
Shell::Bash => write!(f, "BASH"),
|
||||||
Shell::Fish => write!(f, "FISH"),
|
Shell::Fish => write!(f, "FISH"),
|
||||||
Shell::Zsh => write!(f, "ZSH"),
|
Shell::Zsh => write!(f, "ZSH"),
|
||||||
Shell::PowerShell => write!(f, "POWERSHELL"),
|
Shell::PowerShell => write!(f, "POWERSHELL"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,7 @@ pub struct ZshGen<'a, 'b>
|
||||||
impl<'a, 'b> ZshGen<'a, 'b> {
|
impl<'a, 'b> ZshGen<'a, 'b> {
|
||||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||||
debugln!("fn=ZshGen::new;");
|
debugln!("fn=ZshGen::new;");
|
||||||
ZshGen {
|
ZshGen { p: p }
|
||||||
p: p,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||||
|
@ -142,7 +140,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
||||||
debugln!("iter;subcommand={}", sc.p.meta.name);
|
debugln!("iter;subcommand={}", sc.p.meta.name);
|
||||||
add_sc(sc, &sc.p.meta.name, &mut ret);
|
add_sc(sc, &sc.p.meta.name, &mut ret);
|
||||||
if let Some(ref v) = sc.p.meta.aliases {
|
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);
|
add_sc(sc, alias, &mut ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,10 +148,10 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
||||||
|
|
||||||
// Then the positional args
|
// Then the positional args
|
||||||
for arg in p.positionals() {
|
for arg in p.positionals() {
|
||||||
debugln!("iter;arg={}", arg.name);
|
debugln!("iter;arg={}", arg.b.name);
|
||||||
let a = format!("\"{name}:{help}\" \\",
|
let a = format!("\"{name}:{help}\" \\",
|
||||||
name = arg.name.to_ascii_uppercase(),
|
name = arg.b.name.to_ascii_uppercase(),
|
||||||
help = arg.help.unwrap_or(""));
|
help = arg.b.help.unwrap_or(""));
|
||||||
|
|
||||||
if !a.is_empty() {
|
if !a.is_empty() {
|
||||||
ret.push(a);
|
ret.push(a);
|
||||||
|
@ -193,7 +191,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
|
||||||
// [repeat] = From the same recursive calls, but for all subcommands
|
// [repeat] = From the same recursive calls, but for all subcommands
|
||||||
// [subcommand_args] = The same as zsh::get_args_of
|
// [subcommand_args] = The same as zsh::get_args_of
|
||||||
fn get_subcommands_of(p: &Parser) -> String {
|
fn get_subcommands_of(p: &Parser) -> String {
|
||||||
debugln!("fn=get_subcommands");
|
debugln!("fn=get_subcommands_of");
|
||||||
|
|
||||||
debug!("Has subcommands...");
|
debug!("Has subcommands...");
|
||||||
if !p.has_subcommands() {
|
if !p.has_subcommands() {
|
||||||
|
@ -236,6 +234,9 @@ esac",
|
||||||
|
|
||||||
fn parser_of<'a, 'b>(p: &'b Parser<'a, 'b>, sc: &str) -> &'b Parser<'a, 'b> {
|
fn parser_of<'a, 'b>(p: &'b Parser<'a, 'b>, sc: &str) -> &'b Parser<'a, 'b> {
|
||||||
debugln!("fn=parser_of;sc={}", sc);
|
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
|
&p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG).p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,15 +266,15 @@ fn get_args_of(p: &Parser) -> String {
|
||||||
let opts = write_opts_of(p);
|
let opts = write_opts_of(p);
|
||||||
let flags = write_flags_of(p);
|
let flags = write_flags_of(p);
|
||||||
let sc_or_a = if p.has_subcommands() || p.has_positionals() {
|
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(" ", "_"))
|
name = p.meta.bin_name.as_ref().unwrap().replace(" ", "_"))
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
let sc = if p.has_subcommands() {
|
let sc = if p.has_subcommands() {
|
||||||
format!("\"*:: :->{name}\" \\", name = p.meta.name)
|
format!("\"*:: :->{name}\" \\", name = p.meta.name)
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
if !opts.is_empty() {
|
if !opts.is_empty() {
|
||||||
|
@ -300,11 +301,19 @@ fn write_opts_of(p: &Parser) -> String {
|
||||||
debugln!("iter;o={}", o.name());
|
debugln!("iter;o={}", o.name());
|
||||||
let help = o.help().unwrap_or("");
|
let help = o.help().unwrap_or("");
|
||||||
let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
|
let mut conflicts = get_zsh_arg_conflicts!(p, o, INTERNAL_ERROR_MSG);
|
||||||
conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };
|
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() {
|
let pv = if let Some(pv_vec) = o.possible_vals() {
|
||||||
format!(": :({})", pv_vec.join(" "))
|
format!(": :({})", pv_vec.join(" "))
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
|
@ -342,9 +351,17 @@ fn write_flags_of(p: &Parser) -> String {
|
||||||
debugln!("iter;f={}", f.name());
|
debugln!("iter;f={}", f.name());
|
||||||
let help = f.help().unwrap_or("");
|
let help = f.help().unwrap_or("");
|
||||||
let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
|
let mut conflicts = get_zsh_arg_conflicts!(p, f, INTERNAL_ERROR_MSG);
|
||||||
conflicts = if conflicts.is_empty() { String::new() } else { format!("({})", conflicts) };
|
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() {
|
if let Some(short) = f.short() {
|
||||||
let s = format!("\"{conflicts}{multiple}-{arg}[{help}]\" \\",
|
let s = format!("\"{conflicts}{multiple}-{arg}[{help}]\" \\",
|
||||||
multiple = multiple,
|
multiple = multiple,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::process;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
use args::any_arg::AnyArg;
|
use args::{FlagBuilder, AnyArg};
|
||||||
use fmt;
|
use fmt;
|
||||||
use suggestions;
|
use suggestions;
|
||||||
|
|
||||||
|
@ -402,9 +402,7 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> { write!(w, "{}", self.message) }
|
||||||
write!(w, "{}", self.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A,
|
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A,
|
||||||
|
@ -703,13 +701,22 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[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 {
|
let c = fmt::Colorizer {
|
||||||
use_stderr: true,
|
use_stderr: true,
|
||||||
when: color,
|
when: color,
|
||||||
};
|
};
|
||||||
Error {
|
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,
|
kind: ErrorKind::ValueValidation,
|
||||||
info: None,
|
info: None,
|
||||||
}
|
}
|
||||||
|
@ -717,7 +724,8 @@ impl Error {
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn value_validation_auto(err: String) -> Self {
|
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)]
|
#[doc(hidden)]
|
||||||
|
@ -858,21 +866,15 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for Error {
|
impl StdError for Error {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str { &*self.message }
|
||||||
&*self.message
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result {
|
fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result { writeln!(f, "{}", self.message) }
|
||||||
writeln!(f, "{}", self.message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(e: io::Error) -> Self {
|
fn from(e: io::Error) -> Self { Error::with_description(e.description(), ErrorKind::Io) }
|
||||||
Error::with_description(e.description(), ErrorKind::Io)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std_fmt::Error> for Error {
|
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")))]
|
#[cfg(all(feature = "color", not(target_os = "windows")))]
|
||||||
impl<T: AsRef<str>> fmt::Display for Format<T> {
|
impl<T: AsRef<str>> fmt::Display for Format<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &self.format()) }
|
||||||
write!(f, "{}", &self.format())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "color"), target_os = "windows"))]
|
#[cfg(any(not(feature = "color"), target_os = "windows"))]
|
||||||
impl<T: fmt::Display> fmt::Display for Format<T> {
|
impl<T: fmt::Display> fmt::Display for Format<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &self.format()) }
|
||||||
write!(f, "{}", &self.format())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(test, feature = "color", not(target_os = "windows")))]
|
#[cfg(all(test, feature = "color", not(target_os = "windows")))]
|
||||||
|
|
346
src/lib.rs
346
src/lib.rs
|
@ -3,51 +3,41 @@
|
||||||
// (see LICENSE or <http://opensource.org/licenses/MIT>) All files in the project carrying such
|
// (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.
|
// 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
|
//! ## 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
|
//! ## 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`
|
//! **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.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! // (Full example with detailed comments in examples/01b_quick_example.rs)
|
//! // (Full example with detailed comments in examples/01b_quick_example.rs)
|
||||||
|
@ -117,9 +107,9 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The following example is functionally the same as the one above,
|
//! The next example shows a far less verbose method, but sacrifices some of the advanced
|
||||||
//! but shows a far less verbose method but sacrifices some of the advanced configuration options
|
//! configuration options (not shown in this small example). This method also takes a *very* minor
|
||||||
//! (not shown in this small example).
|
//! runtime penalty.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! // (Full example with detailed comments in examples/01a_quick_example.rs)
|
//! // (Full example with detailed comments in examples/01a_quick_example.rs)
|
||||||
|
@ -149,41 +139,16 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The following combines the previous two examples by using the less verbose `from_usage` methods and the performance of the Builder Pattern.
|
//! 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.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! First, create the `cli.yml` file to hold your CLI options, but it could be called anything we
|
||||||
//! // (Full example with detailed comments in examples/01c_quick_example.rs)
|
//! like:
|
||||||
//! //
|
|
||||||
//! // 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):
|
|
||||||
//!
|
//!
|
||||||
//! ```yaml
|
//! ```yaml
|
||||||
//! name: myapp
|
//! name: myapp
|
||||||
//! version: 1.0
|
//! version: "1.0"
|
||||||
//! author: Kevin K. <kbknapp@gmail.com>
|
//! author: Kevin K. <kbknapp@gmail.com>
|
||||||
//! about: Does awesome things
|
//! about: Does awesome things
|
||||||
//! args:
|
//! args:
|
||||||
|
@ -204,7 +169,7 @@
|
||||||
//! subcommands:
|
//! subcommands:
|
||||||
//! - test:
|
//! - test:
|
||||||
//! about: controls testing features
|
//! about: controls testing features
|
||||||
//! version: 1.3
|
//! version: "1.3"
|
||||||
//! author: Someone E. <someone_else@other.com>
|
//! author: Someone E. <someone_else@other.com>
|
||||||
//! args:
|
//! args:
|
||||||
//! - debug:
|
//! - debug:
|
||||||
|
@ -212,8 +177,10 @@
|
||||||
//! help: print debug information
|
//! help: print debug information
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Because this feature is not compiled in by default we need to enable a feature flag in Cargo.toml:
|
//! Since this feature requires additional dependencies that not everyone may want, it is *not*
|
||||||
//! Simply change your `clap = "2"` to `clap = {version = "2", features = ["yaml"]}`.
|
//! 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:
|
//! At last we create our `main.rs` file just like we would have with the previous two examples:
|
||||||
//!
|
//!
|
||||||
|
@ -235,7 +202,35 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! 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
|
//! ```text
|
||||||
//! $ myprog --help
|
//! $ myprog --help
|
||||||
|
@ -262,7 +257,8 @@
|
||||||
//! test Controls testing features
|
//! 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!
|
//! ## Try it!
|
||||||
//!
|
//!
|
||||||
|
@ -277,22 +273,16 @@
|
||||||
//!
|
//!
|
||||||
//! ### BYOB (Build Your Own Binary)
|
//! ### BYOB (Build Your Own Binary)
|
||||||
//!
|
//!
|
||||||
//! To test `clap`'s default auto-generated help/version, follow these steps:
|
//! 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`
|
||||||
//!
|
//!
|
||||||
//! * Create a new cargo project:
|
//! ```toml
|
||||||
|
//! [dependencies]
|
||||||
|
//! clap = "2"
|
||||||
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ```ignore
|
//! * Add the following to your `src/main.rs`
|
||||||
//! 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
|
//! ```no_run
|
||||||
//! extern crate clap;
|
//! extern crate clap;
|
||||||
|
@ -303,21 +293,19 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * Build your program, then run it:
|
//! * Build your program `$ cargo build --release`
|
||||||
//!
|
//! * Run with help or version `$ ./target/release/fake --help` or `$ ./target/release/fake
|
||||||
//! ```ignore
|
//! --version`
|
||||||
//! cargo build --release
|
|
||||||
//! ./target/release/fake --help
|
|
||||||
//! ./target/release/fake --version
|
|
||||||
//! ```
|
|
||||||
//!
|
//!
|
||||||
//! ## Usage
|
//! ## 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
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! clap = "2"
|
//! clap = "~2.19.0"
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Or get the latest changes from the master branch at github:
|
//! Or get the latest changes from the master branch at github:
|
||||||
|
@ -329,7 +317,8 @@
|
||||||
//!
|
//!
|
||||||
//! Add `extern crate clap;` to your crate root.
|
//! 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.
|
//! Then run `cargo build` or `cargo update && cargo build` for your project.
|
||||||
//!
|
//!
|
||||||
|
@ -337,15 +326,15 @@
|
||||||
//!
|
//!
|
||||||
//! #### Features enabled by default
|
//! #### Features enabled by default
|
||||||
//!
|
//!
|
||||||
//! * **"suggestions"**: Turns on the `Did you mean '--myoption'?` feature for when users make typos.
|
//! * **"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.
|
//! * **"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.
|
//! * **"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`:
|
//! To disable these, add this to your `Cargo.toml`:
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies.clap]
|
//! [dependencies.clap]
|
||||||
//! version = "2"
|
//! version = "~2.19.0"
|
||||||
//! default-features = false
|
//! default-features = false
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -353,7 +342,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies.clap]
|
//! [dependencies.clap]
|
||||||
//! version = "2"
|
//! version = "~2.19.0"
|
||||||
//! default-features = false
|
//! default-features = false
|
||||||
//!
|
//!
|
||||||
//! # Cherry-pick the features you'd like to use
|
//! # Cherry-pick the features you'd like to use
|
||||||
|
@ -362,42 +351,163 @@
|
||||||
//!
|
//!
|
||||||
//! #### Opt-in features
|
//! #### 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
|
//! ### 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.
|
//! You can also find usage examples in the [examples/](examples) directory of this repo.
|
||||||
//!
|
//!
|
||||||
//! #### Video Tutorials
|
//! #### Video Tutorials
|
||||||
//!
|
//!
|
||||||
//! 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.
|
//! There's also the video tutorial series [Argument Parsing with Rust v2](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U).
|
||||||
//!
|
//!
|
||||||
//! *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.
|
//! These videos slowly trickle out as I finish them and currently a work in progress.
|
||||||
//!
|
//!
|
||||||
//! *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.
|
//! ## How to Contribute
|
||||||
//!
|
//!
|
||||||
//! ### For clap contributors
|
//! 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!
|
||||||
//!
|
//!
|
||||||
//! If contributing, you can run theese commands to test everything:
|
//! 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 :)
|
||||||
//!
|
//!
|
||||||
//! ```sh
|
//! 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 --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
|
//! Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the
|
||||||
//! to get style warnings and code smells; If you get one from code you think is fine,
|
//! prebuilt recipies. *Not* using `just` is prfeclty fine as well, it simply bundles commands
|
||||||
//! you can ignore it by prepending `#[cfg_attr(feature="lints", allow(lint_name))]`
|
//! automatically.
|
||||||
//! to the function or impl block.
|
|
||||||
//!
|
//!
|
||||||
//! If you are debugging (or just trying to understand the code) you can enable the
|
//! For example, to test the code, as above simply run:
|
||||||
//! "debug" feature which will trace function calls and brances in some parts of the code.
|
//!
|
||||||
|
//! ```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"
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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
|
//! ## 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"]
|
#![crate_type= "lib"]
|
||||||
#![deny(
|
#![deny(
|
||||||
|
|
|
@ -28,19 +28,13 @@ impl OsStrExt3 for OsStr {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
unsafe { mem::transmute(b) }
|
unsafe { mem::transmute(b) }
|
||||||
}
|
}
|
||||||
fn as_bytes(&self) -> &[u8] {
|
fn as_bytes(&self) -> &[u8] { self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8) }
|
||||||
self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsStrExt2 for OsStr {
|
impl OsStrExt2 for OsStr {
|
||||||
fn starts_with(&self, s: &[u8]) -> bool {
|
fn starts_with(&self, s: &[u8]) -> bool { self.as_bytes().starts_with(s) }
|
||||||
self.as_bytes().starts_with(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty_(&self) -> bool {
|
fn is_empty_(&self) -> bool { self.as_bytes().is_empty() }
|
||||||
self.as_bytes().is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_byte(&self, byte: u8) -> bool {
|
fn contains_byte(&self, byte: u8) -> bool {
|
||||||
for b in self.as_bytes() {
|
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..]))
|
(OsStr::from_bytes(&self.as_bytes()[..i]), OsStr::from_bytes(&self.as_bytes()[i..]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len_(&self) -> usize {
|
fn len_(&self) -> usize { self.as_bytes().len() }
|
||||||
self.as_bytes().len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn split(&self, b: u8) -> OsSplit {
|
fn split(&self, b: u8) -> OsSplit {
|
||||||
OsSplit {
|
OsSplit {
|
||||||
|
|
|
@ -111,7 +111,7 @@ impl<'a> UsageParser<'a> {
|
||||||
{
|
{
|
||||||
debugln!("fn=stop_at;");
|
debugln!("fn=stop_at;");
|
||||||
self.start = self.pos;
|
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>) {
|
fn short_or_long(&mut self, arg: &mut Arg<'a, 'a>) {
|
||||||
|
@ -192,14 +192,10 @@ impl<'a> UsageParser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn name_end(b: u8) -> bool {
|
fn name_end(b: u8) -> bool { b != b']' && b != b'>' }
|
||||||
b != b']' && b != b'>'
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn token(b: u8) -> bool {
|
fn token(b: u8) -> bool { b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'-' }
|
||||||
b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'-'
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn long_end(b: u8) -> bool {
|
fn long_end(b: u8) -> bool {
|
||||||
|
@ -207,9 +203,7 @@ fn long_end(b: u8) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn help_start(b: u8) -> bool {
|
fn help_start(b: u8) -> bool { b != b'\'' }
|
||||||
b != b'\''
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -990,7 +984,8 @@ mod test {
|
||||||
assert!(d.is_set(ArgSettings::Multiple));
|
assert!(d.is_set(ArgSettings::Multiple));
|
||||||
assert!(d.is_set(ArgSettings::TakesValue));
|
assert!(d.is_set(ArgSettings::TakesValue));
|
||||||
assert!(d.is_set(ArgSettings::Required));
|
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());
|
assert!(d.num_vals.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1199,7 +1194,8 @@ mod test {
|
||||||
let a = Arg::from_usage("[ñämê] --ôpt=[üñíčöĐ€] 'hælp'");
|
let a = Arg::from_usage("[ñämê] --ôpt=[üñíčöĐ€] 'hælp'");
|
||||||
assert_eq!(a.name, "ñämê");
|
assert_eq!(a.name, "ñämê");
|
||||||
assert_eq!(a.long, Some("ôpt"));
|
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"));
|
assert_eq!(a.help, Some("hælp"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,66 @@ fn require_delims() {
|
||||||
assert_eq!(m.value_of("file").unwrap(), "some");
|
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]
|
#[test]
|
||||||
#[cfg(feature="suggestions")]
|
#[cfg(feature="suggestions")]
|
||||||
fn did_you_mean() {
|
fn did_you_mean() {
|
||||||
|
|
|
@ -174,6 +174,23 @@ fn arg_require_group_3() {
|
||||||
|
|
||||||
// REQUIRED_UNLESS
|
// 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]
|
#[test]
|
||||||
fn required_unless() {
|
fn required_unless() {
|
||||||
let res = App::new("unlesstest")
|
let res = App::new("unlesstest")
|
||||||
|
|
Loading…
Reference in a new issue