mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 14:54:15 +00:00
Merged clap_derive using git-subtree
This commit is contained in:
commit
5e8f424dfe
133 changed files with 7317 additions and 0 deletions
13
clap_derive/.clog.toml
Normal file
13
clap_derive/.clog.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[clog]
|
||||
repository = "https://github.com/kbknapp/clap_derive"
|
||||
outfile = "CHANGELOG.md"
|
||||
from-latest-tag = true
|
||||
|
||||
[sections]
|
||||
Performance = ["perf"]
|
||||
Improvements = ["impr", "im", "imp"]
|
||||
Documentation = ["docs"]
|
||||
Deprecations = ["depr"]
|
||||
Examples = ["examples"]
|
||||
"New Settings" = ["setting", "settings"]
|
||||
"API Additions" = ["add", "api"]
|
84
clap_derive/.github/CONTRIBUTING.md
vendored
Normal file
84
clap_derive/.github/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
# How to Contribute
|
||||
|
||||
Contributions are always welcome! And there is a multitude of ways in which you can help depending on what you like to do, or are good at. Anything from documentation, code cleanup, issue completion, new features, you name it, even filing issues is contributing and greatly appreciated!
|
||||
|
||||
Another really great way to help is if you find an interesting, or helpful way in which to use `clap_derive`. 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 :)
|
||||
|
||||
### Testing Code
|
||||
|
||||
To test with all features both enabled and disabled, you can run these commands:
|
||||
|
||||
```sh
|
||||
$ cargo test
|
||||
```
|
||||
|
||||
Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the prebuilt recipes. *Not* using `just` is perfectly fine as well, it simply bundles commands automatically.
|
||||
|
||||
For example, to test the code, as above simply run:
|
||||
|
||||
```sh
|
||||
$ just run-tests
|
||||
```
|
||||
|
||||
From here on, I will list 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_derive` 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
|
||||
$ cargo +nightly build --features lints
|
||||
|
||||
# Or
|
||||
|
||||
$ just lint
|
||||
```
|
||||
|
||||
### Commit Messages
|
||||
|
||||
I use a [conventional](https://github.com/ajoslin/conventional-changelog/blob/a5505865ff3dd710cf757f50530e73ef0ca641da/conventions/angular.md) changelog format so I can update my changelog automatically using [clog](https://github.com/clog-tool/clog-cli)
|
||||
|
||||
* Please format your commit subject line using the following format: `TYPE(COMPONENT): MESSAGE` where `TYPE` is one of the following:
|
||||
- `api` - An addition to the API
|
||||
- `setting` - A new `AppSettings` variant
|
||||
- `feat` - A new feature of an existing API
|
||||
- `imp` - An improvement to an existing feature/API
|
||||
- `perf` - A performance improvement
|
||||
- `docs` - Changes to documentation only
|
||||
- `tests` - Changes to the testing framework or tests only
|
||||
- `fix` - A bug fix
|
||||
- `refactor` - Code functionality doesn't change, but underlying structure may
|
||||
- `style` - Stylistic changes only, no functionality changes
|
||||
- `wip` - A work in progress commit (Should typically be `git rebase`'ed away)
|
||||
- `chore` - Catch all or things that have to do with the build system, etc
|
||||
- `examples` - Changes to existing example, or a new example
|
||||
* The `COMPONENT` is optional, and may be a single file, directory, or logical component. Parenthesis can be omitted if you are opting not to use the `COMPONENT`.
|
||||
|
||||
### Tests and Documentation
|
||||
|
||||
1. Create tests for your changes
|
||||
2. **Ensure the tests are passing.** Run the tests (`cargo test`), alternatively `just run-tests` if you have `just` installed.
|
||||
3. **Optional** Run the lints (`cargo +nightly build --features lints`) (requires a nightly compiler), alternatively `just lint`
|
||||
4. Ensure your changes contain documentation if adding new APIs or features.
|
||||
|
||||
### Preparing the PR
|
||||
|
||||
1. `git rebase` into concise commits and remove `--fixup`s or `wip` commits (`git rebase -i HEAD~NUM` where `NUM` is number of commits back to start the rebase)
|
||||
2. Push your changes back to your fork (`git push origin $your-branch`)
|
||||
3. Create a pull request against `master`! (You can also create the pull request first, and we'll merge when ready. This a good way to discuss proposed changes.)
|
||||
|
||||
### Other ways to contribute
|
||||
|
||||
Another really great way to help is if you find an interesting, or helpful way in which to use `clap_derive`. 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 :)
|
23
clap_derive/.github/ISSUE_TEMPLATE.md
vendored
Normal file
23
clap_derive/.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
Please use the following template to assist with creating an issue and to ensure a speedy resolution. If an area is not applicable, feel free to delete the area or mark with `N/A`
|
||||
-->
|
||||
|
||||
### Rust Version
|
||||
|
||||
* Use the output of `rustc -V`
|
||||
|
||||
### Affected Version of clap and clap_derive
|
||||
|
||||
* Can be found in Cargo.lock of your project (i.e. `grep clap Cargo.lock`)
|
||||
|
||||
### Expected Behavior Summary
|
||||
|
||||
|
||||
### Actual Behavior Summary
|
||||
|
||||
|
||||
### Steps to Reproduce the issue
|
||||
|
||||
|
||||
### Sample Code or Link to Sample Code
|
||||
|
28
clap_derive/.gitignore
vendored
Normal file
28
clap_derive/.gitignore
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Compiled files
|
||||
*.o
|
||||
*.so
|
||||
*.rlib
|
||||
*.dll
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
|
||||
# Generated by Cargo
|
||||
/target/
|
||||
/clap-test/target/
|
||||
|
||||
# Cargo files
|
||||
Cargo.lock
|
||||
|
||||
# Temp files
|
||||
.*~
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*.bk
|
||||
*.orig
|
||||
|
||||
# Project files
|
||||
.vscode/*
|
||||
.idea/*
|
||||
clap-rs.iml
|
156
clap_derive/CHANGELOG.md
Normal file
156
clap_derive/CHANGELOG.md
Normal file
|
@ -0,0 +1,156 @@
|
|||
# v0.2.10 (2018-06-07)
|
||||
|
||||
* 1.21.0 is the minimum required rustc version by
|
||||
[@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.2.9 (2018-06-05)
|
||||
|
||||
* Fix a bug when using `flatten` by
|
||||
[@fbenkstein](https://github.com/fbenkstein)
|
||||
* Update syn, quote and proc_macro2 by
|
||||
[@TeXitoi](https://github.com/TeXitoi)
|
||||
* Fix a regression when there is multiple authors by
|
||||
[@windwardly](https://github.com/windwardly)
|
||||
|
||||
# v0.2.8 (2018-04-28)
|
||||
|
||||
* Add `StructOpt::from_iter_safe()`, which returns an `Error` instead of
|
||||
killing the program when it fails to parse, or parses one of the
|
||||
short-circuiting flags. ([#98](https://github.com/TeXitoi/structopt/pull/98)
|
||||
by [@quodlibetor](https://github.com/quodlibetor))
|
||||
* Allow users to enable `clap` features independently by
|
||||
[@Kerollmops](https://github.com/Kerollmops)
|
||||
* Fix a bug when flattening an enum
|
||||
([#103](https://github.com/TeXitoi/structopt/pull/103) by
|
||||
[@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.2.7 (2018-04-12)
|
||||
|
||||
* Add flattening, the insertion of options of another StructOpt struct
|
||||
into another ([#92](https://github.com/TeXitoi/structopt/pull/92))
|
||||
by [@birkenfeld](https://github.com/birkenfeld)
|
||||
* Fail compilation when using `default_value` or `required` with
|
||||
`Option` ([#88](https://github.com/TeXitoi/structopt/pull/88)) by
|
||||
[@Kerollmops](https://github.com/Kerollmops)
|
||||
|
||||
# v0.2.6 (2018-03-31)
|
||||
|
||||
* Fail compilation when using `default_value` or `required` with `bool` ([#80](https://github.com/TeXitoi/structopt/issues/80)) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Fix compilation with `#[deny(warnings)]` with the `!` type (https://github.com/rust-lang/rust/pull/49039#issuecomment-376398999) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Improve first example in the documentation ([#82](https://github.com/TeXitoi/structopt/issues/82)) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.2.5 (2018-03-07)
|
||||
|
||||
* Work around breakage when `proc-macro2`'s nightly feature is enabled. ([#77](https://github.com/Texitoi/structopt/pull/77) and [proc-macro2#67](https://github.com/alexcrichton/proc-macro2/issues/67)) by [@fitzgen](https://github.com/fitzgen)
|
||||
|
||||
# v0.2.4 (2018-02-25)
|
||||
|
||||
* Fix compilation with `#![deny(missig_docs]` ([#74](https://github.com/TeXitoi/structopt/issues/74)) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Fix [#76](https://github.com/TeXitoi/structopt/issues/76) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Re-licensed to Apache-2.0/MIT by [@CAD97](https://github.com/cad97)
|
||||
|
||||
# v0.2.3 (2018-02-16)
|
||||
|
||||
* An empty line in a doc comment will result in a double linefeed in the generated about/help call by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.2.2 (2018-02-12)
|
||||
|
||||
* Fix [#66](https://github.com/TeXitoi/structopt/issues/66) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.2.1 (2018-02-11)
|
||||
|
||||
* Fix a bug around enum tuple and the about message in the global help by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Fix [#65](https://github.com/TeXitoi/structopt/issues/65) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.2.0 (2018-02-10)
|
||||
|
||||
## Breaking changes
|
||||
|
||||
### Don't special case `u64` by [@SergioBenitez](https://github.com/SergioBenitez)
|
||||
|
||||
If you are using a `u64` in your struct to get the number of occurence of a flag, you should now add `parse(from_occurrences)` on the flag.
|
||||
|
||||
For example
|
||||
```rust
|
||||
#[clap(short = "v", long = "verbose")]
|
||||
verbose: u64,
|
||||
```
|
||||
must be changed by
|
||||
```rust
|
||||
#[clap(short = "v", long = "verbose", parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
```
|
||||
|
||||
This feature was surprising as shown in [#30](https://github.com/TeXitoi/structopt/issues/30). Using the `parse` feature seems much more natural.
|
||||
|
||||
### Change the signature of `Structopt::from_clap` to take its argument by reference by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
There was no reason to take the argument by value. Most of the StructOpt users will not be impacted by this change. If you are using `StructOpt::from_clap`, just add a `&` before the argument.
|
||||
|
||||
### Fail if attributes are not used by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
StructOpt was quite fuzzy in its attribute parsing: it was only searching for interresting things, e. g. something like `#[clap(foo(bar))]` was accepted but not used. It now fails the compilation.
|
||||
|
||||
You should have nothing to do here. This breaking change may highlight some missuse that can be bugs.
|
||||
|
||||
In future versions, if there is cases that are not highlighed, they will be considerated as bugs, not breaking changes.
|
||||
|
||||
### Use `raw()` wrapping instead of `_raw` suffixing by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
The syntax of raw attributes is changed to improve the syntax.
|
||||
|
||||
You have to change `foo_raw = "bar", baz_raw = "foo"` by `raw(foo = "bar", baz = "foo")` or `raw(foo = "bar"), raw(baz = "foo")`.
|
||||
|
||||
## New features
|
||||
|
||||
* Add `parse(from_occurrences)` parser by [@SergioBenitez](https://github.com/SergioBenitez)
|
||||
* Support 1-uple enum variant as subcommand by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* structopt-derive crate is now an implementation detail, structopt reexport the custom derive macro by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Add the `StructOpt::from_iter` method by [@Kerollmops](https://github.com/Kerollmops)
|
||||
|
||||
## Documentation
|
||||
|
||||
* Improve doc by [@bestouff](https://github.com/bestouff)
|
||||
* All the documentation is now on the structopt crate by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.1.7 (2018-01-23)
|
||||
|
||||
* Allow opting out of clap default features by [@ski-csis](https://github.com/ski-csis)
|
||||
|
||||
# v0.1.6 (2017-11-25)
|
||||
|
||||
* Improve documentation by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Fix bug [#31](https://github.com/TeXitoi/structopt/issues/31) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.1.5 (2017-11-14)
|
||||
|
||||
* Fix a bug with optional subsubcommand and Enum by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.1.4 (2017-11-09)
|
||||
|
||||
* Implement custom string parser from either `&str` or `&OsStr` by [@kennytm](https://github.com/kennytm)
|
||||
|
||||
# v0.1.3 (2017-11-01)
|
||||
|
||||
* Improve doc by [@TeXitoi](https://github.com/TeXitoi)
|
||||
|
||||
# v0.1.2 (2017-11-01)
|
||||
|
||||
* Fix bugs [#24](https://github.com/TeXitoi/structopt/issues/24) and [#25](https://github.com/TeXitoi/structopt/issues/25) by [@TeXitoi](https://github.com/TeXitoi)
|
||||
* Support of methods with something else that a string as argument thanks to `_raw` suffix by [@Flakebi](https://github.com/Flakebi)
|
||||
|
||||
# v0.1.1 (2017-09-22)
|
||||
|
||||
* Better formating of multiple authors by [@killercup](https://github.com/killercup)
|
||||
|
||||
# v0.1.0 (2017-07-17)
|
||||
|
||||
* Subcommand support by [@williamyaoh](https://github.com/williamyaoh)
|
||||
|
||||
# v0.0.5 (2017-06-16)
|
||||
|
||||
* Using doc comment to populate help by [@killercup](https://github.com/killercup)
|
||||
|
||||
# v0.0.3 (2017-02-11)
|
||||
|
||||
* First version with flags, arguments and options support by [@TeXitoi](https://github.com/TeXitoi)
|
19
clap_derive/CONTRIBUTORS.md
Normal file
19
clap_derive/CONTRIBUTORS.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Contributors to `clap_derive`
|
||||
|
||||
| [<img alt="TeXitoi" src="https://avatars0.githubusercontent.com/u/5787066?v=4&s=117" width="117">](https://github.com/TeXitoi) | [<img alt="williamyaoh" src="https://avatars1.githubusercontent.com/u/5668019?v=4&s=117" width="117">](https://github.com/williamyaoh) | [<img alt="kbknapp" src="https://avatars1.githubusercontent.com/u/6942134?v=4&s=117" width="117">](https://github.com/kbknapp) | [<img alt="killercup" src="https://avatars1.githubusercontent.com/u/20063?v=4&s=117" width="117">](https://github.com/killercup) | [<img alt="Kerollmops" src="https://avatars0.githubusercontent.com/u/3610253?v=4&s=117" width="117">](https://github.com/Kerollmops) | [<img alt="Hoverbear" src="https://avatars3.githubusercontent.com/u/130903?v=4&s=117" width="117">](https://github.com/Hoverbear) |
|
||||
| :----------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [TeXitoi](https://github.com/TeXitoi) | [williamyaoh](https://github.com/williamyaoh) | [kbknapp](https://github.com/kbknapp) | [killercup](https://github.com/killercup) | [Kerollmops](https://github.com/Kerollmops) | [Hoverbear](https://github.com/Hoverbear) |
|
||||
|
||||
| [<img alt="SergioBenitez" src="https://avatars3.githubusercontent.com/u/1480321?v=4&s=117" width="117">](https://github.com/SergioBenitez) | [<img alt="quodlibetor" src="https://avatars3.githubusercontent.com/u/277161?v=4&s=117" width="117">](https://github.com/quodlibetor) | [<img alt="bruceadams" src="https://avatars0.githubusercontent.com/u/225823?v=4&s=117" width="117">](https://github.com/bruceadams) | [<img alt="CAD97" src="https://avatars1.githubusercontent.com/u/5992217?v=4&s=117" width="117">](https://github.com/CAD97) | [<img alt="fbenkstein" src="https://avatars2.githubusercontent.com/u/7721507?v=4&s=117" width="117">](https://github.com/fbenkstein) | [<img alt="windwardly" src="https://avatars3.githubusercontent.com/u/24761347?v=4&s=117" width="117">](https://github.com/windwardly) |
|
||||
| :----------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [SergioBenitez](https://github.com/SergioBenitez) | [quodlibetor](https://github.com/quodlibetor) | [bruceadams](https://github.com/bruceadams) | [CAD97](https://github.com/CAD97) | [fbenkstein](https://github.com/fbenkstein) | [windwardly](https://github.com/windwardly) |
|
||||
|
||||
| [<img alt="fitzgen" src="https://avatars2.githubusercontent.com/u/74571?v=4&s=117" width="117">](https://github.com/fitzgen) | [<img alt="Flakebi" src="https://avatars2.githubusercontent.com/u/6499211?v=4&s=117" width="117">](https://github.com/Flakebi) | [<img alt="ski-csis" src="https://avatars1.githubusercontent.com/u/1162493?v=4&s=117" width="117">](https://github.com/ski-csis) | [<img alt="tvincent2" src="https://avatars0.githubusercontent.com/u/2185278?v=4&s=117" width="117">](https://github.com/tvincent2) | [<img alt="tshepang" src="https://avatars0.githubusercontent.com/u/588486?v=4&s=117" width="117">](https://github.com/tshepang) | [<img alt="bestouff" src="https://avatars0.githubusercontent.com/u/1163188?v=4&s=117" width="117">](https://github.com/bestouff) |
|
||||
| :--------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [fitzgen](https://github.com/fitzgen) | [Flakebi](https://github.com/Flakebi) | [ski-csis](https://github.com/ski-csis) | [tvincent2](https://github.com/tvincent2) | [tshepang](https://github.com/tshepang) | [bestouff](https://github.com/bestouff) |
|
||||
|
||||
| [<img alt="hcpl" src="https://avatars3.githubusercontent.com/u/17436405?v=4&s=117" width="117">](https://github.com/hcpl) | [<img alt="kennytm" src="https://avatars1.githubusercontent.com/u/103023?v=4&s=117" width="117">](https://github.com/kennytm) | [<img alt="spease" src="https://avatars1.githubusercontent.com/u/2825204?v=4&s=117" width="117">](https://github.com/spease) | [<img alt="birkenfeld" src="https://avatars0.githubusercontent.com/u/144359?v=4&s=117" width="117">](https://github.com/birkenfeld) |
|
||||
| :-----------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [hcpl](https://github.com/hcpl) | [kennytm](https://github.com/kennytm) | [spease](https://github.com/spease) | [birkenfeld](https://github.com/birkenfeld) |
|
||||
|
||||
This list was generated by [mgechev/github-contributors-list](https://github.com/mgechev/github-contributors-list)
|
42
clap_derive/Cargo.toml
Normal file
42
clap_derive/Cargo.toml
Normal file
|
@ -0,0 +1,42 @@
|
|||
[package]
|
||||
name = "clap_derive"
|
||||
version = "0.3.0"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Guillaume Pinot <texitoi@texitoi.eu>",
|
||||
"Kevin K. <kbknapp@gmail.com>",
|
||||
"hoverbear <andrew@hoverbear.org>"
|
||||
]
|
||||
description = "Parse command line argument by defining a struct, derive crate."
|
||||
documentation = "https://docs.rs/clap_derive"
|
||||
repository = "https://github.com/clap-rs/clap_derive"
|
||||
keywords = ["clap", "cli", "derive", "proc_macro", "parse"]
|
||||
categories = ["command-line-interface", "development-tools::procedural-macro-helpers"]
|
||||
license = "Apache-2.0/MIT"
|
||||
readme = "README.md"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "clap-rs/clap_derive" }
|
||||
appveyor = { repository = "https://github.com/clap-rs/clap_derive", service = "github" }
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1", features = ["full"] }
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
heck = "0.3.0"
|
||||
proc-macro-error = "0.4.3"
|
||||
|
||||
[dev-dependencies]
|
||||
clap = { git = "https://github.com/clap-rs/clap", branch = "master"} # ONLY FOR INITIAL DEVELOPMENT...change to real crates.io ver for rlease!
|
||||
trybuild = "1.0.5"
|
||||
rustversion = "0.1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
nightly = []
|
||||
lints = []
|
||||
debug = []
|
||||
doc = []
|
201
clap_derive/LICENSE-APACHE
Normal file
201
clap_derive/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
21
clap_derive/LICENSE-MIT
Normal file
21
clap_derive/LICENSE-MIT
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
169
clap_derive/README.md
Normal file
169
clap_derive/README.md
Normal file
|
@ -0,0 +1,169 @@
|
|||
# Work in Progress
|
||||
|
||||
This crate is currently a work in progress and not meant to be used. Please use [`structopt`](https://github.com/TeXitoi/structopt)
|
||||
while this crate is being built.
|
||||
|
||||
# clap_derive[![Build status](https://travis-ci.org/clap-rs/clap_derive.svg?branch=master)](https://travis-ci.org/clap-rs/clap_derive) [![](https://img.shields.io/crates/v/clap_derive.svg)](https://crates.io/crates/clap_derive) [![](https://docs.rs/clap_derive/badge.svg)](https://docs.rs/clap_derive)
|
||||
|
||||
Parse command line argument by defining a struct. It combines [structopt](https://github.com/TeXitoi/structopt) and [clap](https://crates.io/crates/clap) into a single experience. This crate is used by clap, and not meant to be used directly by
|
||||
consumers.
|
||||
|
||||
## Documentation
|
||||
|
||||
Find it on [Docs.rs](https://docs.rs/clap_derive). You can also check the [examples](https://github.com/clap-rs/clap_derive/tree/master/examples) and the [changelog](https://github.com/clap-rs/clap_derive/blob/master/CHANGELOG.md).
|
||||
|
||||
## Example
|
||||
|
||||
Add `clap` to your dependencies of your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
clap = "3"
|
||||
```
|
||||
|
||||
And then, in your rust file:
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use clap::Clap;
|
||||
|
||||
/// A basic example
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
// A flag, true if used in the command line. Note doc comment will
|
||||
// be used for the help message of the flag. The name of the
|
||||
// argument will be, by default, based on the name of the field.
|
||||
/// Activate debug mode
|
||||
#[clap(short, long)]
|
||||
debug: bool,
|
||||
|
||||
// The number of occurrences of the `v/verbose` flag
|
||||
/// Verbose mode (-v, -vv, -vvv, etc.)
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: u8,
|
||||
|
||||
/// Set speed
|
||||
#[clap(short, long, default_value = "42")]
|
||||
speed: f64,
|
||||
|
||||
/// Output file
|
||||
#[clap(short, long, parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
|
||||
// the long option will be translated by default to kebab case,
|
||||
// i.e. `--nb-cars`.
|
||||
/// Number of cars
|
||||
#[clap(short = "c", long)]
|
||||
nb_cars: Option<i32>,
|
||||
|
||||
/// admin_level to consider
|
||||
#[clap(short, long)]
|
||||
level: Vec<String>,
|
||||
|
||||
/// Files to process
|
||||
#[clap(name = "FILE", parse(from_os_str))]
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:#?}", opt);
|
||||
}
|
||||
```
|
||||
|
||||
Using this example:
|
||||
```
|
||||
$ ./basic
|
||||
error: The following required arguments were not provided:
|
||||
--output <output>
|
||||
|
||||
USAGE:
|
||||
basic --output <output> --speed <speed>
|
||||
|
||||
For more information try --help
|
||||
$ ./basic --help
|
||||
basic 0.3.0
|
||||
Guillaume Pinot <texitoi@texitoi.eu>, others
|
||||
A basic example
|
||||
|
||||
USAGE:
|
||||
basic [FLAGS] [OPTIONS] --output <output> [--] [file]...
|
||||
|
||||
ARGS:
|
||||
<FILE>... Files to process
|
||||
|
||||
FLAGS:
|
||||
-d, --debug Activate debug mode
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Verbose mode (-v, -vv, -vvv, etc.)
|
||||
|
||||
OPTIONS:
|
||||
-l, --level <level>... admin_level to consider
|
||||
-c, --nb-cars <nb-cars> Number of cars
|
||||
-o, --output <output> Output file
|
||||
-s, --speed <speed> Set speed [default: 42]
|
||||
|
||||
ARGS:
|
||||
<file>... Files to process
|
||||
$ ./basic -o foo.txt
|
||||
Opt {
|
||||
debug: false,
|
||||
verbose: 0,
|
||||
speed: 42.0,
|
||||
output: "foo.txt",
|
||||
nb_cars: None,
|
||||
level: [],
|
||||
files: [],
|
||||
}
|
||||
$ ./basic -o foo.txt -dvvvs 1337 -l alice -l bob --nb-cars 4 bar.txt baz.txt
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: 3,
|
||||
speed: 1337.0,
|
||||
output: "foo.txt",
|
||||
nb_cars: Some(
|
||||
4,
|
||||
),
|
||||
level: [
|
||||
"alice",
|
||||
"bob",
|
||||
],
|
||||
files: [
|
||||
"bar.txt",
|
||||
"baz.txt",
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## clap_derive rustc version policy
|
||||
|
||||
- Minimum rustc version modification must be specified in the [changelog](https://github.com/clap-rs/clap_derive/blob/master/CHANGELOG.md) and in the [travis configuration](https://github.com/clap-rs/clap_derive/blob/master/.travis.yml).
|
||||
- Contributors can increment minimum rustc version without any justification if the new version is required by the latest version of one of clap_derive's depedencies (`cargo update` will not fail on clap_derive).
|
||||
- Contributors can increment minimum rustc version if the library user experience is improved.
|
||||
|
||||
## Why
|
||||
|
||||
I've (@TeXitoi) used [docopt](https://crates.io/crates/docopt) for a long time (pre rust 1.0). I really like the fact that you have a structure with the parsed argument: no need to convert `String` to `f64`, no useless `unwrap`. But on the other hand, I don't like to write by hand the usage string. That's like going back to the golden age of WYSIWYG editors. Field naming is also a bit artificial.
|
||||
|
||||
Today, the new standard to read command line arguments in Rust is [clap](https://crates.io/crates/clap). This library is so feature full! But I think there is one downside: even if you can validate argument and expressing that an argument is required, you still need to transform something looking like a hashmap of string vectors to something useful for your application.
|
||||
|
||||
Now, there is stable custom derive. Thus I can add to clap the automatic conversion that I miss. Here is the result.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
23
clap_derive/appveyor.yml
Normal file
23
clap_derive/appveyor.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-gnu
|
||||
RUST_VERSION: stable
|
||||
- TARGET: x86_64-pc-windows-gnu
|
||||
RUST_VERSION: nightly
|
||||
|
||||
install:
|
||||
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
|
||||
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
- rustc -Vv
|
||||
- cargo -V
|
||||
|
||||
# Building is done in the test phase, so we disable Appveyor's build phase.
|
||||
build: false
|
||||
|
||||
cache:
|
||||
- C:\Users\appveyor\.cargo\registry
|
||||
- target
|
||||
|
||||
test_script:
|
||||
- cargo test
|
78
clap_derive/examples/README.md
Normal file
78
clap_derive/examples/README.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Collection of examples "how to use `clap_derive`"
|
||||
|
||||
### [Help on the bottom](after_help.rs)
|
||||
|
||||
How to append a postscript to the help message generated.
|
||||
|
||||
### [At least N](at_least_two.rs)
|
||||
|
||||
How to require presence of at least N values, like `val1 val2 ... valN ... valM`.
|
||||
|
||||
### [Basic](basic.rs)
|
||||
|
||||
A basic example how to use `clap_derive`.
|
||||
|
||||
### [Deny missing docs](deny_missing_docs.rs)
|
||||
|
||||
**This is not an example but a test**, it should be moved to `tests` folder
|
||||
as soon as [this](https://github.com/rust-lang/rust/issues/24584) is fixed (if ever).
|
||||
|
||||
### [Doc comments](doc_comments.rs)
|
||||
|
||||
How to use doc comments in place of `help/long_help`.
|
||||
|
||||
### [Enums as arguments](enum_in_args.rs)
|
||||
|
||||
How to use `arg_enum!` with `clap_derive`.
|
||||
|
||||
### [Arguments of subcommands in separate `struct`](enum_tuple.rs)
|
||||
|
||||
How to extract subcommands' args into external structs.
|
||||
|
||||
### [Environment variables](env.rs)
|
||||
|
||||
How to use environment variable fallback an how it interacts with `default_value`.
|
||||
|
||||
### [Advanced](example.rs)
|
||||
|
||||
Somewhat complex example of usage of `clap_derive`.
|
||||
|
||||
### [Flatten](flatten.rs)
|
||||
|
||||
How to use `#[clap(flatten)]`
|
||||
|
||||
### [Git](git.rs)
|
||||
|
||||
Pseudo-`git` example, shows how to use subcommands and how to document them.
|
||||
|
||||
### [Groups](group.rs)
|
||||
|
||||
Using `clap::Arg::group` with `clap`.
|
||||
|
||||
### [`key=value` pairs](keyvalue.rs)
|
||||
|
||||
How to parse `key=value` pairs.
|
||||
|
||||
### [`--no-*` flags](negative_flag.rs)
|
||||
|
||||
How to add `no-thing` flag which is `true` by default and `false` if passed.
|
||||
|
||||
### [No version](no_version.rs)
|
||||
|
||||
How to completely remove version.
|
||||
|
||||
### [Rename all](rename_all.rs)
|
||||
|
||||
How `#[clap(rename_all)]` works.
|
||||
|
||||
### [Skip](skip.rs)
|
||||
|
||||
How to use `#[clap(skip)]`.
|
||||
|
||||
### [Aliases](subcommand_aliases.rs)
|
||||
|
||||
How to use aliases
|
||||
|
||||
### [`true` or `false`](true_or_false.rs)
|
||||
|
||||
How to express "`"true"` or `"false"` argument.
|
19
clap_derive/examples/after_help.rs
Normal file
19
clap_derive/examples/after_help.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
//! How to append a postscript to the help message generated.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
/// I am a program and I do things.
|
||||
///
|
||||
/// Sometimes they even work.
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(after_help = "Beware `-d`, dragons be here")]
|
||||
struct Opt {
|
||||
/// Release the dragon.
|
||||
#[clap(short)]
|
||||
dragon: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
28
clap_derive/examples/arg_enum_basic.rs
Normal file
28
clap_derive/examples/arg_enum_basic.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// #[macro_use]
|
||||
// extern crate clap;
|
||||
|
||||
// use clap::{App, Arg};
|
||||
|
||||
fn main() {}
|
||||
|
||||
// #[derive(ArgEnum, Debug)]
|
||||
// enum ArgChoice {
|
||||
// Foo,
|
||||
// Bar,
|
||||
// Baz,
|
||||
// }
|
||||
|
||||
// fn main() {
|
||||
// let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||
// .arg(
|
||||
// Arg::with_name("arg")
|
||||
// .required(true)
|
||||
// .takes_value(true)
|
||||
// .possible_values(&ArgChoice::variants()),
|
||||
// )
|
||||
// .get_matches();
|
||||
|
||||
// let t = value_t!(matches.value_of("arg"), ArgChoice).unwrap_or_else(|e| e.exit());
|
||||
|
||||
// println!("{:?}", t);
|
||||
// }
|
28
clap_derive/examples/arg_enum_case_sensitive.rs
Normal file
28
clap_derive/examples/arg_enum_case_sensitive.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
fn main() {}
|
||||
// #[macro_use]
|
||||
// extern crate clap;
|
||||
|
||||
// use clap::{App, Arg};
|
||||
|
||||
// #[derive(ArgEnum, Debug)]
|
||||
// #[case_sensitive]
|
||||
// enum ArgChoice {
|
||||
// Foo,
|
||||
// Bar,
|
||||
// Baz,
|
||||
// }
|
||||
|
||||
// fn main() {
|
||||
// let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||
// .arg(
|
||||
// Arg::with_name("arg")
|
||||
// .required(true)
|
||||
// .takes_value(true)
|
||||
// .possible_values(&ArgChoice::variants()),
|
||||
// )
|
||||
// .get_matches();
|
||||
|
||||
// let t = value_t!(matches.value_of("arg"), ArgChoice).unwrap_or_else(|e| e.exit());
|
||||
|
||||
// println!("{:?}", t);
|
||||
// }
|
15
clap_derive/examples/at_least_two.rs
Normal file
15
clap_derive/examples/at_least_two.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//! How to require presence of at least N values,
|
||||
//! like `val1 val2 ... valN ... valM`.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct Opt {
|
||||
#[clap(required = true, min_values = 2)]
|
||||
foos: Vec<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
48
clap_derive/examples/basic.rs
Normal file
48
clap_derive/examples/basic.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
//! A somewhat comprehensive example of a typical `StructOpt` usage.use
|
||||
|
||||
use clap::Clap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// A basic example
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
// A flag, true if used in the command line. Note doc comment will
|
||||
// be used for the help message of the flag. The name of the
|
||||
// argument will be, by default, based on the name of the field.
|
||||
/// Activate debug mode
|
||||
#[clap(short, long)]
|
||||
debug: bool,
|
||||
|
||||
// The number of occurrences of the `v/verbose` flag
|
||||
/// Verbose mode (-v, -vv, -vvv, etc.)
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: u8,
|
||||
|
||||
/// Set speed
|
||||
#[clap(short, long, default_value = "42")]
|
||||
speed: f64,
|
||||
|
||||
/// Output file
|
||||
#[clap(short, long, parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
|
||||
// the long option will be translated by default to kebab case,
|
||||
// i.e. `--nb-cars`.
|
||||
/// Number of cars
|
||||
#[clap(short = "c", long)]
|
||||
nb_cars: Option<i32>,
|
||||
|
||||
/// admin_level to consider
|
||||
#[clap(short, long)]
|
||||
level: Vec<String>,
|
||||
|
||||
/// Files to process
|
||||
#[clap(name = "FILE", parse(from_os_str))]
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:#?}", opt);
|
||||
}
|
54
clap_derive/examples/deny_missing_docs.rs
Normal file
54
clap_derive/examples/deny_missing_docs.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
//! A test to check that clap_derive compiles with deny(missing_docs)
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
/// The options
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub struct Opt {
|
||||
#[clap(short)]
|
||||
verbose: bool,
|
||||
#[clap(subcommand)]
|
||||
cmd: Option<Cmd>,
|
||||
}
|
||||
|
||||
/// Some subcommands
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub enum Cmd {
|
||||
/// command A
|
||||
A,
|
||||
/// command B
|
||||
B {
|
||||
/// Alice?
|
||||
#[clap(short)]
|
||||
alice: bool,
|
||||
},
|
||||
/// command C
|
||||
C(COpt),
|
||||
}
|
||||
|
||||
/// The options for C
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub struct COpt {
|
||||
#[clap(short)]
|
||||
bob: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", Opt::parse());
|
||||
}
|
74
clap_derive/examples/doc_comments.rs
Normal file
74
clap_derive/examples/doc_comments.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
//! How to use doc comments in place of `help/long_help`.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
/// A basic example for the usage of doc comments as replacement
|
||||
/// of the arguments `help`, `long_help`, `about` and `long_about`.
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
/// Just use doc comments to replace `help`, `long_help`,
|
||||
/// `about` or `long_about` input.
|
||||
#[clap(short, long)]
|
||||
first_flag: bool,
|
||||
|
||||
/// Split between `help` and `long_help`.
|
||||
///
|
||||
/// In the previous case clap is going to present
|
||||
/// the whole comment both as text for the `help` and the
|
||||
/// `long_help` argument.
|
||||
///
|
||||
/// But if the doc comment is formatted like this example
|
||||
/// -- with an empty second line splitting the heading and
|
||||
/// the rest of the comment -- only the first line is used
|
||||
/// as `help` argument. The `long_help` argument will still
|
||||
/// contain the whole comment.
|
||||
///
|
||||
/// ## Attention
|
||||
///
|
||||
/// Any formatting next to empty lines that could be used
|
||||
/// inside a doc comment is currently not preserved. If
|
||||
/// lists or other well formatted content is required it is
|
||||
/// necessary to use the related clap argument with a
|
||||
/// raw string as shown on the `third_flag` description.
|
||||
#[clap(short, long)]
|
||||
second_flag: bool,
|
||||
|
||||
#[clap(
|
||||
short,
|
||||
long,
|
||||
long_help = r"This is a raw string.
|
||||
|
||||
It can be used to pass well formatted content (e.g. lists or source
|
||||
code) in the description:
|
||||
|
||||
- first example list entry
|
||||
- second example list entry
|
||||
"
|
||||
)]
|
||||
third_flag: bool,
|
||||
|
||||
#[clap(subcommand)]
|
||||
sub_command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap()]
|
||||
enum SubCommand {
|
||||
/// The same rules described previously for flags. Are
|
||||
/// also true for in regards of sub-commands.
|
||||
First,
|
||||
|
||||
/// Applicable for both `about` an `help`.
|
||||
///
|
||||
/// The formatting rules described in the comment of the
|
||||
/// `second_flag` also apply to the description of
|
||||
/// sub-commands which is normally given through the `about`
|
||||
/// and `long_about` arguments.
|
||||
Second,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
25
clap_derive/examples/enum_in_args.rs
Normal file
25
clap_derive/examples/enum_in_args.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
//! How to use `arg_enum!` with `StructOpt`.
|
||||
// TODO: make it work
|
||||
fn main() {}
|
||||
// use clap::Clap;
|
||||
|
||||
// arg_enum! {
|
||||
// #[derive(Debug)]
|
||||
// enum Baz {
|
||||
// Foo,
|
||||
// Bar,
|
||||
// FooBar
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[derive(Clap, Debug)]
|
||||
// struct Opt {
|
||||
// /// Important argument.
|
||||
// #[clap(possible_values = &Baz::variants(), case_insensitive = true)]
|
||||
// i: Baz,
|
||||
// }
|
||||
|
||||
// fn main() {
|
||||
// let opt = Opt::parse();
|
||||
// println!("{:?}", opt);
|
||||
// }
|
26
clap_derive/examples/enum_tuple.rs
Normal file
26
clap_derive/examples/enum_tuple.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
//! How to extract subcommands' args into external structs.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
pub struct Foo {
|
||||
pub bar: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
pub enum Command {
|
||||
#[clap(name = "foo")]
|
||||
Foo(Foo),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
#[clap(name = "classify")]
|
||||
pub struct ApplicationArguments {
|
||||
#[clap(subcommand)]
|
||||
pub command: Command,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = ApplicationArguments::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
26
clap_derive/examples/env.rs
Normal file
26
clap_derive/examples/env.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
//! How to use environment variable fallback an how it
|
||||
//! interacts with `default_value`.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
/// Example for allowing to specify options via environment variables.
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "env")]
|
||||
struct Opt {
|
||||
// Use `env` to enable specifying the option with an environment
|
||||
// variable. Command line arguments take precedence over env.
|
||||
/// URL for the API server
|
||||
#[clap(long, env = "API_URL")]
|
||||
api_url: String,
|
||||
|
||||
// The default value is used if neither argument nor environment
|
||||
// variable is specified.
|
||||
/// Number of retries
|
||||
#[clap(long, env = "RETRIES", default_value = "5")]
|
||||
retries: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:#?}", opt);
|
||||
}
|
54
clap_derive/examples/example.rs
Normal file
54
clap_derive/examples/example.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
//! Somewhat complex example of usage of structopt.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "example")]
|
||||
/// An example of clap_derive usage.
|
||||
struct Opt {
|
||||
// A flag, true if used in the command line.
|
||||
#[clap(short, long)]
|
||||
/// Activate debug mode
|
||||
debug: bool,
|
||||
|
||||
// An argument of type float, with a default value.
|
||||
#[clap(short, long, default_value = "42")]
|
||||
/// Set speed
|
||||
speed: f64,
|
||||
|
||||
// Needed parameter, the first on the command line.
|
||||
/// Input file
|
||||
input: String,
|
||||
|
||||
// An optional parameter, will be `None` if not present on the
|
||||
// command line.
|
||||
/// Output file, stdout if not present
|
||||
output: Option<String>,
|
||||
|
||||
// An optional parameter with optional value, will be `None` if
|
||||
// not present on the command line, will be `Some(None)` if no
|
||||
// argument is provided (i.e. `--log`) and will be
|
||||
// `Some(Some(String))` if argument is provided (e.g. `--log
|
||||
// log.txt`).
|
||||
#[clap(long)]
|
||||
#[allow(clippy::option_option)]
|
||||
/// Log file, stdout if no file, no logging if not present
|
||||
log: Option<Option<String>>,
|
||||
|
||||
// An optional list of values, will be `None` if not present on
|
||||
// the command line, will be `Some(vec![])` if no argument is
|
||||
// provided (i.e. `--optv`) and will be `Some(Some(String))` if
|
||||
// argument list is provided (e.g. `--optv a b c`).
|
||||
#[clap(long)]
|
||||
optv: Option<Vec<String>>,
|
||||
|
||||
// Skipped option: it won't be parsed and will be filled with the
|
||||
// default value for its type (in this case it'll be an empty string).
|
||||
#[clap(skip)]
|
||||
skipped: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
29
clap_derive/examples/flatten.rs
Normal file
29
clap_derive/examples/flatten.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
//! How to use flattening.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct Cmdline {
|
||||
/// switch verbosity on
|
||||
#[clap(short)]
|
||||
verbose: bool,
|
||||
|
||||
#[clap(flatten)]
|
||||
daemon_opts: DaemonOpts,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct DaemonOpts {
|
||||
/// daemon user
|
||||
#[clap(short)]
|
||||
user: String,
|
||||
|
||||
/// daemon group
|
||||
#[clap(short)]
|
||||
group: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Cmdline::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
35
clap_derive/examples/git.rs
Normal file
35
clap_derive/examples/git.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//! `git.rs` serves as a demonstration of how to use subcommands,
|
||||
//! as well as a demonstration of adding documentation to subcommands.
|
||||
//! Documentation can be added either through doc comments or
|
||||
//! `help`/`about` attributes.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "git")]
|
||||
/// the stupid content tracker
|
||||
enum Opt {
|
||||
/// fetch branches from remote repository
|
||||
Fetch {
|
||||
#[clap(long)]
|
||||
dry_run: bool,
|
||||
#[clap(long)]
|
||||
all: bool,
|
||||
#[clap(default_value = "origin")]
|
||||
repository: String,
|
||||
},
|
||||
#[clap(override_help = "add files to the staging area")]
|
||||
Add {
|
||||
#[clap(short)]
|
||||
interactive: bool,
|
||||
#[clap(short)]
|
||||
all: bool,
|
||||
files: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = Opt::parse();
|
||||
|
||||
println!("{:?}", matches);
|
||||
}
|
31
clap_derive/examples/group.rs
Normal file
31
clap_derive/examples/group.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
//! How to use `clap::Arg::group`
|
||||
|
||||
use clap::{ArgGroup, Clap};
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(group = ArgGroup::with_name("verb").required(true))]
|
||||
struct Opt {
|
||||
/// Set a custom HTTP verb
|
||||
#[clap(long, group = "verb")]
|
||||
method: Option<String>,
|
||||
/// HTTP GET
|
||||
#[clap(long, group = "verb")]
|
||||
get: bool,
|
||||
/// HTTP HEAD
|
||||
#[clap(long, group = "verb")]
|
||||
head: bool,
|
||||
/// HTTP POST
|
||||
#[clap(long, group = "verb")]
|
||||
post: bool,
|
||||
/// HTTP PUT
|
||||
#[clap(long, group = "verb")]
|
||||
put: bool,
|
||||
/// HTTP DELETE
|
||||
#[clap(long, group = "verb")]
|
||||
delete: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
36
clap_derive/examples/keyvalue.rs
Normal file
36
clap_derive/examples/keyvalue.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
//! How to parse "key=value" pairs with structopt.
|
||||
|
||||
use clap::Clap;
|
||||
use std::error::Error;
|
||||
|
||||
/// Parse a single key-value pair
|
||||
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error>>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
T::Err: Error + 'static,
|
||||
U: std::str::FromStr,
|
||||
U::Err: Error + 'static,
|
||||
{
|
||||
let pos = s
|
||||
.find('=')
|
||||
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?;
|
||||
Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct Opt {
|
||||
// number_of_values = 1 forces the user to repeat the -D option for each key-value pair:
|
||||
// my_program -D a=1 -D b=2
|
||||
// Without number_of_values = 1 you can do:
|
||||
// my_program -D a=1 b=2
|
||||
// but this makes adding an argument after the values impossible:
|
||||
// my_program -D a=1 -D b=2 my_input_file
|
||||
// becomes invalid.
|
||||
#[clap(short = "D", parse(try_from_str = parse_key_val), number_of_values = 1)]
|
||||
defines: Vec<(String, i32)>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
15
clap_derive/examples/negative_flag.rs
Normal file
15
clap_derive/examples/negative_flag.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//! How to add `no-thing` flag which is `true` by default and
|
||||
//! `false` if passed.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
struct Opt {
|
||||
#[clap(long = "no-verbose", parse(from_flag = std::ops::Not::not))]
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cmd = Opt::parse();
|
||||
println!("{:#?}", cmd);
|
||||
}
|
16
clap_derive/examples/no_version.rs
Normal file
16
clap_derive/examples/no_version.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
//! How to completely remove version.
|
||||
|
||||
use clap::{AppSettings, Clap};
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(
|
||||
name = "no_version",
|
||||
no_version,
|
||||
global_setting = AppSettings::DisableVersion
|
||||
)]
|
||||
struct Opt {}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
74
clap_derive/examples/rename_all.rs
Normal file
74
clap_derive/examples/rename_all.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
//! Example on how the `rename_all` parameter works.
|
||||
//!
|
||||
//! `rename_all` can be used to override the casing style used during argument
|
||||
//! generation. By default the `kebab-case` style will be used but there are a wide
|
||||
//! variety of other styles available.
|
||||
//!
|
||||
//! ## Supported styles overview:
|
||||
//!
|
||||
//! - **Camel Case**: Indicate word boundaries with uppercase letter, excluding
|
||||
//! the first word.
|
||||
//! - **Kebab Case**: Keep all letters lowercase and indicate word boundaries
|
||||
//! with hyphens.
|
||||
//! - **Pascal Case**: Indicate word boundaries with uppercase letter,
|
||||
//! including the first word.
|
||||
//! - **Screaming Snake Case**: Keep all letters uppercase and indicate word
|
||||
//! boundaries with underscores.
|
||||
//! - **Snake Case**: Keep all letters lowercase and indicate word boundaries
|
||||
//! with underscores.
|
||||
//! - **Verbatim**: Use the original attribute name defined in the code.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "rename_all", rename_all = "screaming_snake_case")]
|
||||
enum Opt {
|
||||
// This subcommand will be named `FIRST_COMMAND`. As the command doesn't
|
||||
// override the initial casing style, ...
|
||||
/// A screaming loud first command. Only use if necessary.
|
||||
FirstCommand {
|
||||
// this flag will be available as `--FOO` and `-F`.
|
||||
/// This flag will even scream louder.
|
||||
#[clap(long, short)]
|
||||
foo: bool,
|
||||
},
|
||||
|
||||
// As we override the casing style for this variant the related subcommand
|
||||
// will be named `SecondCommand`.
|
||||
/// Not nearly as loud as the first command.
|
||||
#[clap(rename_all = "pascal_case")]
|
||||
SecondCommand {
|
||||
// We can also override it again on a single field.
|
||||
/// Nice quiet flag. No one is annoyed.
|
||||
#[clap(rename_all = "snake_case", long)]
|
||||
bar_option: bool,
|
||||
|
||||
// Renaming will not be propagated into subcommand flagged enums. If
|
||||
// a non default casing style is required it must be defined on the
|
||||
// enum itself.
|
||||
#[clap(subcommand)]
|
||||
cmds: Subcommands,
|
||||
|
||||
// or flattened structs.
|
||||
#[clap(flatten)]
|
||||
options: BonusOptions,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum Subcommands {
|
||||
// This one will be available as `first-subcommand`.
|
||||
FirstSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct BonusOptions {
|
||||
// And this one will be available as `baz-option`.
|
||||
#[clap(long)]
|
||||
baz_option: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
47
clap_derive/examples/skip.rs
Normal file
47
clap_derive/examples/skip.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
//! How to use `#[structopt(skip)]`
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub struct Opt {
|
||||
#[clap(long, short)]
|
||||
number: u32,
|
||||
#[clap(skip)]
|
||||
k: Kind,
|
||||
#[clap(skip)]
|
||||
v: Vec<u32>,
|
||||
|
||||
#[clap(skip = Kind::A)]
|
||||
k2: Kind,
|
||||
#[clap(skip = vec![1, 2, 3])]
|
||||
v2: Vec<u32>,
|
||||
#[clap(skip = "cake")] // &str implements Into<String>
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Kind {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
impl Default for Kind {
|
||||
fn default() -> Self {
|
||||
return Kind::B;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "-n", "10"]),
|
||||
Opt {
|
||||
number: 10,
|
||||
k: Kind::B,
|
||||
v: vec![],
|
||||
|
||||
k2: Kind::A,
|
||||
v2: vec![1, 2, 3],
|
||||
s: String::from("cake")
|
||||
}
|
||||
);
|
||||
}
|
20
clap_derive/examples/subcommand_aliases.rs
Normal file
20
clap_derive/examples/subcommand_aliases.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
//! How to assign some aliases to subcommands
|
||||
|
||||
use clap::{AppSettings, Clap};
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
// https://docs.rs/clap/2/clap/enum.AppSettings.html#variant.InferSubcommands
|
||||
#[clap(setting = AppSettings::InferSubcommands)]
|
||||
enum Opt {
|
||||
// https://docs.rs/clap/2/clap/struct.App.html#method.alias
|
||||
#[clap(alias = "foobar")]
|
||||
Foo,
|
||||
// https://docs.rs/clap/2/clap/struct.App.html#method.aliases
|
||||
#[clap(aliases = &["baz", "fizz"])]
|
||||
Bar,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
41
clap_derive/examples/true_or_false.rs
Normal file
41
clap_derive/examples/true_or_false.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
//! How to parse `--foo=true --bar=false` and turn them into bool.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
fn true_or_false(s: &str) -> Result<bool, &'static str> {
|
||||
match s {
|
||||
"true" => Ok(true),
|
||||
"false" => Ok(false),
|
||||
_ => Err("expected `true` or `false`"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
// Default parser for `try_from_str` is FromStr::from_str.
|
||||
// `impl FromStr for bool` parses `true` or `false` so this
|
||||
// works as expected.
|
||||
#[clap(long, parse(try_from_str))]
|
||||
foo: bool,
|
||||
|
||||
// Of course, this could be done with an explicit parser function.
|
||||
#[clap(long, parse(try_from_str = true_or_false))]
|
||||
bar: bool,
|
||||
|
||||
// `bool` can be positional only with explicit `parse(...)` annotation
|
||||
#[clap(long, parse(try_from_str))]
|
||||
boom: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "--foo=true", "--bar=false", "true"]),
|
||||
Opt {
|
||||
foo: true,
|
||||
bar: false,
|
||||
boom: true
|
||||
}
|
||||
);
|
||||
// no beauty, only truth and falseness
|
||||
assert!(Opt::try_parse_from(&["test", "--foo=beauty"]).is_err());
|
||||
}
|
61
clap_derive/justfile
Normal file
61
clap_derive/justfile
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
@update-contributors:
|
||||
echo 'Removing old CONTRIBUTORS.md'
|
||||
mv CONTRIBUTORS.md CONTRIBUTORS.md.bak
|
||||
echo 'Downloading a list of new contributors'
|
||||
echo "the following is a list of contributors:" > CONTRIBUTORS.md
|
||||
echo "" >> CONTRIBUTORS.md
|
||||
echo "" >> CONTRIBUTORS.md
|
||||
githubcontrib --owner kbknapp --repo clap_derive --sha master --cols 6 --format md --showlogin true --sortBy contributions --sortOrder desc >> CONTRIBUTORS.md
|
||||
echo "" >> CONTRIBUTORS.md
|
||||
echo "" >> CONTRIBUTORS.md
|
||||
echo "This list was generated by [mgechev/github-contributors-list](https://github.com/mgechev/github-contributors-list)" >> CONTRIBUTORS.md
|
||||
rm CONTRIBUTORS.md.bak
|
||||
|
||||
run-test TESTG TEST="":
|
||||
cargo test --test {{TESTG}} -- {{TEST}}
|
||||
|
||||
debug TESTG TEST="":
|
||||
cargo test --test {{TESTG}} --features debug -- {{TEST}}
|
||||
|
||||
run-tests:
|
||||
cargo test
|
||||
|
||||
@bench: nightly
|
||||
cargo bench && just remove-nightly
|
||||
|
||||
nightly:
|
||||
rustup override add nightly
|
||||
|
||||
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 {} \;
|
||||
|
||||
top-errors NUM="95":
|
||||
@cargo check 2>&1 | head -n {{NUM}}
|
||||
|
||||
count-errors:
|
||||
@cargo check 2>&1 | grep -e '^error' | wc -l
|
||||
|
||||
find-errors:
|
||||
@cargo check 2>&1 | grep --only-matching -e '-->[^:]*' | sort | uniq -c | sort -nr
|
||||
|
||||
count-warnings:
|
||||
@cargo check 2>&1 | grep -e '^warning' | wc -l
|
||||
|
||||
find-warnings:
|
||||
@cargo check 2>&1 | grep -A1 -e 'warning' | grep --only-matching -e '-->[^:]*' | sort | uniq -c | sort -nr
|
||||
|
||||
@update-todo:
|
||||
./etc/update-todo.sh
|
||||
|
||||
@count-failures:
|
||||
./etc/count-tests.sh
|
2
clap_derive/rustfmt.toml
Normal file
2
clap_derive/rustfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
format_strings = false
|
||||
fn_single_line = true
|
94
clap_derive/src/derives/arg_enum.rs
Normal file
94
clap_derive/src/derives/arg_enum.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
use proc_macro2;
|
||||
// use quote;
|
||||
use syn;
|
||||
// use syn::punctuated;
|
||||
// use syn::token;
|
||||
|
||||
pub fn derive_arg_enum(_ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||
unimplemented!()
|
||||
|
||||
// let from_str_block = impl_from_str(ast)?;
|
||||
// let variants_block = impl_variants(ast)?;
|
||||
|
||||
// quote! {
|
||||
// #from_str_block
|
||||
// #variants_block
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
fn impl_from_str(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||
let ident = &ast.ident;
|
||||
let is_case_sensitive = ast.attrs.iter().any(|v| v.name() == "case_sensitive");
|
||||
let variants = variants(ast)?;
|
||||
|
||||
let strings = variants
|
||||
.iter()
|
||||
.map(|ref variant| String::from(variant.ident.as_ref()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// All of these need to be iterators.
|
||||
let ident_slice = [ident.clone()];
|
||||
let idents = ident_slice.iter().cycle();
|
||||
|
||||
let for_error_message = strings.clone();
|
||||
|
||||
let condition_function_slice = [match is_case_sensitive {
|
||||
true => quote! { str::eq },
|
||||
false => quote! { ::std::ascii::AsciiExt::eq_ignore_ascii_case },
|
||||
}];
|
||||
let condition_function = condition_function_slice.iter().cycle();
|
||||
|
||||
Ok(quote! {
|
||||
impl ::std::str::FromStr for #ident {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(input: &str) -> ::std::result::Result<Self, Self::Err> {
|
||||
match input {
|
||||
#(val if #condition_function(val, #strings) => Ok(#idents::#variants),)*
|
||||
_ => Err({
|
||||
let v = #for_error_message;
|
||||
format!("valid values: {}",
|
||||
v.join(" ,"))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn impl_variants(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||
let ident = &ast.ident;
|
||||
let variants = variants(ast)?
|
||||
.iter()
|
||||
.map(|ref variant| String::from(variant.ident.as_ref()))
|
||||
.collect::<Vec<_>>();
|
||||
let length = variants.len();
|
||||
|
||||
Ok(quote! {
|
||||
impl #ident {
|
||||
fn variants() -> [&'static str; #length] {
|
||||
#variants
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn variants(ast: &syn::DeriveInput) -> &punctuated::Punctuated<syn::Variant, token::Comma> {
|
||||
use syn::Data::*;
|
||||
|
||||
match ast.data {
|
||||
Enum(ref data) => data.variants,
|
||||
_ => panic!("Only enums are supported for deriving the ArgEnum trait"),
|
||||
}
|
||||
}
|
||||
*/
|
663
clap_derive/src/derives/attrs.rs
Normal file
663
clap_derive/src/derives/attrs.rs
Normal file
|
@ -0,0 +1,663 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use super::{parse::*, spanned::Sp, ty::Ty};
|
||||
|
||||
use std::env;
|
||||
|
||||
use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase};
|
||||
use proc_macro2::{self, Span, TokenStream};
|
||||
use proc_macro_error::abort;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Expr, Ident, LitStr, MetaNameValue};
|
||||
|
||||
/// Default casing style for generated arguments.
|
||||
pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Kind {
|
||||
Arg(Sp<Ty>),
|
||||
Subcommand(Sp<Ty>),
|
||||
FlattenStruct,
|
||||
Skip(Option<syn::Expr>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Method {
|
||||
name: syn::Ident,
|
||||
args: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Parser {
|
||||
pub kind: Sp<ParserKind>,
|
||||
pub func: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ParserKind {
|
||||
FromStr,
|
||||
TryFromStr,
|
||||
FromOsStr,
|
||||
TryFromOsStr,
|
||||
FromOccurrences,
|
||||
FromFlag,
|
||||
}
|
||||
|
||||
/// Defines the casing for the attributes long representation.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum CasingStyle {
|
||||
/// Indicate word boundaries with uppercase letter, excluding the first word.
|
||||
Camel,
|
||||
/// Keep all letters lowercase and indicate word boundaries with hyphens.
|
||||
Kebab,
|
||||
/// Indicate word boundaries with uppercase letter, including the first word.
|
||||
Pascal,
|
||||
/// Keep all letters uppercase and indicate word boundaries with underscores.
|
||||
ScreamingSnake,
|
||||
/// Keep all letters lowercase and indicate word boundaries with underscores.
|
||||
Snake,
|
||||
/// Use the original attribute name defined in the code.
|
||||
Verbatim,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Name {
|
||||
Derived(syn::Ident),
|
||||
Assigned(syn::LitStr),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Attrs {
|
||||
name: Name,
|
||||
casing: Sp<CasingStyle>,
|
||||
methods: Vec<Method>,
|
||||
parser: Sp<Parser>,
|
||||
author: Option<Method>,
|
||||
about: Option<Method>,
|
||||
version: Option<Method>,
|
||||
no_version: Option<syn::Ident>,
|
||||
has_custom_parser: bool,
|
||||
kind: Sp<Kind>,
|
||||
}
|
||||
|
||||
/// Output for the gen_xxx() methods were we need more than a simple stream of tokens.
|
||||
///
|
||||
/// The output of a generation method is not only the stream of new tokens but also the attribute
|
||||
/// information of the current element. These attribute information may contain valuable information
|
||||
/// for any kind of child arguments.
|
||||
pub struct GenOutput {
|
||||
pub tokens: proc_macro2::TokenStream,
|
||||
pub attrs: Attrs,
|
||||
}
|
||||
|
||||
impl Method {
|
||||
fn new(name: syn::Ident, args: proc_macro2::TokenStream) -> Self {
|
||||
Method { name, args }
|
||||
}
|
||||
|
||||
fn from_lit_or_env(ident: syn::Ident, lit: Option<syn::LitStr>, env_var: &str) -> Option<Self> {
|
||||
let mut lit = match lit {
|
||||
Some(lit) => lit,
|
||||
|
||||
None => match env::var(env_var) {
|
||||
Ok(val) => syn::LitStr::new(&val, ident.span()),
|
||||
Err(_) => {
|
||||
abort!(ident.span(),
|
||||
"cannot derive `{}` from Cargo.toml", ident;
|
||||
note = "`{}` environment variable is not set", env_var;
|
||||
help = "use `{} = \"...\"` to set {} manually", ident, ident;
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if ident == "author" {
|
||||
let edited = process_author_str(&lit.value());
|
||||
lit = syn::LitStr::new(&edited, lit.span());
|
||||
}
|
||||
|
||||
Some(Method::new(ident, quote!(#lit)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Method {
|
||||
fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
|
||||
let Method { ref name, ref args } = self;
|
||||
|
||||
let tokens = if name == "short" {
|
||||
quote!( .#name(#args.chars().nth(0).unwrap()) )
|
||||
} else {
|
||||
quote!( .#name(#args) )
|
||||
};
|
||||
|
||||
tokens.to_tokens(ts);
|
||||
}
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
fn default_spanned(span: Span) -> Sp<Self> {
|
||||
let kind = Sp::new(ParserKind::TryFromStr, span);
|
||||
let func = quote_spanned!(span=> ::std::str::FromStr::from_str);
|
||||
Sp::new(Parser { kind, func }, span)
|
||||
}
|
||||
|
||||
fn from_spec(parse_ident: syn::Ident, spec: ParserSpec) -> Sp<Self> {
|
||||
use self::ParserKind::*;
|
||||
|
||||
let kind = match &*spec.kind.to_string() {
|
||||
"from_str" => FromStr,
|
||||
"try_from_str" => TryFromStr,
|
||||
"from_os_str" => FromOsStr,
|
||||
"try_from_os_str" => TryFromOsStr,
|
||||
"from_occurrences" => FromOccurrences,
|
||||
"from_flag" => FromFlag,
|
||||
s => abort!(spec.kind.span(), "unsupported parser `{}`", s),
|
||||
};
|
||||
|
||||
let func = match spec.parse_func {
|
||||
None => match kind {
|
||||
FromStr | FromOsStr => {
|
||||
quote_spanned!(spec.kind.span()=> ::std::convert::From::from)
|
||||
}
|
||||
TryFromStr => quote_spanned!(spec.kind.span()=> ::std::str::FromStr::from_str),
|
||||
TryFromOsStr => abort!(
|
||||
spec.kind.span(),
|
||||
"you must set parser for `try_from_os_str` explicitly"
|
||||
),
|
||||
FromOccurrences => quote_spanned!(spec.kind.span()=> { |v| v as _ }),
|
||||
FromFlag => quote_spanned!(spec.kind.span()=> ::std::convert::From::from),
|
||||
},
|
||||
|
||||
Some(func) => match func {
|
||||
syn::Expr::Path(_) => quote!(#func),
|
||||
_ => abort!(func.span(), "`parse` argument must be a function path"),
|
||||
},
|
||||
};
|
||||
|
||||
let kind = Sp::new(kind, spec.kind.span());
|
||||
let parser = Parser { kind, func };
|
||||
Sp::new(parser, parse_ident.span())
|
||||
}
|
||||
}
|
||||
|
||||
impl CasingStyle {
|
||||
fn from_lit(name: syn::LitStr) -> Sp<Self> {
|
||||
use self::CasingStyle::*;
|
||||
|
||||
let normalized = name.value().to_camel_case().to_lowercase();
|
||||
let cs = |kind| Sp::new(kind, name.span());
|
||||
|
||||
match normalized.as_ref() {
|
||||
"camel" | "camelcase" => cs(Camel),
|
||||
"kebab" | "kebabcase" => cs(Kebab),
|
||||
"pascal" | "pascalcase" => cs(Pascal),
|
||||
"screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
|
||||
"snake" | "snakecase" => cs(Snake),
|
||||
"verbatim" | "verbatimcase" => cs(Verbatim),
|
||||
s => abort!(name.span(), "unsupported casing: `{}`", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn translate(self, style: CasingStyle) -> LitStr {
|
||||
use self::CasingStyle::*;
|
||||
|
||||
match self {
|
||||
Name::Assigned(lit) => lit,
|
||||
Name::Derived(ident) => {
|
||||
let s = ident.unraw().to_string();
|
||||
let s = match style {
|
||||
Pascal => s.to_camel_case(),
|
||||
Kebab => s.to_kebab_case(),
|
||||
Camel => s.to_mixed_case(),
|
||||
ScreamingSnake => s.to_shouty_snake_case(),
|
||||
Snake => s.to_snake_case(),
|
||||
Verbatim => s,
|
||||
};
|
||||
syn::LitStr::new(&s, ident.span())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attrs {
|
||||
fn new(default_span: Span, name: Name, casing: Sp<CasingStyle>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
casing,
|
||||
methods: vec![],
|
||||
parser: Parser::default_spanned(default_span),
|
||||
about: None,
|
||||
author: None,
|
||||
version: None,
|
||||
no_version: None,
|
||||
|
||||
has_custom_parser: false,
|
||||
kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span),
|
||||
}
|
||||
}
|
||||
|
||||
/// push `.method("str literal")`
|
||||
fn push_str_method(&mut self, name: Sp<String>, arg: Sp<String>) {
|
||||
match (&**name, &**arg) {
|
||||
("name", _) => {
|
||||
self.name = Name::Assigned(arg.as_lit());
|
||||
}
|
||||
_ => self
|
||||
.methods
|
||||
.push(Method::new(name.as_ident(), quote!(#arg))),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_attrs(&mut self, attrs: &[syn::Attribute]) {
|
||||
use ClapAttr::*;
|
||||
|
||||
for attr in parse_clap_attributes(attrs) {
|
||||
match attr {
|
||||
Short(ident) | Long(ident) => {
|
||||
self.push_str_method(
|
||||
ident.into(),
|
||||
self.name.clone().translate(*self.casing).into(),
|
||||
);
|
||||
}
|
||||
|
||||
Subcommand(ident) => {
|
||||
let ty = Sp::call_site(Ty::Other);
|
||||
let kind = Sp::new(Kind::Subcommand(ty), ident.span());
|
||||
self.set_kind(kind);
|
||||
}
|
||||
|
||||
Flatten(ident) => {
|
||||
let kind = Sp::new(Kind::FlattenStruct, ident.span());
|
||||
self.set_kind(kind);
|
||||
}
|
||||
|
||||
Skip(ident, expr) => {
|
||||
let kind = Sp::new(Kind::Skip(expr), ident.span());
|
||||
self.set_kind(kind);
|
||||
}
|
||||
|
||||
NoVersion(ident) => self.no_version = Some(ident),
|
||||
|
||||
About(ident, about) => {
|
||||
self.about = Method::from_lit_or_env(ident, about, "CARGO_PKG_DESCRIPTION");
|
||||
}
|
||||
|
||||
Author(ident, author) => {
|
||||
self.author = Method::from_lit_or_env(ident, author, "CARGO_PKG_AUTHORS");
|
||||
}
|
||||
|
||||
Version(ident, version) => {
|
||||
self.version = Some(Method::new(ident, quote!(#version)))
|
||||
}
|
||||
|
||||
NameLitStr(name, lit) => {
|
||||
self.push_str_method(name.into(), lit.into());
|
||||
}
|
||||
|
||||
NameExpr(name, expr) => self.methods.push(Method::new(name, quote!(#expr))),
|
||||
|
||||
MethodCall(name, args) => self.methods.push(Method::new(name, quote!(#(#args),*))),
|
||||
|
||||
RenameAll(_, casing_lit) => {
|
||||
self.casing = CasingStyle::from_lit(casing_lit);
|
||||
}
|
||||
|
||||
Parse(ident, spec) => {
|
||||
self.has_custom_parser = true;
|
||||
self.parser = Parser::from_spec(ident, spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_doc_comment(&mut self, attrs: &[syn::Attribute], name: &str) {
|
||||
let doc_comments = attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if attr.path.is_ident("doc") {
|
||||
attr.parse_meta().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|attr| {
|
||||
use syn::Lit::*;
|
||||
use syn::Meta::*;
|
||||
if let NameValue(syn::MetaNameValue {
|
||||
path, lit: Str(s), ..
|
||||
}) = attr
|
||||
{
|
||||
if !path.is_ident("doc") {
|
||||
return None;
|
||||
}
|
||||
let value = s.value();
|
||||
|
||||
let text = value
|
||||
.trim_start_matches("//!")
|
||||
.trim_start_matches("///")
|
||||
.trim_start_matches("/*!")
|
||||
.trim_start_matches("/**")
|
||||
.trim_end_matches("*/")
|
||||
.trim();
|
||||
if text.is_empty() {
|
||||
Some("\n\n".to_string())
|
||||
} else {
|
||||
Some(text.to_string())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if doc_comments.is_empty() {
|
||||
return;
|
||||
}
|
||||
let merged_lines = doc_comments
|
||||
.join(" ")
|
||||
.split('\n')
|
||||
.map(str::trim)
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let expected_doc_comment_split = if let Some(content) = doc_comments.get(1) {
|
||||
(doc_comments.len() > 2) && (content == &"\n\n")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if expected_doc_comment_split {
|
||||
let long_name = Sp::call_site(format!("long_{}", name));
|
||||
|
||||
self.methods
|
||||
.push(Method::new(long_name.as_ident(), quote!(#merged_lines)));
|
||||
|
||||
// Remove trailing whitespace and period from short help, as rustdoc
|
||||
// best practice is to use complete sentences, but command-line help
|
||||
// typically omits the trailing period.
|
||||
let short_arg = doc_comments
|
||||
.first()
|
||||
.map(|s| s.trim())
|
||||
.map_or("", |s| s.trim_end_matches('.'));
|
||||
|
||||
self.methods.push(Method::new(
|
||||
syn::Ident::new(name, Span::call_site()),
|
||||
quote!(#short_arg),
|
||||
));
|
||||
} else {
|
||||
self.methods.push(Method::new(
|
||||
syn::Ident::new(name, Span::call_site()),
|
||||
quote!(#merged_lines),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_struct(
|
||||
span: Span,
|
||||
attrs: &[syn::Attribute],
|
||||
name: Name,
|
||||
argument_casing: Sp<CasingStyle>,
|
||||
) -> Self {
|
||||
let mut res = Self::new(span, name, argument_casing);
|
||||
res.push_attrs(attrs);
|
||||
res.push_doc_comment(attrs, "about");
|
||||
|
||||
if res.has_custom_parser {
|
||||
abort!(
|
||||
res.parser.span(),
|
||||
"`parse` attribute is only allowed on fields"
|
||||
);
|
||||
}
|
||||
match &*res.kind {
|
||||
Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"),
|
||||
Kind::FlattenStruct => abort!(res.kind.span(), "flatten is only allowed on fields"),
|
||||
Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"),
|
||||
Kind::Arg(_) => res,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_field(field: &syn::Field, struct_casing: Sp<CasingStyle>) -> Self {
|
||||
let name = field.ident.clone().unwrap();
|
||||
let mut res = Self::new(field.span(), Name::Derived(name.clone()), struct_casing);
|
||||
res.push_doc_comment(&field.attrs, "help");
|
||||
res.push_attrs(&field.attrs);
|
||||
|
||||
match &*res.kind {
|
||||
Kind::FlattenStruct => {
|
||||
if res.has_custom_parser {
|
||||
abort!(
|
||||
res.parser.span(),
|
||||
"parse attribute is not allowed for flattened entry"
|
||||
);
|
||||
}
|
||||
if res.has_explicit_methods() || res.has_doc_methods() {
|
||||
abort!(
|
||||
res.kind.span(),
|
||||
"methods and doc comments are not allowed for flattened entry"
|
||||
);
|
||||
}
|
||||
}
|
||||
Kind::Subcommand(_) => {
|
||||
if res.has_custom_parser {
|
||||
abort!(
|
||||
res.parser.span(),
|
||||
"parse attribute is not allowed for subcommand"
|
||||
);
|
||||
}
|
||||
if res.has_explicit_methods() {
|
||||
abort!(
|
||||
res.kind.span(),
|
||||
"methods in attributes are not allowed for subcommand"
|
||||
);
|
||||
}
|
||||
|
||||
let ty = Ty::from_syn_ty(&field.ty);
|
||||
match *ty {
|
||||
Ty::OptionOption => {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"Option<Option<T>> type is not allowed for subcommand"
|
||||
);
|
||||
}
|
||||
Ty::OptionVec => {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"Option<Vec<T>> type is not allowed for subcommand"
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
res.kind = Sp::new(Kind::Subcommand(ty), res.kind.span());
|
||||
}
|
||||
Kind::Skip(_) => {
|
||||
if res.has_explicit_methods() {
|
||||
abort!(
|
||||
res.kind.span(),
|
||||
"methods are not allowed for skipped fields"
|
||||
);
|
||||
}
|
||||
}
|
||||
Kind::Arg(orig_ty) => {
|
||||
let mut ty = Ty::from_syn_ty(&field.ty);
|
||||
if res.has_custom_parser {
|
||||
match *ty {
|
||||
Ty::Option | Ty::Vec | Ty::OptionVec => (),
|
||||
_ => ty = Sp::new(Ty::Other, ty.span()),
|
||||
}
|
||||
}
|
||||
|
||||
match *ty {
|
||||
Ty::Bool => {
|
||||
if res.is_positional() && !res.has_custom_parser {
|
||||
abort!(ty.span(),
|
||||
"`bool` cannot be used as positional parameter with default parser";
|
||||
help = "if you want to create a flag add `long` or `short`";
|
||||
help = "If you really want a boolean parameter \
|
||||
add an explicit parser, for example `parse(try_from_str)`";
|
||||
note = "see also https://github.com/clap-rs/clap_derive/tree/master/examples/true_or_false.rs";
|
||||
)
|
||||
}
|
||||
if let Some(m) = res.find_method("default_value") {
|
||||
abort!(m.name.span(), "default_value is meaningless for bool")
|
||||
}
|
||||
if let Some(m) = res.find_method("required") {
|
||||
abort!(m.name.span(), "required is meaningless for bool")
|
||||
}
|
||||
}
|
||||
Ty::Option => {
|
||||
if let Some(m) = res.find_method("default_value") {
|
||||
abort!(m.name.span(), "default_value is meaningless for Option")
|
||||
}
|
||||
if let Some(m) = res.find_method("required") {
|
||||
abort!(m.name.span(), "required is meaningless for Option")
|
||||
}
|
||||
}
|
||||
Ty::OptionOption => {
|
||||
if res.is_positional() {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"Option<Option<T>> type is meaningless for positional argument"
|
||||
)
|
||||
}
|
||||
}
|
||||
Ty::OptionVec => {
|
||||
if res.is_positional() {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"Option<Vec<T>> type is meaningless for positional argument"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
res.kind = Sp::new(Kind::Arg(ty), orig_ty.span());
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn set_kind(&mut self, kind: Sp<Kind>) {
|
||||
if let Kind::Arg(_) = *self.kind {
|
||||
self.kind = kind;
|
||||
} else {
|
||||
abort!(
|
||||
kind.span(),
|
||||
"subcommand, flatten and skip cannot be used together"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_method(&self, name: &str) -> bool {
|
||||
self.find_method(name).is_some()
|
||||
}
|
||||
|
||||
pub fn find_method(&self, name: &str) -> Option<&Method> {
|
||||
self.methods.iter().find(|m| m.name == name)
|
||||
}
|
||||
|
||||
/// generate methods from attributes on top of struct or enum
|
||||
pub fn top_level_methods(&self) -> proc_macro2::TokenStream {
|
||||
let version = match (&self.no_version, &self.version) {
|
||||
(Some(no_version), Some(_)) => abort!(
|
||||
no_version.span(),
|
||||
"`no_version` and `version = \"version\"` can't be used together"
|
||||
),
|
||||
|
||||
(None, Some(m)) => m.to_token_stream(),
|
||||
|
||||
(None, None) => std::env::var("CARGO_PKG_VERSION")
|
||||
.map(|version| quote!( .version(#version) ))
|
||||
.unwrap_or_default(),
|
||||
|
||||
(Some(_), None) => quote!(),
|
||||
};
|
||||
|
||||
let author = &self.author;
|
||||
let about = &self.about;
|
||||
let methods = &self.methods;
|
||||
|
||||
quote!( #author #version #(#methods)* #about )
|
||||
}
|
||||
|
||||
/// generate methods on top of a field
|
||||
pub fn field_methods(&self) -> proc_macro2::TokenStream {
|
||||
let methods = &self.methods;
|
||||
quote!( #(#methods)* )
|
||||
}
|
||||
|
||||
pub fn cased_name(&self) -> LitStr {
|
||||
self.name.clone().translate(*self.casing)
|
||||
}
|
||||
|
||||
pub fn parser(&self) -> &Sp<Parser> {
|
||||
&self.parser
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> Sp<Kind> {
|
||||
self.kind.clone()
|
||||
}
|
||||
|
||||
pub fn casing(&self) -> Sp<CasingStyle> {
|
||||
self.casing.clone()
|
||||
}
|
||||
|
||||
pub fn is_positional(&self) -> bool {
|
||||
self.methods
|
||||
.iter()
|
||||
.all(|m| m.name != "long" && m.name != "short")
|
||||
}
|
||||
|
||||
pub fn has_explicit_methods(&self) -> bool {
|
||||
self.methods
|
||||
.iter()
|
||||
.any(|m| m.name != "help" && m.name != "long_help")
|
||||
}
|
||||
|
||||
pub fn has_doc_methods(&self) -> bool {
|
||||
self.methods
|
||||
.iter()
|
||||
.any(|m| m.name == "help" || m.name == "long_help")
|
||||
}
|
||||
}
|
||||
|
||||
/// replace all `:` with `, ` when not inside the `<>`
|
||||
///
|
||||
/// `"author1:author2:author3" => "author1, author2, author3"`
|
||||
/// `"author1 <http://website1.com>:author2" => "author1 <http://website1.com>, author2"
|
||||
fn process_author_str(author: &str) -> String {
|
||||
let mut res = String::with_capacity(author.len());
|
||||
let mut inside_angle_braces = 0usize;
|
||||
|
||||
for ch in author.chars() {
|
||||
if inside_angle_braces > 0 && ch == '>' {
|
||||
inside_angle_braces -= 1;
|
||||
res.push(ch);
|
||||
} else if ch == '<' {
|
||||
inside_angle_braces += 1;
|
||||
res.push(ch);
|
||||
} else if inside_angle_braces == 0 && ch == ':' {
|
||||
res.push_str(", ");
|
||||
} else {
|
||||
res.push(ch);
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
427
clap_derive/src/derives/clap.rs
Normal file
427
clap_derive/src/derives/clap.rs
Normal file
|
@ -0,0 +1,427 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
use proc_macro2;
|
||||
use proc_macro_error::{abort, abort_call_site, set_dummy};
|
||||
use syn::{self, punctuated, spanned::Spanned, token};
|
||||
|
||||
use super::{from_argmatches, into_app, sub_type, Attrs, Kind, Name, ParserKind, Ty};
|
||||
|
||||
/// Generate a block of code to add arguments/subcommands corresponding to
|
||||
/// the `fields` to an app.
|
||||
fn gen_app_augmentation(
|
||||
fields: &punctuated::Punctuated<syn::Field, token::Comma>,
|
||||
app_var: &syn::Ident,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let mut subcmds = fields.iter().filter_map(|field| {
|
||||
let attrs = Attrs::from_field(&field, parent_attribute.casing());
|
||||
let kind = attrs.kind();
|
||||
if let Kind::Subcommand(ty) = &*kind {
|
||||
let subcmd_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
let required = if **ty == Ty::Option {
|
||||
quote!()
|
||||
} else {
|
||||
quote_spanned! { kind.span()=>
|
||||
let #app_var = #app_var.setting(
|
||||
::clap::AppSettings::SubcommandRequiredElseHelp
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let span = field.span();
|
||||
let ts = quote! {
|
||||
let #app_var = <#subcmd_type>::augment_app( #app_var );
|
||||
#required
|
||||
};
|
||||
Some((span, ts))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let subcmd = subcmds.next().map(|(_, ts)| ts);
|
||||
if let Some((span, _)) = subcmds.next() {
|
||||
abort!(
|
||||
span,
|
||||
"multiple subcommand sets are not allowed, that's the second"
|
||||
);
|
||||
}
|
||||
|
||||
let args = fields.iter().filter_map(|field| {
|
||||
let attrs = Attrs::from_field(field, parent_attribute.casing());
|
||||
let kind = attrs.kind();
|
||||
match &*kind {
|
||||
Kind::Subcommand(_) | Kind::Skip(_) => None,
|
||||
Kind::FlattenStruct => {
|
||||
let ty = &field.ty;
|
||||
Some(quote_spanned! { kind.span()=>
|
||||
let #app_var = <#ty>::augment_app(#app_var);
|
||||
let #app_var = if <#ty>::is_subcommand() {
|
||||
#app_var.setting(::clap::AppSettings::SubcommandRequiredElseHelp)
|
||||
} else {
|
||||
#app_var
|
||||
};
|
||||
})
|
||||
}
|
||||
Kind::Arg(ty) => {
|
||||
let convert_type = match **ty {
|
||||
Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
|
||||
Ty::OptionOption | Ty::OptionVec => {
|
||||
sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
|
||||
}
|
||||
_ => &field.ty,
|
||||
};
|
||||
|
||||
let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
|
||||
let flag = *attrs.parser().kind == ParserKind::FromFlag;
|
||||
|
||||
let parser = attrs.parser();
|
||||
let func = &parser.func;
|
||||
let validator = match *parser.kind {
|
||||
ParserKind::TryFromStr => quote_spanned! { func.span()=>
|
||||
.validator(|s| {
|
||||
#func(s.as_str())
|
||||
.map(|_: #convert_type| ())
|
||||
.map_err(|e| e.to_string())
|
||||
})
|
||||
},
|
||||
ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
|
||||
.validator_os(|s| #func(&s).map(|_: #convert_type| ()))
|
||||
},
|
||||
_ => quote!(),
|
||||
};
|
||||
|
||||
let modifier = match **ty {
|
||||
Ty::Bool => quote!(),
|
||||
|
||||
Ty::Option => quote_spanned! { ty.span()=>
|
||||
.takes_value(true)
|
||||
#validator
|
||||
},
|
||||
|
||||
Ty::OptionOption => quote_spanned! { ty.span()=>
|
||||
.takes_value(true)
|
||||
.multiple(false)
|
||||
.min_values(0)
|
||||
.max_values(1)
|
||||
#validator
|
||||
},
|
||||
|
||||
Ty::OptionVec => quote_spanned! { ty.span()=>
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.min_values(0)
|
||||
#validator
|
||||
},
|
||||
|
||||
Ty::Vec => quote_spanned! { ty.span()=>
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
#validator
|
||||
},
|
||||
|
||||
Ty::Other if occurrences => quote_spanned! { ty.span()=>
|
||||
.multiple_occurrences(true)
|
||||
},
|
||||
|
||||
Ty::Other if flag => quote_spanned! { ty.span()=>
|
||||
.takes_value(false)
|
||||
.multiple(false)
|
||||
},
|
||||
|
||||
Ty::Other => {
|
||||
let required = !attrs.has_method("default_value");
|
||||
quote_spanned! { ty.span()=>
|
||||
.takes_value(true)
|
||||
.required(#required)
|
||||
#validator
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let name = attrs.cased_name();
|
||||
let methods = attrs.field_methods();
|
||||
|
||||
Some(quote_spanned! { field.span()=>
|
||||
let #app_var = #app_var.arg(
|
||||
::clap::Arg::with_name(#name)
|
||||
#modifier
|
||||
#methods
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let app_methods = parent_attribute.top_level_methods();
|
||||
quote! {{
|
||||
let #app_var = #app_var#app_methods;
|
||||
#( #args )*
|
||||
#subcmd
|
||||
#app_var
|
||||
}}
|
||||
}
|
||||
|
||||
fn gen_augment_app_fn(
|
||||
fields: &punctuated::Punctuated<syn::Field, token::Comma>,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let app_var = syn::Ident::new("app", proc_macro2::Span::call_site());
|
||||
let augmentation = gen_app_augmentation(fields, &app_var, parent_attribute);
|
||||
quote! {
|
||||
pub fn augment_app<'b>(
|
||||
#app_var: ::clap::App<'b>
|
||||
) -> ::clap::App<'b> {
|
||||
#augmentation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_augment_app_for_enum(
|
||||
variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let subcommands = variants.iter().map(|variant| {
|
||||
let attrs = Attrs::from_struct(
|
||||
variant.span(),
|
||||
&variant.attrs,
|
||||
Name::Derived(variant.ident.clone()),
|
||||
parent_attribute.casing(),
|
||||
);
|
||||
let app_var = syn::Ident::new("subcommand", proc_macro2::Span::call_site());
|
||||
let arg_block = match variant.fields {
|
||||
Named(ref fields) => gen_app_augmentation(&fields.named, &app_var, &attrs),
|
||||
Unit => quote!( #app_var ),
|
||||
Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
|
||||
let ty = &unnamed[0];
|
||||
quote_spanned! { ty.span() =>
|
||||
{
|
||||
let #app_var = <#ty>::augment_app(#app_var);
|
||||
if <#ty>::is_subcommand() {
|
||||
#app_var.setting(
|
||||
::clap::AppSettings::SubcommandRequiredElseHelp
|
||||
)
|
||||
} else {
|
||||
#app_var
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
|
||||
};
|
||||
|
||||
let name = attrs.cased_name();
|
||||
let from_attrs = attrs.top_level_methods();
|
||||
|
||||
quote! {
|
||||
.subcommand({
|
||||
let #app_var = ::clap::App::new(#name);
|
||||
let #app_var = #arg_block;
|
||||
#app_var#from_attrs
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let app_methods = parent_attribute.top_level_methods();
|
||||
|
||||
quote! {
|
||||
pub fn augment_app<'b>(
|
||||
app: ::clap::App<'b>
|
||||
) -> ::clap::App<'b> {
|
||||
app #app_methods #( #subcommands )*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_from_subcommand(
|
||||
name: &syn::Ident,
|
||||
variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
use syn::Fields::*;
|
||||
|
||||
let match_arms = variants.iter().map(|variant| {
|
||||
let attrs = Attrs::from_struct(
|
||||
variant.span(),
|
||||
&variant.attrs,
|
||||
Name::Derived(variant.ident.clone()),
|
||||
parent_attribute.casing(),
|
||||
);
|
||||
let sub_name = attrs.cased_name();
|
||||
let variant_name = &variant.ident;
|
||||
let constructor_block = match variant.fields {
|
||||
Named(ref fields) => from_argmatches::gen_constructor(&fields.named, &attrs),
|
||||
Unit => quote!(),
|
||||
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||
let ty = &fields.unnamed[0];
|
||||
quote!( ( <#ty as ::clap::FromArgMatches>::from_argmatches(matches) ) )
|
||||
}
|
||||
Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
|
||||
};
|
||||
|
||||
quote! {
|
||||
(#sub_name, Some(matches)) =>
|
||||
Some(#name :: #variant_name #constructor_block)
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
pub fn from_subcommand<'b>(
|
||||
sub: (&'b str, Option<&'b ::clap::ArgMatches>)
|
||||
) -> Option<Self> {
|
||||
match sub {
|
||||
#( #match_arms ),*,
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clap_impl_for_struct(
|
||||
name: &syn::Ident,
|
||||
fields: &punctuated::Punctuated<syn::Field, token::Comma>,
|
||||
attrs: &[syn::Attribute],
|
||||
) -> proc_macro2::TokenStream {
|
||||
let into_app_impl = into_app::gen_into_app_impl_for_struct(name, attrs);
|
||||
let into_app_impl_tokens = into_app_impl.tokens;
|
||||
let augment_app_fn = gen_augment_app_fn(fields, &into_app_impl.attrs);
|
||||
let from_argmatches_impl =
|
||||
from_argmatches::gen_from_argmatches_impl_for_struct(name, fields, &into_app_impl.attrs);
|
||||
let parse_fns = gen_parse_fns(name);
|
||||
|
||||
quote! {
|
||||
#[allow(unused_variables)]
|
||||
impl ::clap::Clap for #name { }
|
||||
|
||||
#into_app_impl_tokens
|
||||
|
||||
#from_argmatches_impl
|
||||
|
||||
#[allow(dead_code, unreachable_code)]
|
||||
#[doc(hidden)]
|
||||
impl #name {
|
||||
#augment_app_fn
|
||||
|
||||
#parse_fns
|
||||
|
||||
pub fn is_subcommand() -> bool { false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clap_impl_for_enum(
|
||||
name: &syn::Ident,
|
||||
variants: &punctuated::Punctuated<syn::Variant, token::Comma>,
|
||||
attrs: &[syn::Attribute],
|
||||
) -> proc_macro2::TokenStream {
|
||||
let into_app_impl = into_app::gen_into_app_impl_for_enum(name, attrs);
|
||||
let into_app_impl_tokens = into_app_impl.tokens;
|
||||
let augment_app_fn = gen_augment_app_for_enum(variants, &into_app_impl.attrs);
|
||||
let from_argmatches_impl = from_argmatches::gen_from_argmatches_impl_for_enum(name);
|
||||
let from_subcommand = gen_from_subcommand(name, variants, &into_app_impl.attrs);
|
||||
let parse_fns = gen_parse_fns(name);
|
||||
|
||||
quote! {
|
||||
#[allow(unused_variables)]
|
||||
impl ::clap::Clap for #name { }
|
||||
|
||||
#into_app_impl_tokens
|
||||
|
||||
#from_argmatches_impl
|
||||
|
||||
#[allow(unused_variables, dead_code, unreachable_code)]
|
||||
#[doc(hidden)]
|
||||
impl #name {
|
||||
#augment_app_fn
|
||||
|
||||
#from_subcommand
|
||||
|
||||
#parse_fns
|
||||
|
||||
pub fn is_subcommand() -> bool { true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive_clap(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||
use syn::Data::*;
|
||||
|
||||
let struct_name = &input.ident;
|
||||
|
||||
set_dummy(quote! {
|
||||
impl ::clap::Clap for #struct_name {}
|
||||
|
||||
impl ::clap::IntoApp for #struct_name {
|
||||
fn into_app<'b>() -> ::clap::App<'b> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::clap::FromArgMatches for #struct_name {
|
||||
fn from_argmatches(m: &::clap::ArgMatches) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl #struct_name {
|
||||
fn parse() -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
match input.data {
|
||||
Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(ref fields),
|
||||
..
|
||||
}) => clap_impl_for_struct(struct_name, &fields.named, &input.attrs),
|
||||
Enum(ref e) => clap_impl_for_enum(struct_name, &e.variants, &input.attrs),
|
||||
_ => abort_call_site!("clap_derive only supports non-tuple structs and enums"),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_parse_fns(name: &syn::Ident) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
#[allow(unreachable_pub)]
|
||||
pub fn parse() -> #name {
|
||||
use ::clap::{FromArgMatches, IntoApp};
|
||||
#name::from_argmatches(&#name::into_app().get_matches())
|
||||
}
|
||||
#[allow(unreachable_pub)]
|
||||
pub fn try_parse() -> ::std::result::Result<#name, ::clap::Error> {
|
||||
use ::clap::{FromArgMatches, IntoApp};
|
||||
Ok(#name::from_argmatches(&#name::into_app().try_get_matches()?))
|
||||
}
|
||||
#[allow(unreachable_pub)]
|
||||
pub fn parse_from<I, T>(itr: I) -> #name
|
||||
where
|
||||
I: ::std::iter::IntoIterator<Item = T>,
|
||||
T: Into<::std::ffi::OsString> + Clone {
|
||||
use ::clap::{FromArgMatches, IntoApp};
|
||||
#name::from_argmatches(&#name::into_app().get_matches_from(itr))
|
||||
}
|
||||
#[allow(unreachable_pub)]
|
||||
pub fn try_parse_from<I, T>(itr: I) -> ::std::result::Result<#name, ::clap::Error>
|
||||
where
|
||||
I: ::std::iter::IntoIterator<Item = T>,
|
||||
T: Into<::std::ffi::OsString> + Clone {
|
||||
use ::clap::{FromArgMatches, IntoApp};
|
||||
Ok(#name::from_argmatches(&#name::into_app().try_get_matches_from(itr)?))
|
||||
}
|
||||
}
|
||||
}
|
237
clap_derive/src/derives/from_argmatches.rs
Normal file
237
clap_derive/src/derives/from_argmatches.rs
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
use std::env;
|
||||
|
||||
use proc_macro2;
|
||||
use syn;
|
||||
use syn::punctuated;
|
||||
use syn::spanned::Spanned as _;
|
||||
use syn::token;
|
||||
|
||||
use super::{spanned::Sp, sub_type, Attrs, Kind, Name, ParserKind, Ty, DEFAULT_CASING};
|
||||
|
||||
pub fn derive_from_argmatches(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||
use syn::Data::*;
|
||||
|
||||
let struct_name = &input.ident;
|
||||
let inner_impl = match input.data {
|
||||
Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(ref fields),
|
||||
..
|
||||
}) => {
|
||||
let name = env::var("CARGO_PKG_NAME")
|
||||
.ok()
|
||||
.unwrap_or_else(String::default);
|
||||
|
||||
let attrs = Attrs::from_struct(
|
||||
proc_macro2::Span::call_site(),
|
||||
&input.attrs,
|
||||
Name::Assigned(syn::LitStr::new(&name, proc_macro2::Span::call_site())),
|
||||
Sp::call_site(DEFAULT_CASING),
|
||||
);
|
||||
|
||||
gen_from_argmatches_impl_for_struct(struct_name, &fields.named, &attrs)
|
||||
}
|
||||
// Enum(ref e) => clap_for_enum_impl(struct_name, &e.variants, &input.attrs),
|
||||
_ => panic!("clap_derive only supports non-tuple structs"), // and enums"),
|
||||
};
|
||||
|
||||
quote!(#inner_impl)
|
||||
}
|
||||
|
||||
pub fn gen_from_argmatches_impl_for_struct(
|
||||
name: &syn::Ident,
|
||||
fields: &punctuated::Punctuated<syn::Field, token::Comma>,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let from_argmatches_fn = gen_from_argmatches_fn_for_struct(name, fields, parent_attribute);
|
||||
|
||||
quote! {
|
||||
impl ::clap::FromArgMatches for #name {
|
||||
#from_argmatches_fn
|
||||
}
|
||||
|
||||
impl From<::clap::ArgMatches> for #name {
|
||||
fn from(m: ::clap::ArgMatches) -> Self {
|
||||
use ::clap::FromArgMatches;
|
||||
<Self as ::clap::FromArgMatches>::from_argmatches(&m)
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO impl TryFrom once stable
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_from_argmatches_fn_for_struct(
|
||||
struct_name: &syn::Ident,
|
||||
fields: &punctuated::Punctuated<syn::Field, token::Comma>,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field_block = gen_constructor(fields, parent_attribute);
|
||||
|
||||
quote! {
|
||||
fn from_argmatches(matches: &::clap::ArgMatches) -> Self {
|
||||
#struct_name #field_block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_constructor(
|
||||
fields: &punctuated::Punctuated<syn::Field, token::Comma>,
|
||||
parent_attribute: &Attrs,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let fields = fields.iter().map(|field| {
|
||||
let attrs = Attrs::from_field(field, parent_attribute.casing());
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let kind = attrs.kind();
|
||||
match &*attrs.kind() {
|
||||
Kind::Subcommand(ty) => {
|
||||
let subcmd_type = match (**ty, sub_type(&field.ty)) {
|
||||
(Ty::Option, Some(sub_type)) => sub_type,
|
||||
_ => &field.ty,
|
||||
};
|
||||
let unwrapper = match **ty {
|
||||
Ty::Option => quote!(),
|
||||
_ => quote_spanned!( ty.span()=> .unwrap() ),
|
||||
};
|
||||
quote_spanned! { kind.span()=>
|
||||
#field_name: <#subcmd_type>::from_subcommand(matches.subcommand())#unwrapper
|
||||
}
|
||||
}
|
||||
|
||||
Kind::FlattenStruct => quote_spanned! { kind.span()=>
|
||||
#field_name: ::clap::FromArgMatches::from_argmatches(matches)
|
||||
},
|
||||
|
||||
Kind::Skip(val) => match val {
|
||||
None => quote_spanned!(kind.span()=> #field_name: Default::default()),
|
||||
Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
|
||||
},
|
||||
|
||||
Kind::Arg(ty) => {
|
||||
use self::ParserKind::*;
|
||||
|
||||
let parser = attrs.parser();
|
||||
let func = &parser.func;
|
||||
let span = parser.kind.span();
|
||||
let (value_of, values_of, parse) = match *parser.kind {
|
||||
FromStr => (
|
||||
quote_spanned!(span=> value_of),
|
||||
quote_spanned!(span=> values_of),
|
||||
func.clone(),
|
||||
),
|
||||
TryFromStr => (
|
||||
quote_spanned!(span=> value_of),
|
||||
quote_spanned!(span=> values_of),
|
||||
quote_spanned!(func.span()=> |s| #func(s).unwrap()),
|
||||
),
|
||||
FromOsStr => (
|
||||
quote_spanned!(span=> value_of_os),
|
||||
quote_spanned!(span=> values_of_os),
|
||||
func.clone(),
|
||||
),
|
||||
TryFromOsStr => (
|
||||
quote_spanned!(span=> value_of_os),
|
||||
quote_spanned!(span=> values_of_os),
|
||||
quote_spanned!(func.span()=> |s| #func(s).unwrap()),
|
||||
),
|
||||
FromOccurrences => (
|
||||
quote_spanned!(span=> occurrences_of),
|
||||
quote!(),
|
||||
func.clone(),
|
||||
),
|
||||
FromFlag => (quote!(), quote!(), func.clone()),
|
||||
};
|
||||
|
||||
let flag = *attrs.parser().kind == ParserKind::FromFlag;
|
||||
let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
|
||||
let name = attrs.cased_name();
|
||||
let field_value = match **ty {
|
||||
Ty::Bool => quote_spanned! { ty.span()=>
|
||||
matches.is_present(#name)
|
||||
},
|
||||
|
||||
Ty::Option => quote_spanned! { ty.span()=>
|
||||
matches.#value_of(#name)
|
||||
.map(#parse)
|
||||
},
|
||||
|
||||
Ty::OptionOption => quote_spanned! { ty.span()=>
|
||||
if matches.is_present(#name) {
|
||||
Some(matches.#value_of(#name).map(#parse))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|
||||
Ty::OptionVec => quote_spanned! { ty.span()=>
|
||||
if matches.is_present(#name) {
|
||||
Some(matches.#values_of(#name)
|
||||
.map(|v| v.map(#parse).collect())
|
||||
.unwrap_or_else(Vec::new))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
|
||||
Ty::Vec => quote_spanned! { ty.span()=>
|
||||
matches.#values_of(#name)
|
||||
.map(|v| v.map(#parse).collect())
|
||||
.unwrap_or_else(Vec::new)
|
||||
},
|
||||
|
||||
Ty::Other if occurrences => quote_spanned! { ty.span()=>
|
||||
#parse(matches.#value_of(#name))
|
||||
},
|
||||
|
||||
Ty::Other if flag => quote_spanned! { ty.span()=>
|
||||
#parse(matches.is_present(#name))
|
||||
},
|
||||
|
||||
Ty::Other => quote_spanned! { ty.span()=>
|
||||
matches.#value_of(#name)
|
||||
.map(#parse)
|
||||
.unwrap()
|
||||
},
|
||||
};
|
||||
|
||||
quote_spanned!(field.span()=> #field_name: #field_value )
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {{
|
||||
#( #fields ),*
|
||||
}}
|
||||
}
|
||||
|
||||
pub fn gen_from_argmatches_impl_for_enum(name: &syn::Ident) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
impl ::clap::FromArgMatches for #name {
|
||||
fn from_argmatches(matches: &::clap::ArgMatches) -> Self {
|
||||
<#name>::from_subcommand(matches.subcommand())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::clap::ArgMatches> for #name {
|
||||
fn from(m: ::clap::ArgMatches) -> Self {
|
||||
use ::clap::FromArgMatches;
|
||||
<Self as ::clap::FromArgMatches>::from_argmatches(&m)
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: impl TryFrom once stable
|
||||
}
|
||||
}
|
132
clap_derive/src/derives/into_app.rs
Normal file
132
clap_derive/src/derives/into_app.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
use std::env;
|
||||
|
||||
use proc_macro2;
|
||||
use syn;
|
||||
|
||||
use super::{spanned::Sp, Attrs, GenOutput, Name, DEFAULT_CASING};
|
||||
|
||||
pub fn derive_into_app(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||
use syn::Data::*;
|
||||
|
||||
let struct_name = &input.ident;
|
||||
let inner_impl = match input.data {
|
||||
Struct(syn::DataStruct { .. }) => {
|
||||
gen_into_app_impl_for_struct(struct_name, &input.attrs).tokens
|
||||
}
|
||||
// @TODO impl into_app for enums?
|
||||
// Enum(ref e) => clap_for_enum_impl(struct_name, &e.variants, &input.attrs),
|
||||
_ => panic!("clap_derive only supports non-tuple structs"), // and enums"),
|
||||
};
|
||||
|
||||
quote!(#inner_impl)
|
||||
}
|
||||
|
||||
pub fn gen_into_app_impl_for_struct(name: &syn::Ident, attrs: &[syn::Attribute]) -> GenOutput {
|
||||
let into_app_fn = gen_into_app_fn_for_struct(attrs);
|
||||
let into_app_fn_tokens = into_app_fn.tokens;
|
||||
|
||||
let tokens = quote! {
|
||||
impl ::clap::IntoApp for #name {
|
||||
#into_app_fn_tokens
|
||||
}
|
||||
|
||||
impl<'b> Into<::clap::App<'b>> for #name {
|
||||
fn into(self) -> ::clap::App<'b> {
|
||||
use ::clap::IntoApp;
|
||||
<#name as ::clap::IntoApp>::into_app()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GenOutput {
|
||||
tokens,
|
||||
attrs: into_app_fn.attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_into_app_fn_for_struct(struct_attrs: &[syn::Attribute]) -> GenOutput {
|
||||
let gen = gen_app_builder(struct_attrs);
|
||||
let app_tokens = gen.tokens;
|
||||
|
||||
let tokens = quote! {
|
||||
fn into_app<'b>() -> ::clap::App<'b> {
|
||||
Self::augment_app(#app_tokens)
|
||||
}
|
||||
};
|
||||
|
||||
GenOutput {
|
||||
tokens,
|
||||
attrs: gen.attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_app_builder(attrs: &[syn::Attribute]) -> GenOutput {
|
||||
let name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
|
||||
|
||||
let attrs = Attrs::from_struct(
|
||||
proc_macro2::Span::call_site(),
|
||||
attrs,
|
||||
Name::Assigned(syn::LitStr::new(&name, proc_macro2::Span::call_site())),
|
||||
Sp::call_site(DEFAULT_CASING),
|
||||
);
|
||||
let tokens = {
|
||||
let name = attrs.cased_name();
|
||||
quote!(::clap::App::new(#name))
|
||||
};
|
||||
|
||||
GenOutput { tokens, attrs }
|
||||
}
|
||||
|
||||
pub fn gen_into_app_impl_for_enum(name: &syn::Ident, attrs: &[syn::Attribute]) -> GenOutput {
|
||||
let into_app_fn = gen_into_app_fn_for_enum(attrs);
|
||||
let into_app_fn_tokens = into_app_fn.tokens;
|
||||
|
||||
let tokens = quote! {
|
||||
impl ::clap::IntoApp for #name {
|
||||
#into_app_fn_tokens
|
||||
}
|
||||
|
||||
impl<'b> Into<::clap::App<'b>> for #name {
|
||||
fn into(self) -> ::clap::App<'b> {
|
||||
use ::clap::IntoApp;
|
||||
<#name as ::clap::IntoApp>::into_app()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GenOutput {
|
||||
tokens,
|
||||
attrs: into_app_fn.attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_into_app_fn_for_enum(enum_attrs: &[syn::Attribute]) -> GenOutput {
|
||||
let gen = gen_app_builder(enum_attrs);
|
||||
let app_tokens = gen.tokens;
|
||||
|
||||
let tokens = quote! {
|
||||
fn into_app<'b>() -> ::clap::App<'b> {
|
||||
let app = #app_tokens
|
||||
.setting(::clap::AppSettings::SubcommandRequiredElseHelp);
|
||||
Self::augment_app(app)
|
||||
}
|
||||
};
|
||||
|
||||
GenOutput {
|
||||
tokens,
|
||||
attrs: gen.attrs,
|
||||
}
|
||||
}
|
30
clap_derive/src/derives/mod.rs
Normal file
30
clap_derive/src/derives/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
use syn;
|
||||
|
||||
pub mod arg_enum;
|
||||
pub mod attrs;
|
||||
pub mod parse;
|
||||
pub mod spanned;
|
||||
pub mod ty;
|
||||
mod clap;
|
||||
mod from_argmatches;
|
||||
mod into_app;
|
||||
|
||||
pub use self::arg_enum::derive_arg_enum;
|
||||
pub use self::attrs::{Attrs, Kind, Name, Parser, ParserKind, CasingStyle, GenOutput, DEFAULT_CASING};
|
||||
pub use self::ty::{sub_type, Ty};
|
||||
pub use self::clap::derive_clap;
|
||||
pub use self::from_argmatches::derive_from_argmatches;
|
||||
pub use self::into_app::derive_into_app;
|
268
clap_derive/src/derives/parse.rs
Normal file
268
clap_derive/src/derives/parse.rs
Normal file
|
@ -0,0 +1,268 @@
|
|||
use std::iter::FromIterator;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro_error::{abort, ResultExt};
|
||||
use syn::{
|
||||
self, parenthesized,
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
parse2,
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
Attribute, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Token,
|
||||
};
|
||||
|
||||
pub struct ClapAttributes {
|
||||
pub paren_token: syn::token::Paren,
|
||||
pub attrs: Punctuated<ClapAttr, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for ClapAttributes {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
let paren_token = parenthesized!(content in input);
|
||||
let attrs = content.parse_terminated(ClapAttr::parse)?;
|
||||
|
||||
Ok(ClapAttributes { paren_token, attrs })
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ClapAttr {
|
||||
// single-identifier attributes
|
||||
Short(Ident),
|
||||
Long(Ident),
|
||||
Flatten(Ident),
|
||||
Subcommand(Ident),
|
||||
NoVersion(Ident),
|
||||
|
||||
// ident [= "string literal"]
|
||||
About(Ident, Option<LitStr>),
|
||||
Author(Ident, Option<LitStr>),
|
||||
|
||||
// ident = "string literal"
|
||||
Version(Ident, LitStr),
|
||||
RenameAll(Ident, LitStr),
|
||||
NameLitStr(Ident, LitStr),
|
||||
|
||||
// parse(parser_kind [= parser_func])
|
||||
Parse(Ident, ParserSpec),
|
||||
|
||||
// ident [= arbitrary_expr]
|
||||
Skip(Ident, Option<Expr>),
|
||||
|
||||
// ident = arbitrary_expr
|
||||
NameExpr(Ident, Expr),
|
||||
|
||||
// ident(arbitrary_expr,*)
|
||||
MethodCall(Ident, Vec<Expr>),
|
||||
}
|
||||
|
||||
impl Parse for ClapAttr {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
use self::ClapAttr::*;
|
||||
|
||||
let name: Ident = input.parse()?;
|
||||
let name_str = name.to_string();
|
||||
|
||||
if input.peek(Token![=]) {
|
||||
// `name = value` attributes.
|
||||
let assign_token = input.parse::<Token![=]>()?; // skip '='
|
||||
|
||||
if input.peek(LitStr) {
|
||||
let lit: LitStr = input.parse()?;
|
||||
let lit_str = lit.value();
|
||||
|
||||
let check_empty_lit = |s| {
|
||||
if lit_str.is_empty() {
|
||||
abort!(
|
||||
lit.span(),
|
||||
"`#[clap({} = \"\")` is deprecated, \
|
||||
now it's default behavior",
|
||||
s
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
match &*name_str.to_string() {
|
||||
"rename_all" => Ok(RenameAll(name, lit)),
|
||||
|
||||
"version" => {
|
||||
check_empty_lit("version");
|
||||
Ok(Version(name, lit))
|
||||
}
|
||||
|
||||
"author" => {
|
||||
check_empty_lit("author");
|
||||
Ok(Author(name, Some(lit)))
|
||||
}
|
||||
|
||||
"about" => {
|
||||
check_empty_lit("about");
|
||||
Ok(About(name, Some(lit)))
|
||||
}
|
||||
|
||||
"skip" => {
|
||||
let expr = ExprLit {
|
||||
attrs: vec![],
|
||||
lit: Lit::Str(lit),
|
||||
};
|
||||
let expr = Expr::Lit(expr);
|
||||
Ok(Skip(name, Some(expr)))
|
||||
}
|
||||
|
||||
_ => Ok(NameLitStr(name, lit)),
|
||||
}
|
||||
} else {
|
||||
match input.parse::<Expr>() {
|
||||
Ok(expr) => {
|
||||
if name_str == "skip" {
|
||||
Ok(Skip(name, Some(expr)))
|
||||
} else {
|
||||
Ok(NameExpr(name, expr))
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => abort! {
|
||||
assign_token.span(),
|
||||
"expected `string literal` or `expression` after `=`"
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if input.peek(syn::token::Paren) {
|
||||
// `name(...)` attributes.
|
||||
let nested;
|
||||
parenthesized!(nested in input);
|
||||
|
||||
match name_str.as_ref() {
|
||||
"parse" => {
|
||||
let parser_specs: Punctuated<ParserSpec, Token![,]> =
|
||||
nested.parse_terminated(ParserSpec::parse)?;
|
||||
|
||||
if parser_specs.len() == 1 {
|
||||
Ok(Parse(name, parser_specs[0].clone()))
|
||||
} else {
|
||||
abort!(name.span(), "parse must have exactly one argument")
|
||||
}
|
||||
}
|
||||
|
||||
"raw" => match nested.parse::<LitBool>() {
|
||||
Ok(bool_token) => {
|
||||
let expr = ExprLit {
|
||||
attrs: vec![],
|
||||
lit: Lit::Bool(bool_token),
|
||||
};
|
||||
let expr = Expr::Lit(expr);
|
||||
Ok(MethodCall(name, vec![expr]))
|
||||
}
|
||||
|
||||
Err(_) => {
|
||||
abort!(name.span(),
|
||||
"`#[clap(raw(...))` attributes are removed, \
|
||||
they are replaced with raw methods";
|
||||
help = "if you meant to call `clap::Arg::raw()` method \
|
||||
you should use bool literal, like `raw(true)` or `raw(false)`";
|
||||
note = raw_method_suggestion(nested);
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_ => {
|
||||
let method_args: Punctuated<_, Token![,]> =
|
||||
nested.parse_terminated(Expr::parse)?;
|
||||
Ok(MethodCall(name, Vec::from_iter(method_args)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Attributes represented with a sole identifier.
|
||||
match name_str.as_ref() {
|
||||
"long" => Ok(Long(name)),
|
||||
"short" => Ok(Short(name)),
|
||||
"flatten" => Ok(Flatten(name)),
|
||||
"subcommand" => Ok(Subcommand(name)),
|
||||
"no_version" => Ok(NoVersion(name)),
|
||||
|
||||
"about" => (Ok(About(name, None))),
|
||||
"author" => (Ok(Author(name, None))),
|
||||
|
||||
"skip" => Ok(Skip(name, None)),
|
||||
|
||||
"version" => abort!(
|
||||
name.span(),
|
||||
"#[clap(version)] is invalid attribute, \
|
||||
clap_derive inherits version from Cargo.toml by default, \
|
||||
no attribute needed"
|
||||
),
|
||||
|
||||
_ => abort!(name.span(), "unexpected attribute: {}", name_str),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParserSpec {
|
||||
pub kind: Ident,
|
||||
pub eq_token: Option<Token![=]>,
|
||||
pub parse_func: Option<Expr>,
|
||||
}
|
||||
|
||||
impl Parse for ParserSpec {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let kind = input
|
||||
.parse()
|
||||
.map_err(|_| input.error("parser specification must start with identifier"))?;
|
||||
let eq_token = input.parse()?;
|
||||
let parse_func = match eq_token {
|
||||
None => None,
|
||||
Some(_) => Some(input.parse()?),
|
||||
};
|
||||
Ok(ParserSpec {
|
||||
kind,
|
||||
eq_token,
|
||||
parse_func,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_method_suggestion(ts: ParseBuffer) -> String {
|
||||
let do_parse = move || -> Result<(Ident, TokenStream), syn::Error> {
|
||||
let name = ts.parse()?;
|
||||
let _eq: Token![=] = ts.parse()?;
|
||||
let val: LitStr = ts.parse()?;
|
||||
Ok((name, syn::parse_str(&val.value())?))
|
||||
};
|
||||
if let Ok((name, val)) = do_parse() {
|
||||
let val = val.to_string().replace(" ", "").replace(",", ", ");
|
||||
format!(
|
||||
"if you need to call `clap::Arg/App::{}` method you \
|
||||
can do it like this: #[clap({}({}))]",
|
||||
name, name, val
|
||||
)
|
||||
} else {
|
||||
"if you need to call some method from `clap::Arg/App` \
|
||||
you should use raw method, see \
|
||||
https://docs.rs/structopt/0.3/structopt/#raw-methods"
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec<ClapAttr> {
|
||||
all_attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path.is_ident("clap"))
|
||||
.flat_map(|attr| {
|
||||
let attrs: ClapAttributes = parse2(attr.tokens.clone())
|
||||
.map_err(|e| match &*e.to_string() {
|
||||
// this error message is misleading and points to Span::call_site()
|
||||
// so we patch it with something meaningful
|
||||
"unexpected end of input, expected parentheses" => {
|
||||
let span = attr.path.span();
|
||||
let patch_msg = "expected parentheses after `clap`";
|
||||
syn::Error::new(span, patch_msg)
|
||||
}
|
||||
_ => e,
|
||||
})
|
||||
.unwrap_or_abort();
|
||||
attrs.attrs
|
||||
})
|
||||
.collect()
|
||||
}
|
101
clap_derive/src/derives/spanned.rs
Normal file
101
clap_derive/src/derives/spanned.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use syn::LitStr;
|
||||
|
||||
/// An entity with a span attached.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Sp<T> {
|
||||
span: Span,
|
||||
val: T,
|
||||
}
|
||||
|
||||
impl<T> Sp<T> {
|
||||
pub fn new(val: T, span: Span) -> Self {
|
||||
Sp { val, span }
|
||||
}
|
||||
|
||||
pub fn call_site(val: T) -> Self {
|
||||
Sp {
|
||||
val,
|
||||
span: Span::call_site(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString> Sp<T> {
|
||||
pub fn as_ident(&self) -> Ident {
|
||||
Ident::new(&self.to_string(), self.span)
|
||||
}
|
||||
|
||||
pub fn as_lit(&self) -> LitStr {
|
||||
LitStr::new(&self.to_string(), self.span)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Sp<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Sp<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ident> for Sp<String> {
|
||||
fn from(ident: Ident) -> Self {
|
||||
Sp {
|
||||
val: ident.to_string(),
|
||||
span: ident.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LitStr> for Sp<String> {
|
||||
fn from(lit: LitStr) -> Self {
|
||||
Sp {
|
||||
val: lit.value(),
|
||||
span: lit.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Sp<&'a str>> for Sp<String> {
|
||||
fn from(sp: Sp<&'a str>) -> Self {
|
||||
Sp::new(sp.val.into(), sp.span)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq for Sp<T> {
|
||||
fn eq(&self, other: &Sp<T>) -> bool {
|
||||
self.val == other.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> AsRef<str> for Sp<T> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.val.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTokens> ToTokens for Sp<T> {
|
||||
fn to_tokens(&self, stream: &mut TokenStream) {
|
||||
// this is the simplest way out of correct ones to change span on
|
||||
// arbitrary token tree I can come up with
|
||||
let tt = self.val.to_token_stream().into_iter().map(|mut tt| {
|
||||
tt.set_span(self.span.clone());
|
||||
tt
|
||||
});
|
||||
|
||||
stream.extend(tt);
|
||||
}
|
||||
}
|
108
clap_derive/src/derives/ty.rs
Normal file
108
clap_derive/src/derives/ty.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
//! Special types handling
|
||||
|
||||
use super::spanned::Sp;
|
||||
|
||||
use syn::{
|
||||
spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed,
|
||||
PathSegment, Type, TypePath,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum Ty {
|
||||
Bool,
|
||||
Vec,
|
||||
Option,
|
||||
OptionOption,
|
||||
OptionVec,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
pub fn from_syn_ty(ty: &syn::Type) -> Sp<Self> {
|
||||
use self::Ty::*;
|
||||
let t = |kind| Sp::new(kind, ty.span());
|
||||
|
||||
if is_simple_ty(ty, "bool") {
|
||||
t(Bool)
|
||||
} else if is_generic_ty(ty, "Vec") {
|
||||
t(Vec)
|
||||
} else if let Some(subty) = subty_if_name(ty, "Option") {
|
||||
if is_generic_ty(subty, "Option") {
|
||||
t(OptionOption)
|
||||
} else if is_generic_ty(subty, "Vec") {
|
||||
t(OptionVec)
|
||||
} else {
|
||||
t(Option)
|
||||
}
|
||||
} else {
|
||||
t(Other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> {
|
||||
subty_if(ty, |_| true)
|
||||
}
|
||||
|
||||
fn only_last_segment(ty: &syn::Type) -> Option<&PathSegment> {
|
||||
match ty {
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path:
|
||||
Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
},
|
||||
}) => only_one(segments.iter()),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type>
|
||||
where
|
||||
F: FnOnce(&PathSegment) -> bool,
|
||||
{
|
||||
only_last_segment(ty)
|
||||
.filter(|segment| f(segment))
|
||||
.and_then(|segment| {
|
||||
if let AngleBracketed(args) = &segment.arguments {
|
||||
only_one(args.args.iter()).and_then(|genneric| {
|
||||
if let GenericArgument::Type(ty) = genneric {
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> {
|
||||
subty_if(ty, |seg| seg.ident == name)
|
||||
}
|
||||
|
||||
fn is_simple_ty(ty: &syn::Type, name: &str) -> bool {
|
||||
only_last_segment(ty)
|
||||
.map(|segment| {
|
||||
if let PathArguments::None = segment.arguments {
|
||||
segment.ident == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_generic_ty(ty: &syn::Type, name: &str) -> bool {
|
||||
subty_if_name(ty, name).is_some()
|
||||
}
|
||||
|
||||
fn only_one<I, T>(mut iter: I) -> Option<T>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
{
|
||||
iter.next().filter(|_| iter.next().is_none())
|
||||
}
|
61
clap_derive/src/lib.rs
Normal file
61
clap_derive/src/lib.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
//! This crate is custom derive for clap. It should not be used
|
||||
//! directly. See [clap documentation](https://docs.rs/clap)
|
||||
//! for the usage of `#[derive(Clap)]`.
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate heck;
|
||||
extern crate proc_macro2;
|
||||
extern crate proc_macro_error;
|
||||
|
||||
use proc_macro_error::proc_macro_error;
|
||||
|
||||
mod derives;
|
||||
|
||||
// /// It is required to have this seperate and specificly defined.
|
||||
// #[proc_macro_derive(ArgEnum, attributes(case_sensitive))]
|
||||
// pub fn arg_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
// let input: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
// derives::derive_arg_enum(&input).into()
|
||||
// }
|
||||
|
||||
/// Generates the `Clap` impl.
|
||||
#[proc_macro_derive(Clap, attributes(clap))]
|
||||
#[proc_macro_error]
|
||||
pub fn clap(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
derives::derive_clap(&input).into()
|
||||
}
|
||||
|
||||
/// Generates the `IntoApp` impl.
|
||||
#[proc_macro_derive(IntoApp, attributes(clap))]
|
||||
#[proc_macro_error]
|
||||
pub fn into_app(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
derives::derive_into_app(&input).into()
|
||||
}
|
||||
|
||||
/// Generates the `FromArgMatches` impl.
|
||||
#[proc_macro_derive(FromArgMatches)]
|
||||
#[proc_macro_error]
|
||||
pub fn from_argmatches(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
derives::derive_from_argmatches(&input).into()
|
||||
}
|
54
clap_derive/tests/arg_enum_basic.rs
Normal file
54
clap_derive/tests/arg_enum_basic.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// // Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
// //
|
||||
// // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// // option. This file may not be copied, modified, or distributed
|
||||
// // except according to those terms.
|
||||
// #[macro_use]
|
||||
// extern crate clap;
|
||||
// #[macro_use]
|
||||
// extern crate clap_derive;
|
||||
|
||||
// use clap::{App, Arg};
|
||||
|
||||
// #[derive(ArgEnum, Debug, PartialEq)]
|
||||
// enum ArgChoice {
|
||||
// Foo,
|
||||
// Bar,
|
||||
// Baz,
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn when_lowercase() {
|
||||
// let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||
// .arg(
|
||||
// Arg::with_name("arg")
|
||||
// .required(true)
|
||||
// .takes_value(true)
|
||||
// .possible_values(&ArgChoice::variants()),
|
||||
// )
|
||||
// .try_get_matches_from(vec!["", "foo"])
|
||||
// .unwrap();
|
||||
// let t = value_t!(matches.value_of("arg"), ArgChoice);
|
||||
// assert!(t.is_ok());
|
||||
// assert_eq!(t.unwrap(), ArgChoice::Foo);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn when_capitalized() {
|
||||
// let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||
// .arg(
|
||||
// Arg::with_name("arg")
|
||||
// .required(true)
|
||||
// .takes_value(true)
|
||||
// .possible_values(&ArgChoice::variants()),
|
||||
// )
|
||||
// .try_get_matches_from(vec!["", "Foo"])
|
||||
// .unwrap();
|
||||
// let t = value_t!(matches.value_of("arg"), ArgChoice);
|
||||
// assert!(t.is_ok());
|
||||
// assert_eq!(t.unwrap(), ArgChoice::Foo);
|
||||
// }
|
51
clap_derive/tests/arg_enum_case_sensitive.rs
Normal file
51
clap_derive/tests/arg_enum_case_sensitive.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// // Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
// //
|
||||
// // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// // option. This file may not be copied, modified, or distributed
|
||||
// // except according to those terms.
|
||||
// #[macro_use]
|
||||
// extern crate clap;
|
||||
|
||||
// use clap::{App, Arg, ArgEnum};
|
||||
|
||||
// #[derive(ArgEnum, Debug, PartialEq)]
|
||||
// #[case_sensitive]
|
||||
// enum ArgChoice {
|
||||
// Foo,
|
||||
// Bar,
|
||||
// Baz,
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn when_lowercase() {
|
||||
// let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||
// .arg(
|
||||
// Arg::with_name("arg")
|
||||
// .required(true)
|
||||
// .takes_value(true)
|
||||
// .possible_values(&ArgChoice::variants()),
|
||||
// )
|
||||
// .try_get_matches_from(vec!["", "foo"]); // We expect this to fail.
|
||||
// assert!(matches.is_err());
|
||||
// assert_eq!(matches.unwrap_err().kind, clap::ErrorKind::InvalidValue);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn when_capitalized() {
|
||||
// let matches = App::new(env!("CARGO_PKG_NAME"))
|
||||
// .arg(
|
||||
// Arg::with_name("arg")
|
||||
// .required(true)
|
||||
// .takes_value(true)
|
||||
// .possible_values(&ArgChoice::variants()),
|
||||
// )
|
||||
// .try_get_matches_from(vec!["", "Foo"])
|
||||
// .unwrap();
|
||||
// let t = value_t!(matches.value_of("arg"), ArgChoice);
|
||||
// assert!(t.is_ok());
|
||||
// assert_eq!(t.unwrap(), ArgChoice::Foo);
|
||||
// }
|
290
clap_derive/tests/argument_naming.rs
Normal file
290
clap_derive/tests/argument_naming.rs
Normal file
|
@ -0,0 +1,290 @@
|
|||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn test_single_word_enum_variant_is_default_renamed_into_kebab_case() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
enum Opt {
|
||||
Command { foo: u32 },
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::Command { foo: 0 },
|
||||
Opt::parse_from(&["test", "command", "0"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_word_enum_variant_is_renamed() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
enum Opt {
|
||||
FirstCommand { foo: u32 },
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::FirstCommand { foo: 0 },
|
||||
Opt::parse_from(&["test", "first-command", "0"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_long_generates_kebab_case() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Opt {
|
||||
#[clap(long)]
|
||||
FOO_OPTION: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt { FOO_OPTION: true },
|
||||
Opt::parse_from(&["test", "--foo-option"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_long_overwrites_default_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(long = "foo")]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt { foo_option: true },
|
||||
Opt::parse_from(&["test", "--foo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_long_uses_previous_defined_custom_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(name = "foo", long)]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt { foo_option: true },
|
||||
Opt::parse_from(&["test", "--foo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_long_ignores_afterwards_defined_custom_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(long, name = "foo")]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt { foo_option: true },
|
||||
Opt::parse_from(&["test", "--foo-option"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_short_generates_kebab_case() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
FOO_OPTION: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { FOO_OPTION: true }, Opt::parse_from(&["test", "-f"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_short_overwrites_default_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(short = "o")]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { foo_option: true }, Opt::parse_from(&["test", "-o"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_short_uses_previous_defined_custom_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(name = "option", short)]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { foo_option: true }, Opt::parse_from(&["test", "-o"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_short_ignores_afterwards_defined_custom_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(short, name = "option")]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { foo_option: true }, Opt::parse_from(&["test", "-f"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_long_uses_previous_defined_casing() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(rename_all = "screaming_snake", long)]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt { foo_option: true },
|
||||
Opt::parse_from(&["test", "--FOO_OPTION"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_short_uses_previous_defined_casing() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(rename_all = "screaming_snake", short)]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { foo_option: true }, Opt::parse_from(&["test", "-F"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_long_works_with_verbatim_casing() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[allow(non_snake_case)]
|
||||
struct Opt {
|
||||
#[clap(rename_all = "verbatim", long)]
|
||||
_fOO_oPtiON: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt { _fOO_oPtiON: true },
|
||||
Opt::parse_from(&["test", "--_fOO_oPtiON"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standalone_short_works_with_verbatim_casing() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(rename_all = "verbatim", short)]
|
||||
_foo: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { _foo: true }, Opt::parse_from(&["test", "-_"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_all_is_propagated_from_struct_to_fields() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
struct Opt {
|
||||
#[clap(long)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { foo: true }, Opt::parse_from(&["test", "--FOO"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_all_is_not_propagated_from_struct_into_flattened() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
struct Opt {
|
||||
#[clap(flatten)]
|
||||
foo: Foo,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Foo {
|
||||
#[clap(long)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
foo: Foo { foo: true }
|
||||
},
|
||||
Opt::parse_from(&["test", "--foo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_all_is_not_propagated_from_struct_into_subcommand() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
struct Opt {
|
||||
#[clap(subcommand)]
|
||||
foo: Foo,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
enum Foo {
|
||||
Command {
|
||||
#[clap(long)]
|
||||
foo: bool,
|
||||
},
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
foo: Foo::Command { foo: true }
|
||||
},
|
||||
Opt::parse_from(&["test", "command", "--foo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_all_is_propagated_from_enum_to_variants_and_their_fields() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
enum Opt {
|
||||
FirstVariant,
|
||||
SecondVariant {
|
||||
#[clap(long)]
|
||||
foo: bool,
|
||||
},
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::FirstVariant,
|
||||
Opt::parse_from(&["test", "FIRST_VARIANT"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt::SecondVariant { foo: true },
|
||||
Opt::parse_from(&["test", "SECOND_VARIANT", "--FOO"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_all_is_propagation_can_be_overridden() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
enum Opt {
|
||||
#[clap(rename_all = "kebab_case")]
|
||||
FirstVariant {
|
||||
#[clap(long)]
|
||||
foo_option: bool,
|
||||
},
|
||||
SecondVariant {
|
||||
#[clap(rename_all = "kebab_case", long)]
|
||||
foo_option: bool,
|
||||
},
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::FirstVariant { foo_option: true },
|
||||
Opt::parse_from(&["test", "first-variant", "--foo-option"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt::SecondVariant { foo_option: true },
|
||||
Opt::parse_from(&["test", "SECOND_VARIANT", "--foo-option"])
|
||||
);
|
||||
}
|
85
clap_derive/tests/arguments.rs
Normal file
85
clap_derive/tests/arguments.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn required_argument() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test", "42"]));
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_argument() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
arg: Option<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: Some(42) }, Opt::parse_from(&["test", "42"]));
|
||||
assert_eq!(Opt { arg: None }, Opt::parse_from(&["test"]));
|
||||
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_with_default() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(default_value = "42")]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 24 }, Opt::parse_from(&["test", "24"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test"]));
|
||||
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "24"]));
|
||||
assert_eq!(Opt { arg: vec![] }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::parse_from(&["test", "24", "42"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_safe() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24] },
|
||||
Opt::try_parse_from(&["test", "24"]).unwrap()
|
||||
);
|
||||
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::try_parse_from(&["test", "24", "42"]).unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
clap::ErrorKind::ValueValidation,
|
||||
Opt::try_parse_from(&["test", "NOPE"]).err().unwrap().kind
|
||||
);
|
||||
}
|
52
clap_derive/tests/author_version_about.rs
Normal file
52
clap_derive/tests/author_version_about.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
mod utils;
|
||||
|
||||
use clap::Clap;
|
||||
use utils::*;
|
||||
|
||||
#[test]
|
||||
fn no_author_version_about() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
#[clap(name = "foo", no_version)]
|
||||
struct Opt {}
|
||||
|
||||
let output = get_long_help::<Opt>();
|
||||
assert!(output.starts_with("foo \n\nUSAGE:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_env() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
#[clap(author, about)]
|
||||
struct Opt {}
|
||||
|
||||
let output = get_long_help::<Opt>();
|
||||
assert!(output.starts_with("clap_derive"));
|
||||
assert!(output.contains("Guillaume Pinot <texitoi@texitoi.eu>, Kevin K. <kbknapp@gmail.com>"));
|
||||
assert!(output.contains("Parse command line argument by defining a struct, derive crate"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_version_not_str() {
|
||||
const VERSION: &str = "custom version";
|
||||
|
||||
#[derive(Clap)]
|
||||
#[clap(version = VERSION)]
|
||||
pub struct Opt {}
|
||||
|
||||
let output = get_long_help::<Opt>();
|
||||
assert!(output.contains("custom version"));
|
||||
}
|
30
clap_derive/tests/basic.rs
Normal file
30
clap_derive/tests/basic.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short = "a", long = "arg")]
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "-a24"]));
|
||||
assert_eq!(Opt { arg: vec![] }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::parse_from(&["test", "-a24", "--arg", "42"])
|
||||
);
|
||||
}
|
309
clap_derive/tests/custom-string-parsers.rs
Normal file
309
clap_derive/tests/custom-string-parsers.rs
Normal file
|
@ -0,0 +1,309 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
use std::ffi::{CString, OsStr, OsString};
|
||||
use std::num::ParseIntError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct PathOpt {
|
||||
#[clap(short, long, parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
#[clap(short, default_value = "../", parse(from_os_str))]
|
||||
default_path: PathBuf,
|
||||
|
||||
#[clap(short, parse(from_os_str))]
|
||||
vector_path: Vec<PathBuf>,
|
||||
|
||||
#[clap(short, parse(from_os_str))]
|
||||
option_path_1: Option<PathBuf>,
|
||||
|
||||
#[clap(short = "q", parse(from_os_str))]
|
||||
option_path_2: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_opt_simple() {
|
||||
assert_eq!(
|
||||
PathOpt {
|
||||
path: PathBuf::from("/usr/bin"),
|
||||
default_path: PathBuf::from("../"),
|
||||
vector_path: vec![
|
||||
PathBuf::from("/a/b/c"),
|
||||
PathBuf::from("/d/e/f"),
|
||||
PathBuf::from("/g/h/i"),
|
||||
],
|
||||
option_path_1: None,
|
||||
option_path_2: Some(PathBuf::from("j.zip")),
|
||||
},
|
||||
PathOpt::parse_from(&[
|
||||
"test", "-p", "/usr/bin", "-v", "/a/b/c", "-v", "/d/e/f", "-v", "/g/h/i", "-q",
|
||||
"j.zip",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
|
||||
u64::from_str_radix(input, 16)
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct HexOpt {
|
||||
#[clap(short, parse(try_from_str = parse_hex))]
|
||||
number: u64,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_hex() {
|
||||
assert_eq!(
|
||||
HexOpt { number: 5 },
|
||||
HexOpt::parse_from(&["test", "-n", "5"])
|
||||
);
|
||||
assert_eq!(
|
||||
HexOpt { number: 0xabcdef },
|
||||
HexOpt::parse_from(&["test", "-n", "abcdef"])
|
||||
);
|
||||
|
||||
let err = HexOpt::try_parse_from(&["test", "-n", "gg"]).unwrap_err();
|
||||
assert!(err.message.contains("invalid digit found in string"), err);
|
||||
}
|
||||
|
||||
fn custom_parser_1(_: &str) -> &'static str {
|
||||
"A"
|
||||
}
|
||||
fn custom_parser_2(_: &str) -> Result<&'static str, u32> {
|
||||
Ok("B")
|
||||
}
|
||||
fn custom_parser_3(_: &OsStr) -> &'static str {
|
||||
"C"
|
||||
}
|
||||
fn custom_parser_4(_: &OsStr) -> Result<&'static str, String> {
|
||||
Ok("D")
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct NoOpOpt {
|
||||
#[clap(short, parse(from_str = custom_parser_1))]
|
||||
a: &'static str,
|
||||
#[clap(short, parse(try_from_str = custom_parser_2))]
|
||||
b: &'static str,
|
||||
#[clap(short, parse(from_os_str = custom_parser_3))]
|
||||
c: &'static str,
|
||||
#[clap(short, parse(try_from_os_str = custom_parser_4))]
|
||||
d: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_every_custom_parser() {
|
||||
assert_eq!(
|
||||
NoOpOpt {
|
||||
a: "A",
|
||||
b: "B",
|
||||
c: "C",
|
||||
d: "D"
|
||||
},
|
||||
NoOpOpt::parse_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"])
|
||||
);
|
||||
}
|
||||
|
||||
// Note: can't use `Vec<u8>` directly, as clap would instead look for
|
||||
// conversion function from `&str` to `u8`.
|
||||
type Bytes = Vec<u8>;
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct DefaultedOpt {
|
||||
#[clap(short, parse(from_str))]
|
||||
bytes: Bytes,
|
||||
|
||||
#[clap(short, parse(try_from_str))]
|
||||
integer: u64,
|
||||
|
||||
#[clap(short, parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_with_default_value() {
|
||||
assert_eq!(
|
||||
DefaultedOpt {
|
||||
bytes: b"E\xc2\xb2=p\xc2\xb2c\xc2\xb2+m\xc2\xb2c\xe2\x81\xb4".to_vec(),
|
||||
integer: 9000,
|
||||
path: PathBuf::from("src/lib.rs"),
|
||||
},
|
||||
DefaultedOpt::parse_from(&[
|
||||
"test",
|
||||
"-b",
|
||||
"E²=p²c²+m²c⁴",
|
||||
"-i",
|
||||
"9000",
|
||||
"-p",
|
||||
"src/lib.rs",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Foo(u8);
|
||||
|
||||
fn foo(value: u64) -> Foo {
|
||||
Foo(value as u8)
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Occurrences {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
signed: i32,
|
||||
|
||||
#[clap(short, parse(from_occurrences))]
|
||||
little_signed: i8,
|
||||
|
||||
#[clap(short, parse(from_occurrences))]
|
||||
unsigned: usize,
|
||||
|
||||
#[clap(short = "r", parse(from_occurrences))]
|
||||
little_unsigned: u8,
|
||||
|
||||
#[clap(short, long, parse(from_occurrences = foo))]
|
||||
custom: Foo,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_occurrences() {
|
||||
assert_eq!(
|
||||
Occurrences {
|
||||
signed: 3,
|
||||
little_signed: 1,
|
||||
unsigned: 0,
|
||||
little_unsigned: 4,
|
||||
custom: Foo(5),
|
||||
},
|
||||
Occurrences::parse_from(&[
|
||||
"test", "-s", "--signed", "--signed", "-l", "-rrrr", "-cccc", "--custom",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_bool() {
|
||||
fn parse_bool(s: &str) -> Result<bool, String> {
|
||||
match s {
|
||||
"true" => Ok(true),
|
||||
"false" => Ok(false),
|
||||
_ => Err(format!("invalid bool {}", s)),
|
||||
}
|
||||
}
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, parse(try_from_str = parse_bool))]
|
||||
debug: bool,
|
||||
#[clap(
|
||||
short,
|
||||
default_value = "false",
|
||||
parse(try_from_str = parse_bool)
|
||||
)]
|
||||
verbose: bool,
|
||||
#[clap(short, parse(try_from_str = parse_bool))]
|
||||
tribool: Option<bool>,
|
||||
#[clap(short, parse(try_from_str = parse_bool))]
|
||||
bitset: Vec<bool>,
|
||||
}
|
||||
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-d"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-dfoo"]).is_err());
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: false,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dfalse"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dtrue"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dtrue", "-vfalse"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: true,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dtrue", "-vtrue"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: Some(false),
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dtrue", "-tfalse"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: Some(true),
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dtrue", "-ttrue"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![false, true, false, false],
|
||||
},
|
||||
Opt::parse_from(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cstring() {
|
||||
use clap::IntoApp;
|
||||
|
||||
#[derive(Clap)]
|
||||
struct Opt {
|
||||
#[clap(parse(try_from_str = CString::new))]
|
||||
c_string: CString,
|
||||
}
|
||||
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "bla"]).c_string.to_bytes(),
|
||||
b"bla"
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test", "bla\0bla"]).is_err());
|
||||
}
|
55
clap_derive/tests/deny-warnings.rs
Normal file
55
clap_derive/tests/deny-warnings.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate clap;
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
fn try_str(s: &str) -> Result<String, std::convert::Infallible> {
|
||||
Ok(s.into())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warning_never_struct() {
|
||||
#[derive(Debug, PartialEq, Clap)]
|
||||
struct Opt {
|
||||
#[clap(parse(try_from_str = try_str))]
|
||||
s: String,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
s: "foo".to_string()
|
||||
},
|
||||
Opt::parse_from(&["test", "foo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warning_never_enum() {
|
||||
#[derive(Debug, PartialEq, Clap)]
|
||||
enum Opt {
|
||||
Foo {
|
||||
#[clap(parse(try_from_str = try_str))]
|
||||
s: String,
|
||||
},
|
||||
}
|
||||
assert_eq!(
|
||||
Opt::Foo {
|
||||
s: "foo".to_string()
|
||||
},
|
||||
Opt::parse_from(&["test", "foo", "foo"])
|
||||
);
|
||||
}
|
116
clap_derive/tests/doc-comments-help.rs
Normal file
116
clap_derive/tests/doc-comments-help.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
mod utils;
|
||||
|
||||
use clap::Clap;
|
||||
use utils::*;
|
||||
|
||||
#[test]
|
||||
fn doc_comments() {
|
||||
/// Lorem ipsum
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct LoremIpsum {
|
||||
/// Fooify a bar
|
||||
/// and a baz
|
||||
#[clap(short, long)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
let help = get_long_help::<LoremIpsum>();
|
||||
assert!(help.contains("Lorem ipsum"));
|
||||
assert!(help.contains("Fooify a bar and a baz"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_is_better_than_comments() {
|
||||
/// Lorem ipsum
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
|
||||
struct LoremIpsum {
|
||||
/// Fooify a bar
|
||||
#[clap(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
let help = get_long_help::<LoremIpsum>();
|
||||
assert!(help.contains("Dolor sit amet"));
|
||||
assert!(!help.contains("Lorem ipsum"));
|
||||
assert!(help.contains("DO NOT PASS A BAR"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_line_in_doc_comment_is_double_linefeed() {
|
||||
/// Foo.
|
||||
///
|
||||
/// Bar
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
#[clap(name = "lorem-ipsum", no_version)]
|
||||
struct LoremIpsum {}
|
||||
|
||||
let help = get_long_help::<LoremIpsum>();
|
||||
assert!(help.starts_with("lorem-ipsum \nFoo.\n\nBar\n\nUSAGE:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_long_doc_comment_both_help_long_help() {
|
||||
/// Lorem ipsumclap
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
|
||||
struct LoremIpsum {
|
||||
/// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES.
|
||||
///
|
||||
/// Or something else
|
||||
#[clap(long)]
|
||||
foo: bool,
|
||||
}
|
||||
|
||||
let short_help = get_help::<LoremIpsum>();
|
||||
let long_help = get_long_help::<LoremIpsum>();
|
||||
|
||||
assert!(short_help.contains("CIRCUMSTANCES"));
|
||||
assert!(!short_help.contains("CIRCUMSTANCES."));
|
||||
assert!(!short_help.contains("Or something else"));
|
||||
assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
|
||||
assert!(long_help.contains("Or something else"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_long_doc_comment_both_help_long_help() {
|
||||
/// Lorem ipsumclap
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
|
||||
struct LoremIpsum {
|
||||
#[clap(subcommand)]
|
||||
foo: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub enum SubCommand {
|
||||
/// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
|
||||
///
|
||||
/// Or something else
|
||||
Foo {
|
||||
#[clap(help = "foo")]
|
||||
bars: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
let short_help = get_help::<LoremIpsum>();
|
||||
let long_help = get_subcommand_long_help::<LoremIpsum>("foo");
|
||||
|
||||
assert!(!short_help.contains("Or something else"));
|
||||
assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
|
||||
assert!(long_help.contains("Or something else"));
|
||||
}
|
32
clap_derive/tests/explicit_name_no_renaming.rs
Normal file
32
clap_derive/tests/explicit_name_no_renaming.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
mod utils;
|
||||
|
||||
use clap::Clap;
|
||||
use utils::*;
|
||||
|
||||
#[test]
|
||||
fn explicit_short_long_no_rename() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short = ".", long = ".foo")]
|
||||
foo: Vec<String>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
foo: vec!["short".into(), "long".into()]
|
||||
},
|
||||
Opt::parse_from(&["test", "-.", "short", "--.foo", "long"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_name_no_rename() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(name = ".options")]
|
||||
foo: Vec<String>,
|
||||
}
|
||||
|
||||
let help = get_long_help::<Opt>();
|
||||
assert!(help.contains("[.options]..."))
|
||||
}
|
145
clap_derive/tests/flags.rs
Normal file
145
clap_derive/tests/flags.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn unique_flag() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
alice: bool,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { alice: false }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(Opt { alice: true }, Opt::parse_from(&["test", "-a"]));
|
||||
assert_eq!(Opt { alice: true }, Opt::parse_from(&["test", "--alice"]));
|
||||
assert!(Opt::try_parse_from(&["test", "-i"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "-a"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "--alice"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_flag() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
alice: u64,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
bob: u8,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { alice: 0, bob: 0 }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(Opt { alice: 1, bob: 0 }, Opt::parse_from(&["test", "-a"]));
|
||||
assert_eq!(
|
||||
Opt { alice: 2, bob: 0 },
|
||||
Opt::parse_from(&["test", "-a", "-a"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: 2, bob: 2 },
|
||||
Opt::parse_from(&["test", "-a", "--alice", "-bb"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt { alice: 3, bob: 1 },
|
||||
Opt::parse_from(&["test", "-aaa", "--bob"])
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test", "-i"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
|
||||
}
|
||||
|
||||
fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool {
|
||||
std::sync::atomic::AtomicBool::new(b)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_bool_flags() {
|
||||
#[derive(Clap, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long, parse(from_flag = parse_from_flag))]
|
||||
alice: std::sync::atomic::AtomicBool,
|
||||
#[clap(short, long, parse(from_flag))]
|
||||
bob: std::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
let falsey = Opt::parse_from(&["test"]);
|
||||
assert!(!falsey.alice.load(std::sync::atomic::Ordering::Relaxed));
|
||||
assert!(!falsey.bob.load(std::sync::atomic::Ordering::Relaxed));
|
||||
|
||||
let alice = Opt::parse_from(&["test", "-a"]);
|
||||
assert!(alice.alice.load(std::sync::atomic::Ordering::Relaxed));
|
||||
assert!(!alice.bob.load(std::sync::atomic::Ordering::Relaxed));
|
||||
|
||||
let bob = Opt::parse_from(&["test", "-b"]);
|
||||
assert!(!bob.alice.load(std::sync::atomic::Ordering::Relaxed));
|
||||
assert!(bob.bob.load(std::sync::atomic::Ordering::Relaxed));
|
||||
|
||||
let both = Opt::parse_from(&["test", "-b", "-a"]);
|
||||
assert!(both.alice.load(std::sync::atomic::Ordering::Relaxed));
|
||||
assert!(both.bob.load(std::sync::atomic::Ordering::Relaxed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn combined_flags() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
alice: bool,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
bob: u64,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: false,
|
||||
bob: 0
|
||||
},
|
||||
Opt::parse_from(&["test"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 0
|
||||
},
|
||||
Opt::parse_from(&["test", "-a"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 0
|
||||
},
|
||||
Opt::parse_from(&["test", "-a"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: false,
|
||||
bob: 1
|
||||
},
|
||||
Opt::parse_from(&["test", "-b"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 1
|
||||
},
|
||||
Opt::parse_from(&["test", "--alice", "--bob"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
alice: true,
|
||||
bob: 4
|
||||
},
|
||||
Opt::parse_from(&["test", "-bb", "-a", "-bb"])
|
||||
);
|
||||
}
|
99
clap_derive/tests/flatten.rs
Normal file
99
clap_derive/tests/flatten.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Common {
|
||||
arg: i32,
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(flatten)]
|
||||
common: Common,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
common: Common { arg: 42 }
|
||||
},
|
||||
Opt::parse_from(&["test", "42"])
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn flatten_twice() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Common {
|
||||
arg: i32,
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(flatten)]
|
||||
c1: Common,
|
||||
// Defines "arg" twice, so this should not work.
|
||||
#[clap(flatten)]
|
||||
c2: Common,
|
||||
}
|
||||
Opt::parse_from(&["test", "42", "43"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_in_subcommand() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Common {
|
||||
arg: i32,
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Add {
|
||||
#[clap(short)]
|
||||
interactive: bool,
|
||||
#[clap(flatten)]
|
||||
common: Common,
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Opt {
|
||||
Fetch {
|
||||
#[clap(short)]
|
||||
all: bool,
|
||||
#[clap(flatten)]
|
||||
common: Common,
|
||||
},
|
||||
|
||||
Add(Add),
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::Fetch {
|
||||
all: false,
|
||||
common: Common { arg: 42 }
|
||||
},
|
||||
Opt::parse_from(&["test", "fetch", "42"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt::Add(Add {
|
||||
interactive: true,
|
||||
common: Common { arg: 43 }
|
||||
}),
|
||||
Opt::parse_from(&["test", "add", "-i", "43"])
|
||||
);
|
||||
}
|
50
clap_derive/tests/issues.rs
Normal file
50
clap_derive/tests/issues.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
// https://github.com/TeXitoi/structopt/issues/151
|
||||
// https://github.com/TeXitoi/structopt/issues/289
|
||||
|
||||
#[test]
|
||||
fn issue_151() {
|
||||
use clap::{ArgGroup, Clap, IntoApp};
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(group = ArgGroup::with_name("verb").required(true).multiple(true))]
|
||||
struct Opt {
|
||||
#[clap(long, group = "verb")]
|
||||
foo: bool,
|
||||
#[clap(long, group = "verb")]
|
||||
bar: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
struct Cli {
|
||||
#[clap(flatten)]
|
||||
a: Opt,
|
||||
}
|
||||
|
||||
assert!(Cli::try_parse_from(&["test"]).is_err());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--bar"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--zebra"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_289() {
|
||||
use clap::{AppSettings, Clap, IntoApp};
|
||||
|
||||
#[derive(Clap)]
|
||||
#[clap(setting = AppSettings::InferSubcommands)]
|
||||
enum Args {
|
||||
SomeCommand(SubSubCommand),
|
||||
AnotherCommand,
|
||||
}
|
||||
|
||||
#[derive(Clap)]
|
||||
#[clap(setting = AppSettings::InferSubcommands)]
|
||||
enum SubSubCommand {
|
||||
TestCommand,
|
||||
}
|
||||
|
||||
assert!(Args::try_parse_from(&["test", "some-command", "test-command"]).is_ok());
|
||||
assert!(Args::try_parse_from(&["test", "some", "test-command"]).is_ok());
|
||||
assert!(Args::try_parse_from(&["test", "some-command", "test"]).is_ok());
|
||||
assert!(Args::try_parse_from(&["test", "some", "test"]).is_ok());
|
||||
}
|
13
clap_derive/tests/macro-errors.rs
Normal file
13
clap_derive/tests/macro-errors.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
|
||||
#[rustversion::attr(any(not(stable), before(1.39)), ignore)]
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
196
clap_derive/tests/nested-subcommands.rs
Normal file
196
clap_derive/tests/nested-subcommands.rs
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
force: bool,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
#[clap(subcommand)]
|
||||
cmd: Sub,
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Sub {
|
||||
Fetch {},
|
||||
Add {},
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt2 {
|
||||
#[clap(short, long)]
|
||||
force: bool,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
#[clap(subcommand)]
|
||||
cmd: Option<Sub>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_cmd() {
|
||||
let result = Opt::try_parse_from(&["test"]);
|
||||
assert!(result.is_err());
|
||||
|
||||
assert_eq!(
|
||||
Opt2 {
|
||||
force: false,
|
||||
verbose: 0,
|
||||
cmd: None
|
||||
},
|
||||
Opt2::parse_from(&["test"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch() {
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: false,
|
||||
verbose: 3,
|
||||
cmd: Sub::Fetch {}
|
||||
},
|
||||
Opt::parse_from(&["test", "-vvv", "fetch"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: true,
|
||||
verbose: 0,
|
||||
cmd: Sub::Fetch {}
|
||||
},
|
||||
Opt::parse_from(&["test", "--force", "fetch"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: false,
|
||||
verbose: 0,
|
||||
cmd: Sub::Add {}
|
||||
},
|
||||
Opt::parse_from(&["test", "add"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
force: false,
|
||||
verbose: 2,
|
||||
cmd: Sub::Add {}
|
||||
},
|
||||
Opt::parse_from(&["test", "-vv", "add"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_badinput() {
|
||||
let result = Opt::try_parse_from(&["test", "badcmd"]);
|
||||
assert!(result.is_err());
|
||||
let result = Opt::try_parse_from(&["test", "add", "--verbose"]);
|
||||
assert!(result.is_err());
|
||||
let result = Opt::try_parse_from(&["test", "--badopt", "add"]);
|
||||
assert!(result.is_err());
|
||||
let result = Opt::try_parse_from(&["test", "add", "--badopt"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt3 {
|
||||
#[clap(short, long)]
|
||||
all: bool,
|
||||
#[clap(subcommand)]
|
||||
cmd: Sub2,
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Sub2 {
|
||||
Foo {
|
||||
file: String,
|
||||
#[clap(subcommand)]
|
||||
cmd: Sub3,
|
||||
},
|
||||
Bar {},
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Sub3 {
|
||||
Baz {},
|
||||
Quux {},
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subsubcommand() {
|
||||
assert_eq!(
|
||||
Opt3 {
|
||||
all: true,
|
||||
cmd: Sub2::Foo {
|
||||
file: "lib.rs".to_string(),
|
||||
cmd: Sub3::Quux {}
|
||||
}
|
||||
},
|
||||
Opt3::parse_from(&["test", "--all", "foo", "lib.rs", "quux"])
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum SubSubCmdWithOption {
|
||||
Remote {
|
||||
#[clap(subcommand)]
|
||||
cmd: Option<Remote>,
|
||||
},
|
||||
Stash {
|
||||
#[clap(subcommand)]
|
||||
cmd: Stash,
|
||||
},
|
||||
}
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Remote {
|
||||
Add { name: String, url: String },
|
||||
Remove { name: String },
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Stash {
|
||||
Save,
|
||||
Pop,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_sub_cmd_with_option() {
|
||||
fn make(args: &[&str]) -> Option<SubSubCmdWithOption> {
|
||||
use clap::{FromArgMatches, IntoApp};
|
||||
|
||||
SubSubCmdWithOption::try_parse_from(args).ok()
|
||||
}
|
||||
assert_eq!(
|
||||
Some(SubSubCmdWithOption::Remote { cmd: None }),
|
||||
make(&["", "remote"])
|
||||
);
|
||||
assert_eq!(
|
||||
Some(SubSubCmdWithOption::Remote {
|
||||
cmd: Some(Remote::Add {
|
||||
name: "origin".into(),
|
||||
url: "http".into()
|
||||
})
|
||||
}),
|
||||
make(&["", "remote", "add", "origin", "http"])
|
||||
);
|
||||
assert_eq!(
|
||||
Some(SubSubCmdWithOption::Stash { cmd: Stash::Save }),
|
||||
make(&["", "stash", "save"])
|
||||
);
|
||||
assert_eq!(None, make(&["", "stash"]));
|
||||
}
|
149
clap_derive/tests/non_literal_attributes.rs
Normal file
149
clap_derive/tests/non_literal_attributes.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::{AppSettings, Clap};
|
||||
use std::num::ParseIntError;
|
||||
|
||||
pub const DISPLAY_ORDER: usize = 2;
|
||||
|
||||
// Check if the global settings compile
|
||||
#[derive(Clap, Debug, PartialEq, Eq)]
|
||||
#[clap(global_setting = AppSettings::ColoredHelp)]
|
||||
struct Opt {
|
||||
#[clap(
|
||||
long = "x",
|
||||
display_order = DISPLAY_ORDER,
|
||||
next_line_help = true,
|
||||
default_value = "0",
|
||||
require_equals = true
|
||||
)]
|
||||
x: i32,
|
||||
|
||||
#[clap(short = "l", long = "level", aliases = &["set-level", "lvl"])]
|
||||
level: String,
|
||||
|
||||
#[clap(long("values"))]
|
||||
values: Vec<i32>,
|
||||
|
||||
#[clap(name = "FILE", requires_if("FILE", "values"))]
|
||||
files: Vec<String>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice() {
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-l", "1"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "--level", "1"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "--set-level", "1"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: Vec::new(),
|
||||
values: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "--lvl", "1"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_args() {
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: vec!["file".to_string()],
|
||||
values: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-l", "1", "file"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 0,
|
||||
level: "1".to_string(),
|
||||
files: vec!["FILE".to_string()],
|
||||
values: vec![1],
|
||||
},
|
||||
Opt::parse_from(&["test", "-l", "1", "--values", "1", "--", "FILE"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_args_fail() {
|
||||
let result = Opt::try_parse_from(&["test", "-l", "1", "--", "FILE"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool() {
|
||||
assert_eq!(
|
||||
Opt {
|
||||
x: 1,
|
||||
level: "1".to_string(),
|
||||
files: vec![],
|
||||
values: vec![],
|
||||
},
|
||||
Opt::parse_from(&["test", "-l", "1", "--x=1"])
|
||||
);
|
||||
let result = Opt::try_parse_from(&["test", "-l", "1", "--x", "1"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
|
||||
u64::from_str_radix(input, 16)
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct HexOpt {
|
||||
#[clap(short = "n", parse(try_from_str = parse_hex))]
|
||||
number: u64,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_hex_function_path() {
|
||||
assert_eq!(
|
||||
HexOpt { number: 5 },
|
||||
HexOpt::parse_from(&["test", "-n", "5"])
|
||||
);
|
||||
assert_eq!(
|
||||
HexOpt { number: 0xabcdef },
|
||||
HexOpt::parse_from(&["test", "-n", "abcdef"])
|
||||
);
|
||||
|
||||
let err = HexOpt::try_parse_from(&["test", "-n", "gg"]).unwrap_err();
|
||||
assert!(err.message.contains("invalid digit found in string"), err);
|
||||
}
|
285
clap_derive/tests/options.rs
Normal file
285
clap_derive/tests/options.rs
Normal file
|
@ -0,0 +1,285 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn required_option() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test", "-a42"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test", "-a", "42"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test", "--arg", "42"]));
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_option() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
arg: Option<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: Some(42) }, Opt::parse_from(&["test", "-a42"]));
|
||||
assert_eq!(Opt { arg: None }, Opt::parse_from(&["test"]));
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_with_default() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, default_value = "42")]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 24 }, Opt::parse_from(&["test", "-a24"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test"]));
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_with_raw_default() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, default_value = "42")]
|
||||
arg: i32,
|
||||
}
|
||||
assert_eq!(Opt { arg: 24 }, Opt::parse_from(&["test", "-a24"]));
|
||||
assert_eq!(Opt { arg: 42 }, Opt::parse_from(&["test"]));
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "-a24"]));
|
||||
assert_eq!(Opt { arg: vec![] }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(
|
||||
Opt { arg: vec![24, 42] },
|
||||
Opt::parse_from(&["test", "-a24", "--arg", "42"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_value() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, default_value = "test")]
|
||||
arg: String,
|
||||
}
|
||||
assert_eq!(Opt { arg: "test".into() }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(
|
||||
Opt { arg: "foo".into() },
|
||||
Opt::parse_from(&["test", "-afoo"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_from_str() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct A;
|
||||
|
||||
impl<'a> From<&'a str> for A {
|
||||
fn from(_: &str) -> A {
|
||||
A
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clap, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str))]
|
||||
a: Option<A>,
|
||||
}
|
||||
|
||||
assert_eq!(Opt { a: None }, Opt::parse_from(&["test"]));
|
||||
assert_eq!(Opt { a: Some(A) }, Opt::parse_from(&["test", "foo"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_argument_for_optional_option() {
|
||||
use clap::IntoApp;
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
#[allow(clippy::option_option)]
|
||||
arg: Option<Option<i32>>,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(Some(42))
|
||||
},
|
||||
Opt::parse_from(&["test", "-a42"])
|
||||
);
|
||||
assert_eq!(Opt { arg: Some(None) }, Opt::parse_from(&["test", "-a"]));
|
||||
assert_eq!(Opt { arg: None }, Opt::parse_from(&["test"]));
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_option_options() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
arg: Option<Option<i32>>,
|
||||
|
||||
#[clap(long)]
|
||||
field: Option<Option<String>>,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(Some(42)),
|
||||
field: Some(Some("f".into()))
|
||||
},
|
||||
Opt::parse_from(&["test", "-a42", "--field", "f"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(Some(42)),
|
||||
field: Some(None)
|
||||
},
|
||||
Opt::parse_from(&["test", "-a42", "--field"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(None),
|
||||
field: Some(None)
|
||||
},
|
||||
Opt::parse_from(&["test", "-a", "--field"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(None),
|
||||
field: Some(Some("f".into()))
|
||||
},
|
||||
Opt::parse_from(&["test", "-a", "--field", "f"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: None,
|
||||
field: Some(None)
|
||||
},
|
||||
Opt::parse_from(&["test", "--field"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: None,
|
||||
field: None
|
||||
},
|
||||
Opt::parse_from(&["test"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_vec() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
arg: Option<Vec<i32>>,
|
||||
}
|
||||
assert_eq!(
|
||||
Opt { arg: Some(vec![1]) },
|
||||
Opt::parse_from(&["test", "-a", "1"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1, 2])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a1", "-a2"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1, 2])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a1", "-a2", "-a"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1, 2])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a1", "-a", "-a2"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1, 2])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a", "1", "2"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1, 2, 3])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a", "1", "2", "-a", "3"])
|
||||
);
|
||||
|
||||
assert_eq!(Opt { arg: Some(vec![]) }, Opt::parse_from(&["test", "-a"]));
|
||||
|
||||
assert_eq!(
|
||||
Opt { arg: Some(vec![]) },
|
||||
Opt::parse_from(&["test", "-a", "-a"])
|
||||
);
|
||||
|
||||
assert_eq!(Opt { arg: None }, Opt::parse_from(&["test"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_optional_vecs() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
arg: Option<Vec<i32>>,
|
||||
|
||||
#[clap(short)]
|
||||
b: Option<Vec<i32>>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1]),
|
||||
b: Some(vec![])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a", "1", "-b"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1]),
|
||||
b: Some(vec![])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a", "-b", "-a1"])
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some(vec![1, 2]),
|
||||
b: Some(vec![1, 2])
|
||||
},
|
||||
Opt::parse_from(&["test", "-a1", "-a2", "-b1", "-b2"])
|
||||
);
|
||||
|
||||
assert_eq!(Opt { arg: None, b: None }, Opt::parse_from(&["test"]));
|
||||
}
|
36
clap_derive/tests/privacy.rs
Normal file
36
clap_derive/tests/privacy.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
mod options {
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
pub struct Options {
|
||||
#[clap(subcommand)]
|
||||
pub subcommand: super::subcommands::SubCommand,
|
||||
}
|
||||
}
|
||||
|
||||
mod subcommands {
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Debug, Clap)]
|
||||
pub enum SubCommand {
|
||||
/// foo
|
||||
Foo {
|
||||
/// foo
|
||||
bars: Vec<String>,
|
||||
},
|
||||
}
|
||||
}
|
29
clap_derive/tests/raw_bool_literal.rs
Normal file
29
clap_derive/tests/raw_bool_literal.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn raw_bool_literal() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
#[clap(no_version, name = "raw_bool")]
|
||||
struct Opt {
|
||||
#[clap(raw(false))]
|
||||
a: String,
|
||||
#[clap(raw(true))]
|
||||
b: String,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
a: "one".into(),
|
||||
b: "--help".into()
|
||||
},
|
||||
Opt::parse_from(&["test", "one", "--", "--help"])
|
||||
);
|
||||
}
|
17
clap_derive/tests/raw_idents.rs
Normal file
17
clap_derive/tests/raw_idents.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn raw_idents() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(short, long)]
|
||||
r#type: Vec<String>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
r#type: vec!["long".into(), "short".into()]
|
||||
},
|
||||
Opt::parse_from(&["test", "--type", "long", "-t", "short"])
|
||||
);
|
||||
}
|
148
clap_derive/tests/skip.rs
Normal file
148
clap_derive/tests/skip.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[test]
|
||||
fn skip_1() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
x: u32,
|
||||
#[clap(skip)]
|
||||
s: u32,
|
||||
}
|
||||
|
||||
assert!(Opt::try_parse_from(&["test", "-x", "10", "20"]).is_err());
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "-x", "10"]),
|
||||
Opt {
|
||||
x: 10,
|
||||
s: 0, // default
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_2() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(short)]
|
||||
x: u32,
|
||||
#[clap(skip)]
|
||||
ss: String,
|
||||
#[clap(skip)]
|
||||
sn: u8,
|
||||
y: u32,
|
||||
#[clap(skip)]
|
||||
sz: u16,
|
||||
t: u32,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "-x", "10", "20", "30"]),
|
||||
Opt {
|
||||
x: 10,
|
||||
ss: String::from(""),
|
||||
sn: 0,
|
||||
y: 20,
|
||||
sz: 0,
|
||||
t: 30,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_enum() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(unused)]
|
||||
enum Kind {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
impl Default for Kind {
|
||||
fn default() -> Self {
|
||||
return Kind::B;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub struct Opt {
|
||||
#[clap(long, short)]
|
||||
number: u32,
|
||||
#[clap(skip)]
|
||||
k: Kind,
|
||||
#[clap(skip)]
|
||||
v: Vec<u32>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "-n", "10"]),
|
||||
Opt {
|
||||
number: 10,
|
||||
k: Kind::B,
|
||||
v: vec![],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_help_doc_comments() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub struct Opt {
|
||||
#[clap(skip, help = "internal_stuff")]
|
||||
a: u32,
|
||||
|
||||
#[clap(skip, long_help = "internal_stuff\ndo not touch")]
|
||||
b: u32,
|
||||
|
||||
/// Not meant to be used by clap.
|
||||
///
|
||||
/// I want a default here.
|
||||
#[clap(skip)]
|
||||
c: u32,
|
||||
|
||||
#[clap(short, parse(try_from_str))]
|
||||
n: u32,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "-n", "10"]),
|
||||
Opt {
|
||||
n: 10,
|
||||
a: 0,
|
||||
b: 0,
|
||||
c: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_val() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub struct Opt {
|
||||
#[clap(long, short)]
|
||||
number: u32,
|
||||
|
||||
#[clap(skip = "key")]
|
||||
k: String,
|
||||
|
||||
#[clap(skip = vec![1, 2, 3])]
|
||||
v: Vec<u32>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "-n", "10"]),
|
||||
Opt {
|
||||
number: 10,
|
||||
k: "key".into(),
|
||||
v: vec![1, 2, 3]
|
||||
}
|
||||
);
|
||||
}
|
73
clap_derive/tests/special_types.rs
Normal file
73
clap_derive/tests/special_types.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
//! Checks that types like `::std::option::Option` are not special
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[rustversion::since(1.37)]
|
||||
#[test]
|
||||
fn special_types_bool() {
|
||||
mod inner {
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct bool(pub String);
|
||||
|
||||
impl std::str::FromStr for self::bool {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(self::bool(s.into()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
arg: inner::bool,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: inner::bool("success".into())
|
||||
},
|
||||
Opt::parse_from(&["test", "success"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_types_option() {
|
||||
fn parser(s: &str) -> Option<String> {
|
||||
Some(s.to_string())
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str = parser))]
|
||||
arg: ::std::option::Option<String>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: Some("success".into())
|
||||
},
|
||||
Opt::parse_from(&["test", "success"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_types_vec() {
|
||||
fn parser(s: &str) -> Vec<String> {
|
||||
vec![s.to_string()]
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str = parser))]
|
||||
arg: ::std::vec::Vec<String>,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
Opt {
|
||||
arg: vec!["success".into()]
|
||||
},
|
||||
Opt::parse_from(&["test", "success"])
|
||||
);
|
||||
}
|
209
clap_derive/tests/subcommands.rs
Normal file
209
clap_derive/tests/subcommands.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
mod utils;
|
||||
|
||||
use clap::Clap;
|
||||
use utils::*;
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Opt {
|
||||
/// Fetch stuff from GitHub
|
||||
Fetch {
|
||||
#[clap(long)]
|
||||
all: bool,
|
||||
#[clap(short, long)]
|
||||
/// Overwrite local branches.
|
||||
force: bool,
|
||||
repo: String,
|
||||
},
|
||||
|
||||
Add {
|
||||
#[clap(short, long)]
|
||||
interactive: bool,
|
||||
#[clap(short, long)]
|
||||
verbose: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch() {
|
||||
assert_eq!(
|
||||
Opt::Fetch {
|
||||
all: true,
|
||||
force: false,
|
||||
repo: "origin".to_string()
|
||||
},
|
||||
Opt::parse_from(&["test", "fetch", "--all", "origin"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt::Fetch {
|
||||
all: false,
|
||||
force: true,
|
||||
repo: "origin".to_string()
|
||||
},
|
||||
Opt::parse_from(&["test", "fetch", "-f", "origin"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
assert_eq!(
|
||||
Opt::Add {
|
||||
interactive: false,
|
||||
verbose: false
|
||||
},
|
||||
Opt::parse_from(&["test", "add"])
|
||||
);
|
||||
assert_eq!(
|
||||
Opt::Add {
|
||||
interactive: true,
|
||||
verbose: true
|
||||
},
|
||||
Opt::parse_from(&["test", "add", "-i", "-v"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_parse() {
|
||||
let result = Opt::try_parse_from(&["test", "badcmd", "-i", "-v"]);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = Opt::try_parse_from(&["test", "add", "--badoption"]);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = Opt::try_parse_from(&["test"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Opt2 {
|
||||
DoSomething { arg: String },
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// This test is specifically to make sure that hyphenated subcommands get
|
||||
/// processed correctly.
|
||||
fn test_hyphenated_subcommands() {
|
||||
assert_eq!(
|
||||
Opt2::DoSomething {
|
||||
arg: "blah".to_string()
|
||||
},
|
||||
Opt2::parse_from(&["test", "do-something", "blah"])
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Opt3 {
|
||||
Add,
|
||||
Init,
|
||||
Fetch,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_commands() {
|
||||
assert_eq!(Opt3::Add, Opt3::parse_from(&["test", "add"]));
|
||||
assert_eq!(Opt3::Init, Opt3::parse_from(&["test", "init"]));
|
||||
assert_eq!(Opt3::Fetch, Opt3::parse_from(&["test", "fetch"]));
|
||||
}
|
||||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
#[clap(about = "Not shown")]
|
||||
struct Add {
|
||||
file: String,
|
||||
}
|
||||
/// Not shown
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Fetch {
|
||||
remote: String,
|
||||
}
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
enum Opt4 {
|
||||
// Not shown
|
||||
/// Add a file
|
||||
Add(Add),
|
||||
Init,
|
||||
/// download history from remote
|
||||
Fetch(Fetch),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_commands() {
|
||||
use clap::IntoApp;
|
||||
|
||||
assert_eq!(
|
||||
Opt4::Add(Add {
|
||||
file: "f".to_string()
|
||||
}),
|
||||
Opt4::parse_from(&["test", "add", "f"])
|
||||
);
|
||||
assert_eq!(Opt4::Init, Opt4::parse_from(&["test", "init"]));
|
||||
assert_eq!(
|
||||
Opt4::Fetch(Fetch {
|
||||
remote: "origin".to_string()
|
||||
}),
|
||||
Opt4::parse_from(&["test", "fetch", "origin"])
|
||||
);
|
||||
|
||||
let output = get_long_help::<Opt4>();
|
||||
|
||||
assert!(output.contains("download history from remote"));
|
||||
assert!(output.contains("Add a file"));
|
||||
assert!(!output.contains("Not shown"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_in_enum_subsubcommand() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub enum Opt {
|
||||
Daemon(DaemonCommand),
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
pub enum DaemonCommand {
|
||||
Start,
|
||||
Stop,
|
||||
}
|
||||
|
||||
let result = Opt::try_parse_from(&["test"]);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = Opt::try_parse_from(&["test", "daemon"]);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = Opt::parse_from(&["test", "daemon", "start"]);
|
||||
assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_enum() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(flatten)]
|
||||
sub_cmd: SubCmd,
|
||||
}
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
enum SubCmd {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert_eq!(
|
||||
Opt::parse_from(&["test", "foo"]),
|
||||
Opt {
|
||||
sub_cmd: SubCmd::Foo
|
||||
}
|
||||
);
|
||||
}
|
21
clap_derive/tests/ui/bool_default_value.rs
Normal file
21
clap_derive/tests/ui/bool_default_value.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(short, default_value = true)]
|
||||
b: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/bool_default_value.stderr
Normal file
5
clap_derive/tests/ui/bool_default_value.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: default_value is meaningless for bool
|
||||
--> $DIR/bool_default_value.rs:14:19
|
||||
|
|
||||
14 | #[clap(short, default_value = true)]
|
||||
| ^^^^^^^^^^^^^
|
21
clap_derive/tests/ui/bool_required.rs
Normal file
21
clap_derive/tests/ui/bool_required.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(short, required = true)]
|
||||
b: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/bool_required.stderr
Normal file
5
clap_derive/tests/ui/bool_required.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: required is meaningless for bool
|
||||
--> $DIR/bool_required.rs:14:19
|
||||
|
|
||||
14 | #[clap(short, required = true)]
|
||||
| ^^^^^^^^
|
22
clap_derive/tests/ui/clap_derive_empty_attr.rs
Normal file
22
clap_derive/tests/ui/clap_derive_empty_attr.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
||||
|
5
clap_derive/tests/ui/clap_derive_empty_attr.stderr
Normal file
5
clap_derive/tests/ui/clap_derive_empty_attr.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: expected parentheses after `clap`
|
||||
--> $DIR/clap_derive_empty_attr.rs:14:7
|
||||
|
|
||||
14 | #[clap]
|
||||
| ^^^^
|
22
clap_derive/tests/ui/clap_derive_name_value_attr.rs
Normal file
22
clap_derive/tests/ui/clap_derive_name_value_attr.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap = "short"]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
||||
|
5
clap_derive/tests/ui/clap_derive_name_value_attr.stderr
Normal file
5
clap_derive/tests/ui/clap_derive_name_value_attr.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: expected parentheses
|
||||
--> $DIR/clap_derive_name_value_attr.rs:14:12
|
||||
|
|
||||
14 | #[clap = "short"]
|
||||
| ^
|
30
clap_derive/tests/ui/flatten_and_doc.rs
Normal file
30
clap_derive/tests/ui/flatten_and_doc.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct DaemonOpts {
|
||||
#[clap(short)]
|
||||
user: String,
|
||||
#[clap(short)]
|
||||
group: String,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
/// Opts.
|
||||
#[clap(flatten)]
|
||||
opts: DaemonOpts,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/flatten_and_doc.stderr
Normal file
5
clap_derive/tests/ui/flatten_and_doc.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: methods and doc comments are not allowed for flattened entry
|
||||
--> $DIR/flatten_and_doc.rs:23:12
|
||||
|
|
||||
23 | #[clap(flatten)]
|
||||
| ^^^^^^^
|
29
clap_derive/tests/ui/flatten_and_methods.rs
Normal file
29
clap_derive/tests/ui/flatten_and_methods.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct DaemonOpts {
|
||||
#[clap(short)]
|
||||
user: String,
|
||||
#[clap(short)]
|
||||
group: String,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(short, flatten)]
|
||||
opts: DaemonOpts,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/flatten_and_methods.stderr
Normal file
5
clap_derive/tests/ui/flatten_and_methods.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: methods and doc comments are not allowed for flattened entry
|
||||
--> $DIR/flatten_and_methods.rs:22:19
|
||||
|
|
||||
22 | #[clap(short, flatten)]
|
||||
| ^^^^^^^
|
29
clap_derive/tests/ui/flatten_and_parse.rs
Normal file
29
clap_derive/tests/ui/flatten_and_parse.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct DaemonOpts {
|
||||
#[clap(short)]
|
||||
user: String,
|
||||
#[clap(short)]
|
||||
group: String,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(flatten, parse(from_occurrences))]
|
||||
opts: DaemonOpts,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/flatten_and_parse.stderr
Normal file
5
clap_derive/tests/ui/flatten_and_parse.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: parse attribute is not allowed for flattened entry
|
||||
--> $DIR/flatten_and_parse.rs:22:21
|
||||
|
|
||||
22 | #[clap(flatten, parse(from_occurrences))]
|
||||
| ^^^^^
|
21
clap_derive/tests/ui/non_existent_attr.rs
Normal file
21
clap_derive/tests/ui/non_existent_attr.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(short, non_existing_attribute = 1)]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/non_existent_attr.stderr
Normal file
5
clap_derive/tests/ui/non_existent_attr.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error[E0599]: no method named `non_existing_attribute` found for type `clap::build::arg::Arg<'_>` in the current scope
|
||||
--> $DIR/non_existent_attr.rs:14:19
|
||||
|
|
||||
14 | #[clap(short, non_existing_attribute = 1)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ method not found in `clap::build::arg::Arg<'_>`
|
20
clap_derive/tests/ui/opt_opt_nonpositional.rs
Normal file
20
clap_derive/tests/ui/opt_opt_nonpositional.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
n: Option<Option<u32>>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/opt_opt_nonpositional.stderr
Normal file
5
clap_derive/tests/ui/opt_opt_nonpositional.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: Option<Option<T>> type is meaningless for positional argument
|
||||
--> $DIR/opt_opt_nonpositional.rs:14:8
|
||||
|
|
||||
14 | n: Option<Option<u32>>,
|
||||
| ^^^^^^
|
20
clap_derive/tests/ui/opt_vec_nonpositional.rs
Normal file
20
clap_derive/tests/ui/opt_vec_nonpositional.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
n: Option<Vec<u32>>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/opt_vec_nonpositional.stderr
Normal file
5
clap_derive/tests/ui/opt_vec_nonpositional.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: Option<Vec<T>> type is meaningless for positional argument
|
||||
--> $DIR/opt_vec_nonpositional.rs:14:8
|
||||
|
|
||||
14 | n: Option<Vec<u32>>,
|
||||
| ^^^^^^
|
21
clap_derive/tests/ui/option_default_value.rs
Normal file
21
clap_derive/tests/ui/option_default_value.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(short, default_value = 1)]
|
||||
n: Option<u32>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/option_default_value.stderr
Normal file
5
clap_derive/tests/ui/option_default_value.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: default_value is meaningless for Option
|
||||
--> $DIR/option_default_value.rs:14:19
|
||||
|
|
||||
14 | #[clap(short, default_value = 1)]
|
||||
| ^^^^^^^^^^^^^
|
21
clap_derive/tests/ui/option_required.rs
Normal file
21
clap_derive/tests/ui/option_required.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(short, required = true)]
|
||||
n: Option<u32>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/option_required.stderr
Normal file
5
clap_derive/tests/ui/option_required.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: required is meaningless for Option
|
||||
--> $DIR/option_required.rs:14:19
|
||||
|
|
||||
14 | #[clap(short, required = true)]
|
||||
| ^^^^^^^^
|
21
clap_derive/tests/ui/parse_empty_try_from_os.rs
Normal file
21
clap_derive/tests/ui/parse_empty_try_from_os.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(parse(try_from_os_str))]
|
||||
s: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/parse_empty_try_from_os.stderr
Normal file
5
clap_derive/tests/ui/parse_empty_try_from_os.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: you must set parser for `try_from_os_str` explicitly
|
||||
--> $DIR/parse_empty_try_from_os.rs:14:18
|
||||
|
|
||||
14 | #[clap(parse(try_from_os_str))]
|
||||
| ^^^^^^^^^^^^^^^
|
21
clap_derive/tests/ui/parse_function_is_not_path.rs
Normal file
21
clap_derive/tests/ui/parse_function_is_not_path.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str = "2"))]
|
||||
s: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/parse_function_is_not_path.stderr
Normal file
5
clap_derive/tests/ui/parse_function_is_not_path.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: `parse` argument must be a function path
|
||||
--> $DIR/parse_function_is_not_path.rs:14:29
|
||||
|
|
||||
14 | #[clap(parse(from_str = "2"))]
|
||||
| ^^^
|
21
clap_derive/tests/ui/parse_literal_spec.rs
Normal file
21
clap_derive/tests/ui/parse_literal_spec.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(parse("from_str"))]
|
||||
s: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/parse_literal_spec.stderr
Normal file
5
clap_derive/tests/ui/parse_literal_spec.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: parser specification must start with identifier
|
||||
--> $DIR/parse_literal_spec.rs:14:18
|
||||
|
|
||||
14 | #[clap(parse("from_str"))]
|
||||
| ^^^^^^^^^^
|
21
clap_derive/tests/ui/parse_not_zero_args.rs
Normal file
21
clap_derive/tests/ui/parse_not_zero_args.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "basic")]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str, from_str))]
|
||||
s: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
println!("{:?}", opt);
|
||||
}
|
5
clap_derive/tests/ui/parse_not_zero_args.stderr
Normal file
5
clap_derive/tests/ui/parse_not_zero_args.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: parse must have exactly one argument
|
||||
--> $DIR/parse_not_zero_args.rs:14:12
|
||||
|
|
||||
14 | #[clap(parse(from_str, from_str))]
|
||||
| ^^^^^
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue