aboutsummaryrefslogtreecommitdiff
path: root/structopt
diff options
context:
space:
mode:
Diffstat (limited to 'structopt')
-rw-r--r--structopt/.gitignore6
-rw-r--r--structopt/.travis.yml24
-rw-r--r--structopt/CHANGELOG.md444
-rw-r--r--structopt/Cargo.toml35
-rw-r--r--structopt/LICENSE-APACHE201
-rw-r--r--structopt/LICENSE-MIT21
-rw-r--r--structopt/README.md148
-rw-r--r--structopt/examples/README.md82
-rw-r--r--structopt/examples/after_help.rs19
-rw-r--r--structopt/examples/at_least_two.rs15
-rw-r--r--structopt/examples/basic.rs48
-rw-r--r--structopt/examples/deny_missing_docs.rs51
-rw-r--r--structopt/examples/doc_comments.rs74
-rw-r--r--structopt/examples/enum_in_args.rs25
-rw-r--r--structopt/examples/enum_tuple.rs26
-rw-r--r--structopt/examples/env.rs26
-rw-r--r--structopt/examples/example.rs54
-rw-r--r--structopt/examples/flatten.rs29
-rw-r--r--structopt/examples/gen_completions.rs26
-rw-r--r--structopt/examples/git.rs35
-rw-r--r--structopt/examples/group.rs31
-rw-r--r--structopt/examples/keyvalue.rs36
-rw-r--r--structopt/examples/negative_flag.rs15
-rw-r--r--structopt/examples/no_version.rs17
-rw-r--r--structopt/examples/rename_all.rs74
-rw-r--r--structopt/examples/skip.rs47
-rw-r--r--structopt/examples/subcommand_aliases.rs21
-rw-r--r--structopt/examples/true_or_false.rs41
-rw-r--r--structopt/link-check-headers.json14
-rw-r--r--structopt/src/lib.rs1015
-rw-r--r--structopt/structopt-derive/Cargo.toml27
-rw-r--r--structopt/structopt-derive/LICENSE-APACHE201
-rw-r--r--structopt/structopt-derive/LICENSE-MIT21
-rw-r--r--structopt/structopt-derive/src/attrs.rs620
-rw-r--r--structopt/structopt-derive/src/doc_comments.rs103
-rw-r--r--structopt/structopt-derive/src/lib.rs667
-rw-r--r--structopt/structopt-derive/src/parse.rs304
-rw-r--r--structopt/structopt-derive/src/spanned.rs101
-rw-r--r--structopt/structopt-derive/src/ty.rs108
-rw-r--r--structopt/tests/argument_naming.rs311
-rw-r--r--structopt/tests/arguments.rs86
-rw-r--r--structopt/tests/author_version_about.rs46
-rw-r--r--structopt/tests/custom-string-parsers.rs306
-rw-r--r--structopt/tests/deny-warnings.rs47
-rw-r--r--structopt/tests/doc-comments-help.rs162
-rw-r--r--structopt/tests/explicit_name_no_renaming.rs32
-rw-r--r--structopt/tests/flags.rs162
-rw-r--r--structopt/tests/flatten.rs95
-rw-r--r--structopt/tests/issues.rs67
-rw-r--r--structopt/tests/macro-errors.rs13
-rw-r--r--structopt/tests/nested-subcommands.rs193
-rw-r--r--structopt/tests/non_literal_attributes.rs147
-rw-r--r--structopt/tests/options.rs336
-rw-r--r--structopt/tests/privacy.rs32
-rw-r--r--structopt/tests/raw_bool_literal.rs29
-rw-r--r--structopt/tests/raw_idents.rs17
-rw-r--r--structopt/tests/rename_all_env.rs46
-rw-r--r--structopt/tests/skip.rs148
-rw-r--r--structopt/tests/special_types.rs73
-rw-r--r--structopt/tests/subcommands.rs213
-rw-r--r--structopt/tests/ui/bool_default_value.rs21
-rw-r--r--structopt/tests/ui/bool_default_value.stderr5
-rw-r--r--structopt/tests/ui/bool_required.rs21
-rw-r--r--structopt/tests/ui/bool_required.stderr5
-rw-r--r--structopt/tests/ui/flatten_and_doc.rs30
-rw-r--r--structopt/tests/ui/flatten_and_doc.stderr5
-rw-r--r--structopt/tests/ui/flatten_and_methods.rs29
-rw-r--r--structopt/tests/ui/flatten_and_methods.stderr5
-rw-r--r--structopt/tests/ui/flatten_and_parse.rs29
-rw-r--r--structopt/tests/ui/flatten_and_parse.stderr5
-rw-r--r--structopt/tests/ui/non_existent_attr.rs21
-rw-r--r--structopt/tests/ui/non_existent_attr.stderr5
-rw-r--r--structopt/tests/ui/opt_opt_nonpositional.rs20
-rw-r--r--structopt/tests/ui/opt_opt_nonpositional.stderr5
-rw-r--r--structopt/tests/ui/opt_vec_nonpositional.rs20
-rw-r--r--structopt/tests/ui/opt_vec_nonpositional.stderr5
-rw-r--r--structopt/tests/ui/option_default_value.rs21
-rw-r--r--structopt/tests/ui/option_default_value.stderr5
-rw-r--r--structopt/tests/ui/option_required.rs21
-rw-r--r--structopt/tests/ui/option_required.stderr5
-rw-r--r--structopt/tests/ui/parse_empty_try_from_os.rs21
-rw-r--r--structopt/tests/ui/parse_empty_try_from_os.stderr5
-rw-r--r--structopt/tests/ui/parse_function_is_not_path.rs21
-rw-r--r--structopt/tests/ui/parse_function_is_not_path.stderr5
-rw-r--r--structopt/tests/ui/parse_literal_spec.rs21
-rw-r--r--structopt/tests/ui/parse_literal_spec.stderr5
-rw-r--r--structopt/tests/ui/parse_not_zero_args.rs21
-rw-r--r--structopt/tests/ui/parse_not_zero_args.stderr5
-rw-r--r--structopt/tests/ui/positional_bool.rs10
-rw-r--r--structopt/tests/ui/positional_bool.stderr10
-rw-r--r--structopt/tests/ui/raw.rs25
-rw-r--r--structopt/tests/ui/raw.stderr19
-rw-r--r--structopt/tests/ui/rename_all_wrong_casing.rs21
-rw-r--r--structopt/tests/ui/rename_all_wrong_casing.stderr5
-rw-r--r--structopt/tests/ui/skip_flatten.rs42
-rw-r--r--structopt/tests/ui/skip_flatten.stderr5
-rw-r--r--structopt/tests/ui/skip_subcommand.rs42
-rw-r--r--structopt/tests/ui/skip_subcommand.stderr5
-rw-r--r--structopt/tests/ui/skip_with_other_options.rs15
-rw-r--r--structopt/tests/ui/skip_with_other_options.stderr5
-rw-r--r--structopt/tests/ui/skip_without_default.rs29
-rw-r--r--structopt/tests/ui/skip_without_default.stderr9
-rw-r--r--structopt/tests/ui/struct_flatten.rs21
-rw-r--r--structopt/tests/ui/struct_flatten.stderr5
-rw-r--r--structopt/tests/ui/struct_parse.rs21
-rw-r--r--structopt/tests/ui/struct_parse.stderr5
-rw-r--r--structopt/tests/ui/struct_subcommand.rs21
-rw-r--r--structopt/tests/ui/struct_subcommand.stderr5
-rw-r--r--structopt/tests/ui/structopt_empty_attr.rs22
-rw-r--r--structopt/tests/ui/structopt_empty_attr.stderr5
-rw-r--r--structopt/tests/ui/structopt_name_value_attr.rs22
-rw-r--r--structopt/tests/ui/structopt_name_value_attr.stderr5
-rw-r--r--structopt/tests/ui/subcommand_and_flatten.rs36
-rw-r--r--structopt/tests/ui/subcommand_and_flatten.stderr5
-rw-r--r--structopt/tests/ui/subcommand_and_methods.rs36
-rw-r--r--structopt/tests/ui/subcommand_and_methods.stderr5
-rw-r--r--structopt/tests/ui/subcommand_and_parse.rs36
-rw-r--r--structopt/tests/ui/subcommand_and_parse.stderr5
-rw-r--r--structopt/tests/ui/subcommand_opt_opt.rs36
-rw-r--r--structopt/tests/ui/subcommand_opt_opt.stderr5
-rw-r--r--structopt/tests/ui/subcommand_opt_vec.rs36
-rw-r--r--structopt/tests/ui/subcommand_opt_vec.stderr5
-rw-r--r--structopt/tests/ui/tuple_struct.rs18
-rw-r--r--structopt/tests/ui/tuple_struct.stderr5
-rw-r--r--structopt/tests/utils.rs45
125 files changed, 8447 insertions, 0 deletions
diff --git a/structopt/.gitignore b/structopt/.gitignore
new file mode 100644
index 0000000..ea63af4
--- /dev/null
+++ b/structopt/.gitignore
@@ -0,0 +1,6 @@
+target
+Cargo.lock
+*~
+
+.idea/
+.vscode/
diff --git a/structopt/.travis.yml b/structopt/.travis.yml
new file mode 100644
index 0000000..dff3050
--- /dev/null
+++ b/structopt/.travis.yml
@@ -0,0 +1,24 @@
+language: rust
+matrix:
+ include:
+ - rust: stable
+ name: check if `cargo fmt --all` is applied
+ before_script: rustup component add rustfmt-preview
+ script: cargo fmt --all -- --check
+
+ - language: node_js
+ node_js: node
+ name: check links
+ install: npm install -g markdown-link-check
+ script:
+ - markdown-link-check -c link-check-headers.json README.md
+ - markdown-link-check -c link-check-headers.json CHANGELOG.md
+ - markdown-link-check -c link-check-headers.json examples/README.md
+
+ - rust: 1.36.0
+ - rust: stable
+ - rust: beta
+ - rust: nightly
+
+script:
+ - cargo test $FEATURES
diff --git a/structopt/CHANGELOG.md b/structopt/CHANGELOG.md
new file mode 100644
index 0000000..ed2a17f
--- /dev/null
+++ b/structopt/CHANGELOG.md
@@ -0,0 +1,444 @@
+# v0.3.7 (2019-12-28)
+
+Nothing's new. Just re-release of `v0.3.6` due to
+[the mess with versioning](https://github.com/TeXitoi/structopt/issues/315#issuecomment-568502792).
+
+You may notice that `structopt-derive` was bumped to `v0.4.0`, that's OK, it's not a breaking change.
+`structopt` will pull the right version in on its on.
+
+# v0.3.6 (2019-12-22) - YANKED
+
+This is unusually big patch release. It contains a number of bugfixes and
+new features, some of them may theoretically be considered breaking. We did our best
+to avoid any problems on user's side but, if it wasn't good enough, please
+[file an issue ASAP](https://github.com/TeXitoi/structopt/issues).
+
+## Bugfixes
+
+* `structopt` used to treat `::path::to::type::Vec<T>` as `Vec<T>`
+ special type. [This was considered erroneous](https://github.com/TeXitoi/structopt/pull/287).
+ (same for `Option<T>` and `bool`). Now only exact `Vec<T>` match is a special type.
+
+* `#[structopt(version = expr)]` where `expr` is not a string literal used to get
+ overridden by auto generated `.version()` call,
+ [incorrectly](https://github.com/TeXitoi/structopt/issues/283). Now it doesn't.
+
+* Fixed bug with top-level `App::*` calls on multiple `struct`s, see
+ [#289](https://github.com/TeXitoi/structopt/issues/265).
+
+* Positional `bool` args with no explicit `#[structopt(parse(...))]` annotation are
+ now prohibited. This couldn't work well anyway, see
+ [this example](https://github.com/TeXitoi/structopt/blob/master/examples/true_or_false.rs)
+ for details.
+
+* Now we've instituted strict priority between doc comments, about, help, and the like.
+ See [the documentation](https://docs.rs/structopt/0.3/structopt/#help-messages).
+
+ **HUGE THANKS to [`@ssokolow`](https://github.com/ssokolow)** for tidying up our documentation,
+ teaching me English and explaining why our doc used to suck. I promise I'll make the rest
+ of the doc up to your standards... sometime later!
+
+## New features
+
+* Implement `StructOpt` for `Box<impl StructOpt>` so from now on you can use `Box<T>`
+ with `flatten` and `subcommand` ([#304](https://github.com/TeXitoi/structopt/issues/304)).
+
+ ```rust
+ enum Command {
+ #[structopt(name = "version")]
+ PrintVersion,
+
+ #[structopt(name = "second")]
+ DoSomething {
+ #[structopt(flatten)]
+ config: Box<DoSomethingConfig>,
+ },
+
+ #[structopt(name = "first")]
+ DoSomethingElse {
+ #[structopt(flatten)]
+ config: Box<DoSomethingElseConfig>,
+ }
+ }
+ ```
+
+* Introduced `#[structopt(verbatim_doc_comment)]` attribute that keeps line breaks in
+ doc comments, see
+ [the documentation](https://docs.rs/structopt/0.3/structopt/#doc-comment-preprocessing-and-structoptverbatim_doc_comment).
+
+* Introduced `#[structopt(rename_all_env)]` and `#[structopt(env)]` magical methods
+ so you can derive env var's name from field's name. See
+ [the documentation](https://docs.rs/structopt/0.3/structopt/#auto-deriving-environment-variables).
+
+## Improvements
+
+* Now we have nice README for our examples,
+ [check it out](https://github.com/TeXitoi/structopt/tree/master/examples)!
+
+* Some error messages were improved and clarified, thanks for all people involved!
+
+
+# v0.3.5 (2019-11-22)
+
+* `try_from_str` functions are now called with a `&str` instead of a `&String` ([#282](https://github.com/TeXitoi/structopt/pull/282))
+
+# v0.3.4 (2019-11-08)
+
+* `rename_all` does not apply to fields that were annotated with explicit
+ `short/long/name = "..."` anymore ([#265](https://github.com/TeXitoi/structopt/issues/265))
+* Now raw idents are handled correctly ([#269](https://github.com/TeXitoi/structopt/issues/269))
+* Some documentation improvements and clarification.
+
+# v0.3.3 (2019-10-10)
+
+* Add `from_flag` custom parser to create flags from non-bool types.
+ Fixes [#185](https://github.com/TeXitoi/structopt/issues/185)
+
+# v0.3.2 (2019-09-18)
+
+* `structopt` does not replace `:` with `, ` inside "author" strings while inside `<...>`.
+ Fixes [#156](https://github.com/TeXitoi/structopt/issues/156)
+* Introduced [`#[structopt(skip = expr)]` syntax](https://docs.rs/structopt/0.3.2/structopt/#skipping-fields).
+
+# v0.3.1 (2019-09-06)
+
+* Fix error messages ([#241](https://github.com/TeXitoi/structopt/issues/241))
+* Fix "`skip` plus long doc comment" bug ([#245](https://github.com/TeXitoi/structopt/issues/245))
+* Now `structopt` emits dummy `StructOpt` implementation along with an error. It suppresses
+ meaningless errors like `from_args method is not found for Opt`
+* `.version()` not get generated if `CARGO_PKG_VERSION` is not set anymore.
+
+# v0.3.0 (2019-08-30)
+
+## Breaking changes
+
+### Bump minimum rustc version to 1.36 by [@TeXitoi](https://github.com/TeXitoi)
+Now `rustc` 1.36 is the minimum compiler version supported by `structopt`,
+it likely won't work with older compilers.
+
+### Remove "nightly" feature
+Once upon a time this feature had been used to enable some of improvements
+in `proc-macro2` crate that were available only on nightly. Nowadays this feature doesn't
+mean anything so it's now removed.
+
+### Support optional vectors of arguments for distinguishing between `-o 1 2`, `-o` and no option provided at all by [@sphynx](https://github.com/sphynx) ([#180](https://github.com/TeXitoi/structopt/issues/188)).
+
+```rust
+#[derive(StructOpt)]
+struct Opt {
+ #[structopt(long)]
+ fruit: Option<Vec<String>>,
+}
+
+fn main() {
+ assert_eq!(Opt::from_args(&["test"]), None);
+ assert_eq!(Opt::from_args(&["test", "--fruit"]), Some(vec![]));
+ assert_eq!(Opt::from_args(&["test", "--fruit=apple orange"]), Some(vec!["apple", "orange"]));
+}
+```
+
+If you need to fall back to the old behavior you can use a type alias:
+```rust
+type Something = Vec<String>;
+
+#[derive(StructOpt)]
+struct Opt {
+ #[structopt(long)]
+ fruit: Option<Something>,
+}
+```
+
+### Change default case from 'Verbatim' into 'Kebab' by [@0ndorio](https://github.com/0ndorio) ([#202](https://github.com/TeXitoi/structopt/issues/202)).
+`structopt` 0.3 uses field renaming to deduce a name for long options and subcommands.
+
+```rust
+#[derive(StructOpt)]
+struct Opt {
+ #[structopt(long)]
+ http_addr: String, // will be renamed to `--http-addr`
+
+ #[structopt(subcommand)]
+ addr_type: AddrType // this adds `addr-type` subcommand
+}
+```
+
+`structopt` 0.2 used to leave things "as is", not renaming anything. If you want to keep old
+behavior add `#[structopt(rename_all = "verbatim")]` on top of a `struct`/`enum`.
+
+### Change `version`, `author` and `about` attributes behavior.
+Proposed by [@TeXitoi](https://github.com/TeXitoi) [(#217)](https://github.com/TeXitoi/structopt/issues/217), implemented by [@CreepySkeleton](https://github.com/CreepySkeleton) [(#229)](https://github.com/TeXitoi/structopt/pull/229).
+
+`structopt` have been deducing `version`, `author`, and `about` properties from `Cargo.toml`
+for a long time (more accurately, from `CARGO_PKG_...` environment variables).
+But many users found this behavior somewhat confusing, and a hack was added to cancel out
+this behavior: `#[structopt(author = "")]`.
+
+In `structopt` 0.3 this has changed.
+* `author` and `about` are no longer deduced by default. You should use `#[structopt(author, about)]`
+ to explicitly request `structopt` to deduce them.
+* Contrary, `version` **is still deduced by default**. You can use `#[structopt(no_version)]` to
+ cancel it out.
+* `#[structopt(author = "", about = "", version = "")]` is no longer a valid syntax
+ and will trigger an error.
+* `#[structopt(version = "version", author = "author", about = "about")]` syntax
+ stays unaffected by this changes.
+
+### Raw attributes are removed ([#198](https://github.com/TeXitoi/structopt/pull/198)) by [@sphynx](https://github.com/sphynx)
+In `structopt` 0.2 you were able to use any method from `clap::App` and `clap::Arg` via
+raw attribute: `#[structopt(raw(method_name = "arg"))]`. This syntax was kind of awkward.
+
+```rust
+#[derive(StructOpt, Debug)]
+#[structopt(raw(
+ global_settings = "&[AppSettings::ColoredHelp, AppSettings::VersionlessSubcommands]"
+))]
+struct Opt {
+ #[structopt(short = "l", long = "level", raw(aliases = r#"&["set-level", "lvl"]"#))]
+ level: Vec<String>,
+}
+```
+
+Raw attributes were removed in 0.3. Now you can use any method from `App` and `Arg` *directly*:
+```rust
+#[derive(StructOpt)]
+#[structopt(global_settings(&[AppSettings::ColoredHelp, AppSettings::VersionlessSubcommands]))]
+struct Opt {
+ #[structopt(short = "l", long = "level", aliases(&["set-level", "lvl"]))]
+ level: Vec<String>,
+}
+```
+
+## Improvements
+
+### Support skipping struct fields
+Proposed by [@Morganamilo](https://github.com/Morganamilo) in ([#174](https://github.com/TeXitoi/structopt/issues/174))
+implemented by [@sphynx](https://github.com/sphynx) in ([#213](https://github.com/TeXitoi/structopt/issues/213)).
+
+Sometimes you want to include some fields in your `StructOpt` `struct` that are not options
+and `clap` should know nothing about them. In `structopt` 0.3 it's possible via the
+`#[structopt(skip)]` attribute. The field in question will be assigned with `Default::default()`
+value.
+
+```rust
+#[derive(StructOpt)]
+struct Opt {
+ #[structopt(short, long)]
+ speed: f32,
+
+ car: String,
+
+ // this field should not generate any arguments
+ #[structopt(skip)]
+ meta: Vec<u64>
+}
+```
+
+### Add optional feature to support `paw` by [@gameldar](https://github.com/gameldar) ([#187](https://github.com/TeXitoi/structopt/issues/187))
+
+### Significantly improve error reporting by [@CreepySkeleton](https://github.com/CreepySkeleton) ([#225](https://github.com/TeXitoi/structopt/pull/225/))
+Now (almost) every error message points to the location it originates from:
+
+```text
+error: default_value is meaningless for bool
+ --> $DIR/bool_default_value.rs:14:24
+ |
+14 | #[structopt(short, default_value = true)]
+ | ^^^^^^^^^^^^^
+```
+
+# v0.2.16 (2019-05-29)
+
+### Support optional options with optional argument, allowing `cmd [--opt[=value]]` by [@sphynx](https://github.com/sphynx) ([#188](https://github.com/TeXitoi/structopt/issues/188))
+Sometimes you want to represent an optional option that optionally takes an argument,
+i.e `[--opt[=value]]`. This is represented by `Option<Option<T>>`
+
+```rust
+#[derive(StructOpt)]
+struct Opt {
+ #[structopt(long)]
+ fruit: Option<Option<String>>,
+}
+
+fn main() {
+ assert_eq!(Opt::from_args(&["test"]), None);
+ assert_eq!(Opt::from_args(&["test", "--fruit"]), Some(None));
+ assert_eq!(Opt::from_args(&["test", "--fruit=apple"]), Some("apple"));
+}
+```
+
+# v0.2.15 (2019-03-08)
+
+* Fix [#168](https://github.com/TeXitoi/structopt/issues/168) by [@TeXitoi](https://github.com/TeXitoi)
+
+# v0.2.14 (2018-12-10)
+
+* Introduce smarter parsing of doc comments by [@0ndorio](https://github.com/0ndorio)
+
+# v0.2.13 (2018-11-01)
+
+* Automatic naming of fields and subcommands by [@0ndorio](https://github.com/0ndorio)
+
+# v0.2.12 (2018-10-11)
+
+* Fix minimal clap version by [@TeXitoi](https://github.com/TeXitoi)
+
+# v0.2.11 (2018-10-05)
+
+* Upgrade syn to 0.15 by [@konstin](https://github.com/konstin)
+
+# 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
+#[structopt(short = "v", long = "verbose")]
+verbose: u64,
+```
+must be changed by
+```rust
+#[structopt(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 `#[structopt(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)
diff --git a/structopt/Cargo.toml b/structopt/Cargo.toml
new file mode 100644
index 0000000..b43728b
--- /dev/null
+++ b/structopt/Cargo.toml
@@ -0,0 +1,35 @@
+[package]
+name = "structopt"
+version = "0.3.7"
+edition = "2018"
+authors = ["Guillaume Pinot <texitoi@texitoi.eu>", "others"]
+description = "Parse command line argument by defining a struct."
+documentation = "https://docs.rs/structopt"
+repository = "https://github.com/TeXitoi/structopt"
+keywords = ["clap", "cli", "derive", "docopt"]
+categories = ["command-line-interface"]
+license = "Apache-2.0/MIT"
+readme = "README.md"
+
+[features]
+default = ["clap/default"]
+suggestions = ["clap/suggestions"]
+color = ["clap/color"]
+wrap_help = ["clap/wrap_help"]
+yaml = ["clap/yaml"]
+lints = ["clap/lints"]
+debug = ["clap/debug"]
+no_cargo = ["clap/no_cargo"]
+doc = ["clap/doc"]
+paw = ["structopt-derive/paw"]
+
+[badges]
+travis-ci = { repository = "TeXitoi/structopt" }
+
+[dependencies]
+clap = { version = "2.33", default-features = false }
+structopt-derive = { path = "structopt-derive", version = "=0.4.0" }
+
+[dev-dependencies]
+trybuild = "1.0.5"
+rustversion = "1"
diff --git a/structopt/LICENSE-APACHE b/structopt/LICENSE-APACHE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/structopt/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/structopt/LICENSE-MIT b/structopt/LICENSE-MIT
new file mode 100644
index 0000000..e931b83
--- /dev/null
+++ b/structopt/LICENSE-MIT
@@ -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.
diff --git a/structopt/README.md b/structopt/README.md
new file mode 100644
index 0000000..48ac3c3
--- /dev/null
+++ b/structopt/README.md
@@ -0,0 +1,148 @@
+# StructOpt [![Build status](https://travis-ci.org/TeXitoi/structopt.svg?branch=master)](https://travis-ci.org/TeXitoi/structopt) [![](https://img.shields.io/crates/v/structopt.svg)](https://crates.io/crates/structopt) [![](https://docs.rs/structopt/badge.svg)](https://docs.rs/structopt)
+
+Parse command line arguments by defining a struct. It combines [clap](https://crates.io/crates/clap) with custom derive.
+
+## Documentation
+
+Find it on [Docs.rs](https://docs.rs/structopt). You can also check the [examples](https://github.com/TeXitoi/structopt/tree/master/examples) and the [changelog](https://github.com/TeXitoi/structopt/blob/master/CHANGELOG.md).
+
+## Example
+
+Add `structopt` to your dependencies of your `Cargo.toml`:
+```toml
+[dependencies]
+structopt = "0.3"
+```
+
+And then, in your rust file:
+```rust
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+/// A basic example
+#[derive(StructOpt, Debug)]
+#[structopt(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
+ #[structopt(short, long)]
+ debug: bool,
+
+ // The number of occurrences of the `v/verbose` flag
+ /// Verbose mode (-v, -vv, -vvv, etc.)
+ #[structopt(short, long, parse(from_occurrences))]
+ verbose: u8,
+
+ /// Set speed
+ #[structopt(short, long, default_value = "42")]
+ speed: f64,
+
+ /// Output file
+ #[structopt(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
+ #[structopt(short = "c", long)]
+ nb_cars: Option<i32>,
+
+ /// admin_level to consider
+ #[structopt(short, long)]
+ level: Vec<String>,
+
+ /// Files to process
+ #[structopt(name = "FILE", parse(from_os_str))]
+ files: Vec<PathBuf>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ 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]...
+
+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",
+ ],
+}
+```
+
+## StructOpt rustc version policy
+
+- Minimum rustc version modification must be specified in the [changelog](https://github.com/TeXitoi/structopt/blob/master/CHANGELOG.md) and in the [travis configuration](https://github.com/TeXitoi/structopt/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 StructOpt's dependencies (`cargo update` will not fail on StructOpt).
+- Contributors can increment minimum rustc version if the library user experience is improved.
+
+## 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.
diff --git a/structopt/examples/README.md b/structopt/examples/README.md
new file mode 100644
index 0000000..f0db20b
--- /dev/null
+++ b/structopt/examples/README.md
@@ -0,0 +1,82 @@
+# Collection of examples "how to use `structopt`"
+
+### [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 `structopt`.
+
+### [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 `StructOpt`.
+
+### [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 `structopt`.
+
+### [Flatten](flatten.rs)
+
+How to use `#[structopt(flatten)]`
+
+### [`bash` completions](gen_completions.rs)
+
+Generating `bash` completions with `structopt`.
+
+### [Git](git.rs)
+
+Pseudo-`git` example, shows how to use subcommands and how to document them.
+
+### [Groups](group.rs)
+
+Using `clap::Arg::group` with `structopt`.
+
+### [`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 `#[structopt(rename_all)]` works.
+
+### [Skip](skip.rs)
+
+How to use `#[structopt(skip)]`.
+
+### [Aliases](subcommand_aliases.rs)
+
+How to use aliases
+
+### [`true` or `false`](true_or_false.rs)
+
+How to express "`"true"` or `"false"` argument.
diff --git a/structopt/examples/after_help.rs b/structopt/examples/after_help.rs
new file mode 100644
index 0000000..db2845f
--- /dev/null
+++ b/structopt/examples/after_help.rs
@@ -0,0 +1,19 @@
+//! How to append a postscript to the help message generated.
+
+use structopt::StructOpt;
+
+/// I am a program and I do things.
+///
+/// Sometimes they even work.
+#[derive(StructOpt, Debug)]
+#[structopt(after_help = "Beware `-d`, dragons be here")]
+struct Opt {
+ /// Release the dragon.
+ #[structopt(short)]
+ dragon: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/at_least_two.rs b/structopt/examples/at_least_two.rs
new file mode 100644
index 0000000..683db50
--- /dev/null
+++ b/structopt/examples/at_least_two.rs
@@ -0,0 +1,15 @@
+//! How to require presence of at least N values,
+//! like `val1 val2 ... valN ... valM`.
+
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct Opt {
+ #[structopt(required = true, min_values = 2)]
+ foos: Vec<String>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/basic.rs b/structopt/examples/basic.rs
new file mode 100644
index 0000000..510e0e0
--- /dev/null
+++ b/structopt/examples/basic.rs
@@ -0,0 +1,48 @@
+//! A somewhat comprehensive example of a typical `StructOpt` usage.use
+
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+/// A basic example
+#[derive(StructOpt, Debug)]
+#[structopt(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
+ #[structopt(short, long)]
+ debug: bool,
+
+ // The number of occurrences of the `v/verbose` flag
+ /// Verbose mode (-v, -vv, -vvv, etc.)
+ #[structopt(short, long, parse(from_occurrences))]
+ verbose: u8,
+
+ /// Set speed
+ #[structopt(short, long, default_value = "42")]
+ speed: f64,
+
+ /// Output file
+ #[structopt(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
+ #[structopt(short = "c", long)]
+ nb_cars: Option<i32>,
+
+ /// admin_level to consider
+ #[structopt(short, long)]
+ level: Vec<String>,
+
+ /// Files to process
+ #[structopt(name = "FILE", parse(from_os_str))]
+ files: Vec<PathBuf>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:#?}", opt);
+}
diff --git a/structopt/examples/deny_missing_docs.rs b/structopt/examples/deny_missing_docs.rs
new file mode 100644
index 0000000..82b1e63
--- /dev/null
+++ b/structopt/examples/deny_missing_docs.rs
@@ -0,0 +1,51 @@
+// 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.
+
+// This should be in tests but it will not work until
+// https://github.com/rust-lang/rust/issues/24584 is fixed
+
+//! A test to check that structopt compiles with deny(missing_docs)
+
+#![deny(missing_docs)]
+
+use structopt::StructOpt;
+
+/// The options
+#[derive(StructOpt, Debug, PartialEq)]
+pub struct Opt {
+ #[structopt(short)]
+ verbose: bool,
+ #[structopt(subcommand)]
+ cmd: Option<Cmd>,
+}
+
+/// Some subcommands
+#[derive(StructOpt, Debug, PartialEq)]
+pub enum Cmd {
+ /// command A
+ A,
+ /// command B
+ B {
+ /// Alice?
+ #[structopt(short)]
+ alice: bool,
+ },
+ /// command C
+ C(COpt),
+}
+
+/// The options for C
+#[derive(StructOpt, Debug, PartialEq)]
+pub struct COpt {
+ #[structopt(short)]
+ bob: bool,
+}
+
+fn main() {
+ println!("{:?}", Opt::from_args());
+}
diff --git a/structopt/examples/doc_comments.rs b/structopt/examples/doc_comments.rs
new file mode 100644
index 0000000..810101f
--- /dev/null
+++ b/structopt/examples/doc_comments.rs
@@ -0,0 +1,74 @@
+//! How to use doc comments in place of `help/long_help`.
+
+use structopt::StructOpt;
+
+/// A basic example for the usage of doc comments as replacement
+/// of the arguments `help`, `long_help`, `about` and `long_about`.
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ /// Just use doc comments to replace `help`, `long_help`,
+ /// `about` or `long_about` input.
+ #[structopt(short, long)]
+ first_flag: bool,
+
+ /// Split between `help` and `long_help`.
+ ///
+ /// In the previous case structopt 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 structopt argument with a
+ /// raw string as shown on the `third_flag` description.
+ #[structopt(short, long)]
+ second_flag: bool,
+
+ #[structopt(
+ 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,
+
+ #[structopt(subcommand)]
+ sub_command: SubCommand,
+}
+
+#[derive(StructOpt, Debug)]
+#[structopt()]
+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::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/enum_in_args.rs b/structopt/examples/enum_in_args.rs
new file mode 100644
index 0000000..70347da
--- /dev/null
+++ b/structopt/examples/enum_in_args.rs
@@ -0,0 +1,25 @@
+//! How to use `arg_enum!` with `StructOpt`.
+
+use clap::arg_enum;
+use structopt::StructOpt;
+
+arg_enum! {
+ #[derive(Debug)]
+ enum Baz {
+ Foo,
+ Bar,
+ FooBar
+ }
+}
+
+#[derive(StructOpt, Debug)]
+struct Opt {
+ /// Important argument.
+ #[structopt(possible_values = &Baz::variants(), case_insensitive = true)]
+ i: Baz,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/enum_tuple.rs b/structopt/examples/enum_tuple.rs
new file mode 100644
index 0000000..0bad2e6
--- /dev/null
+++ b/structopt/examples/enum_tuple.rs
@@ -0,0 +1,26 @@
+//! How to extract subcommands' args into external structs.
+
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+pub struct Foo {
+ pub bar: Option<String>,
+}
+
+#[derive(Debug, StructOpt)]
+pub enum Command {
+ #[structopt(name = "foo")]
+ Foo(Foo),
+}
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "classify")]
+pub struct ApplicationArguments {
+ #[structopt(subcommand)]
+ pub command: Command,
+}
+
+fn main() {
+ let opt = ApplicationArguments::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/env.rs b/structopt/examples/env.rs
new file mode 100644
index 0000000..0477089
--- /dev/null
+++ b/structopt/examples/env.rs
@@ -0,0 +1,26 @@
+//! How to use environment variable fallback an how it
+//! interacts with `default_value`.
+
+use structopt::StructOpt;
+
+/// Example for allowing to specify options via environment variables.
+#[derive(StructOpt, Debug)]
+#[structopt(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
+ #[structopt(long, env = "API_URL")]
+ api_url: String,
+
+ // The default value is used if neither argument nor environment
+ // variable is specified.
+ /// Number of retries
+ #[structopt(long, env = "RETRIES", default_value = "5")]
+ retries: u32,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:#?}", opt);
+}
diff --git a/structopt/examples/example.rs b/structopt/examples/example.rs
new file mode 100644
index 0000000..7a9a514
--- /dev/null
+++ b/structopt/examples/example.rs
@@ -0,0 +1,54 @@
+//! Somewhat complex example of usage of structopt.
+
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "example")]
+/// An example of StructOpt usage.
+struct Opt {
+ // A flag, true if used in the command line.
+ #[structopt(short, long)]
+ /// Activate debug mode
+ debug: bool,
+
+ // An argument of type float, with a default value.
+ #[structopt(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`).
+ #[structopt(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`).
+ #[structopt(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).
+ #[structopt(skip)]
+ skipped: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/flatten.rs b/structopt/examples/flatten.rs
new file mode 100644
index 0000000..d51647f
--- /dev/null
+++ b/structopt/examples/flatten.rs
@@ -0,0 +1,29 @@
+//! How to use flattening.
+
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct Cmdline {
+ /// switch verbosity on
+ #[structopt(short)]
+ verbose: bool,
+
+ #[structopt(flatten)]
+ daemon_opts: DaemonOpts,
+}
+
+#[derive(StructOpt, Debug)]
+struct DaemonOpts {
+ /// daemon user
+ #[structopt(short)]
+ user: String,
+
+ /// daemon group
+ #[structopt(short)]
+ group: String,
+}
+
+fn main() {
+ let opt = Cmdline::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/gen_completions.rs b/structopt/examples/gen_completions.rs
new file mode 100644
index 0000000..4f35b07
--- /dev/null
+++ b/structopt/examples/gen_completions.rs
@@ -0,0 +1,26 @@
+// Copyright 2019-present structopt developers
+//
+// 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 structopt::clap::Shell;
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+/// An example of how to generate bash completions with structopt.
+struct Opt {
+ #[structopt(short, long)]
+ /// Activate debug mode
+ debug: bool,
+}
+
+fn main() {
+ // generate `bash` completions in "target" directory
+ Opt::clap().gen_completions(env!("CARGO_PKG_NAME"), Shell::Bash, "target");
+
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/git.rs b/structopt/examples/git.rs
new file mode 100644
index 0000000..494e9d1
--- /dev/null
+++ b/structopt/examples/git.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "git")]
+/// the stupid content tracker
+enum Opt {
+ /// fetch branches from remote repository
+ Fetch {
+ #[structopt(long)]
+ dry_run: bool,
+ #[structopt(long)]
+ all: bool,
+ #[structopt(default_value = "origin")]
+ repository: String,
+ },
+ #[structopt(help = "add files to the staging area")]
+ Add {
+ #[structopt(short)]
+ interactive: bool,
+ #[structopt(short)]
+ all: bool,
+ files: Vec<String>,
+ },
+}
+
+fn main() {
+ let matches = Opt::from_args();
+
+ println!("{:?}", matches);
+}
diff --git a/structopt/examples/group.rs b/structopt/examples/group.rs
new file mode 100644
index 0000000..d53de6a
--- /dev/null
+++ b/structopt/examples/group.rs
@@ -0,0 +1,31 @@
+//! How to use `clap::Arg::group`
+
+use structopt::{clap::ArgGroup, StructOpt};
+
+#[derive(StructOpt, Debug)]
+#[structopt(group = ArgGroup::with_name("verb").required(true))]
+struct Opt {
+ /// Set a custom HTTP verb
+ #[structopt(long, group = "verb")]
+ method: Option<String>,
+ /// HTTP GET
+ #[structopt(long, group = "verb")]
+ get: bool,
+ /// HTTP HEAD
+ #[structopt(long, group = "verb")]
+ head: bool,
+ /// HTTP POST
+ #[structopt(long, group = "verb")]
+ post: bool,
+ /// HTTP PUT
+ #[structopt(long, group = "verb")]
+ put: bool,
+ /// HTTP DELETE
+ #[structopt(long, group = "verb")]
+ delete: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/keyvalue.rs b/structopt/examples/keyvalue.rs
new file mode 100644
index 0000000..12ce6fc
--- /dev/null
+++ b/structopt/examples/keyvalue.rs
@@ -0,0 +1,36 @@
+//! How to parse "key=value" pairs with structopt.
+
+use std::error::Error;
+use structopt::StructOpt;
+
+/// 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(StructOpt, 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.
+ #[structopt(short = "D", parse(try_from_str = parse_key_val), number_of_values = 1)]
+ defines: Vec<(String, i32)>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/negative_flag.rs b/structopt/examples/negative_flag.rs
new file mode 100644
index 0000000..b178bf5
--- /dev/null
+++ b/structopt/examples/negative_flag.rs
@@ -0,0 +1,15 @@
+//! How to add `no-thing` flag which is `true` by default and
+//! `false` if passed.
+
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+struct Opt {
+ #[structopt(long = "no-verbose", parse(from_flag = std::ops::Not::not))]
+ verbose: bool,
+}
+
+fn main() {
+ let cmd = Opt::from_args();
+ println!("{:#?}", cmd);
+}
diff --git a/structopt/examples/no_version.rs b/structopt/examples/no_version.rs
new file mode 100644
index 0000000..a542ec1
--- /dev/null
+++ b/structopt/examples/no_version.rs
@@ -0,0 +1,17 @@
+//! How to completely remove version.
+
+use structopt::clap::AppSettings;
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(
+ name = "no_version",
+ no_version,
+ global_settings = &[AppSettings::DisableVersion]
+)]
+struct Opt {}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/rename_all.rs b/structopt/examples/rename_all.rs
new file mode 100644
index 0000000..35f3c4f
--- /dev/null
+++ b/structopt/examples/rename_all.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(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.
+ #[structopt(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.
+ #[structopt(rename_all = "pascal_case")]
+ SecondCommand {
+ // We can also override it again on a single field.
+ /// Nice quiet flag. No one is annoyed.
+ #[structopt(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.
+ #[structopt(subcommand)]
+ cmds: Subcommands,
+
+ // or flattened structs.
+ #[structopt(flatten)]
+ options: BonusOptions,
+ },
+}
+
+#[derive(StructOpt, Debug)]
+enum Subcommands {
+ // This one will be available as `first-subcommand`.
+ FirstSubcommand,
+}
+
+#[derive(StructOpt, Debug)]
+struct BonusOptions {
+ // And this one will be available as `baz-option`.
+ #[structopt(long)]
+ baz_option: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/skip.rs b/structopt/examples/skip.rs
new file mode 100644
index 0000000..1f44769
--- /dev/null
+++ b/structopt/examples/skip.rs
@@ -0,0 +1,47 @@
+//! How to use `#[structopt(skip)]`
+
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug, PartialEq)]
+pub struct Opt {
+ #[structopt(long, short)]
+ number: u32,
+ #[structopt(skip)]
+ k: Kind,
+ #[structopt(skip)]
+ v: Vec<u32>,
+
+ #[structopt(skip = Kind::A)]
+ k2: Kind,
+ #[structopt(skip = vec![1, 2, 3])]
+ v2: Vec<u32>,
+ #[structopt(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::from_iter(&["test", "-n", "10"]),
+ Opt {
+ number: 10,
+ k: Kind::B,
+ v: vec![],
+
+ k2: Kind::A,
+ v2: vec![1, 2, 3],
+ s: String::from("cake")
+ }
+ );
+}
diff --git a/structopt/examples/subcommand_aliases.rs b/structopt/examples/subcommand_aliases.rs
new file mode 100644
index 0000000..30b8cc3
--- /dev/null
+++ b/structopt/examples/subcommand_aliases.rs
@@ -0,0 +1,21 @@
+//! How to assign some aliases to subcommands
+
+use structopt::clap::AppSettings;
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+// https://docs.rs/clap/2/clap/enum.AppSettings.html#variant.InferSubcommands
+#[structopt(setting = AppSettings::InferSubcommands)]
+enum Opt {
+ // https://docs.rs/clap/2/clap/struct.App.html#method.alias
+ #[structopt(alias = "foobar")]
+ Foo,
+ // https://docs.rs/clap/2/clap/struct.App.html#method.aliases
+ #[structopt(aliases = &["baz", "fizz"])]
+ Bar,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/examples/true_or_false.rs b/structopt/examples/true_or_false.rs
new file mode 100644
index 0000000..31a543e
--- /dev/null
+++ b/structopt/examples/true_or_false.rs
@@ -0,0 +1,41 @@
+//! How to parse `--foo=true --bar=false` and turn them into bool.
+
+use structopt::StructOpt;
+
+fn true_or_false(s: &str) -> Result<bool, &'static str> {
+ match s {
+ "true" => Ok(true),
+ "false" => Ok(false),
+ _ => Err("expected `true` or `false`"),
+ }
+}
+
+#[derive(StructOpt, 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.
+ #[structopt(long, parse(try_from_str))]
+ foo: bool,
+
+ // Of course, this could be done with an explicit parser function.
+ #[structopt(long, parse(try_from_str = true_or_false))]
+ bar: bool,
+
+ // `bool` can be positional only with explicit `parse(...)` annotation
+ #[structopt(long, parse(try_from_str))]
+ boom: bool,
+}
+
+fn main() {
+ assert_eq!(
+ Opt::from_iter(&["test", "--foo=true", "--bar=false", "true"]),
+ Opt {
+ foo: true,
+ bar: false,
+ boom: true
+ }
+ );
+ // no beauty, only truth and falseness
+ assert!(Opt::from_iter_safe(&["test", "--foo=beauty"]).is_err());
+}
diff --git a/structopt/link-check-headers.json b/structopt/link-check-headers.json
new file mode 100644
index 0000000..c1bb248
--- /dev/null
+++ b/structopt/link-check-headers.json
@@ -0,0 +1,14 @@
+{
+ "httpHeaders": [
+ {
+ "urls": [
+ "https://",
+ "http://"
+ ],
+ "headers": {
+ "User-Agent": "broken links checker (https://github.com/TeXitoi/structopt)",
+ "Accept": "text/html"
+ }
+ }
+ ]
+}
diff --git a/structopt/src/lib.rs b/structopt/src/lib.rs
new file mode 100644
index 0000000..70c0768
--- /dev/null
+++ b/structopt/src/lib.rs
@@ -0,0 +1,1015 @@
+// 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.
+
+#![deny(missing_docs)]
+
+//! This crate defines the `StructOpt` trait and its custom derive.
+//!
+//! ## Features
+//!
+//! If you want to disable all the `clap` features (colors,
+//! suggestions, ..) add `default-features = false` to the `structopt`
+//! dependency:
+//!
+//! ```toml
+//! [dependencies]
+//! structopt = { version = "0.3", default-features = false }
+//! ```
+//!
+//! Support for [`paw`](https://github.com/rust-cli/paw) (the
+//! `Command line argument paw-rser abstraction for main`) is disabled
+//! by default, but can be enabled in the `structopt` dependency
+//! with the feature `paw`:
+//!
+//! ```toml
+//! [dependencies]
+//! structopt = { version = "0.3", features = [ "paw" ] }
+//! paw = "1.0"
+//! ```
+//!
+//! # Table of Contents
+//!
+//! - [How to `derive(StructOpt)`](#how-to-derivestructopt)
+//! - [Attributes](#attributes)
+//! - [Raw methods](#raw-methods)
+//! - [Magical methods](#magical-methods)
+//! - Arguments
+//! - [Type magic](#type-magic)
+//! - [Specifying argument types](#specifying-argument-types)
+//! - [Help messages](#help-messages)
+//! - [Environment variable fallback](#environment-variable-fallback)
+//! - [Skipping fields](#skipping-fields)
+//! - [Subcommands](#subcommands)
+//! - [Optional subcommands](#optional-subcommands)
+//! - [Flattening](#flattening)
+//! - [Custom string parsers](#custom-string-parsers)
+//!
+//!
+//!
+//! ## How to `derive(StructOpt)`
+//!
+//! First, let's look at the example:
+//!
+//! ```should_panic
+//! use std::path::PathBuf;
+//! use structopt::StructOpt;
+//!
+//! #[derive(Debug, StructOpt)]
+//! #[structopt(name = "example", about = "An example of StructOpt usage.")]
+//! struct Opt {
+//! /// Activate debug mode
+//! // short and long flags (-d, --debug) will be deduced from the field's name
+//! #[structopt(short, long)]
+//! debug: bool,
+//!
+//! /// Set speed
+//! // we don't want to name it "speed", need to look smart
+//! #[structopt(short = "v", long = "velocity", default_value = "42")]
+//! speed: f64,
+//!
+//! /// Input file
+//! #[structopt(parse(from_os_str))]
+//! input: PathBuf,
+//!
+//! /// Output file, stdout if not present
+//! #[structopt(parse(from_os_str))]
+//! output: Option<PathBuf>,
+//!
+//! /// Where to write the output: to `stdout` or `file`
+//! #[structopt(short)]
+//! out_type: String,
+//!
+//! /// File name: only required when `out` is set to `file`
+//! #[structopt(name = "FILE", required_if("out_type", "file"))]
+//! file_name: String,
+//! }
+//!
+//! fn main() {
+//! let opt = Opt::from_args();
+//! println!("{:?}", opt);
+//! }
+//! ```
+//!
+//! So `derive(StructOpt)` tells Rust to generate a command line parser,
+//! and the various `structopt` attributes are simply
+//! used for additional parameters.
+//!
+//! First, define a struct, whatever its name. This structure
+//! corresponds to a `clap::App`, its fields correspond to `clap::Arg`
+//! (unless they're [subcommands](#subcommands)),
+//! and you can adjust these apps and args by `#[structopt(...)]` [attributes](#attributes).
+//!
+//! **Note:**
+//! _________________
+//! Keep in mind that `StructOpt` trait is more than just `from_args` method.
+//! It has a number of additional features, including access to underlying
+//! `clap::App` via `StructOpt::clap()`. See the
+//! [trait's reference documentation](trait.StructOpt.html).
+//! _________________
+//!
+//! ## Attributes
+//!
+//! `#[structopt(...)]` attributes fall into two categories:
+//! - `structopt`'s own [magical methods](#magical-methods).
+//!
+//! They are used by `structopt` itself. They come mostly in
+//! `attr = ["whatever"]` form, but some `attr(args...)` also exist.
+//!
+//! - [`raw` attributes](#raw-methods).
+//!
+//! They represent explicit `clap::Arg/App` method calls.
+//! They are what used to be explicit `#[structopt(raw(...))]` attrs in pre-0.3 `structopt`
+//!
+//! Every `structopt attribute` looks like comma-separated sequence of methods:
+//! ```rust,ignore
+//! #[structopt(
+//! short, // method with no arguments - always magical
+//! long = "--long-option", // method with one argument
+//! required_if("out", "file"), // method with one and more args
+//! parse(from_os_str = path::to::parser) // some magical methods have their own syntax
+//! )]
+//! ```
+//!
+//! `#[structopt(...)]` attributes can be placed on top of `struct`, `enum`,
+//! `struct` field or `enum` variant. Attributes on top of `struct` or `enum`
+//! represent `clap::App` method calls, field or variant attributes correspond
+//! to `clap::Arg` method calls.
+//!
+//! In other words, the `Opt` struct from the example above
+//! will be turned into this (*details omitted*):
+//!
+//! ```
+//! # use structopt::clap::{Arg, App};
+//! App::new("example")
+//! .version("0.2.0")
+//! .about("An example of StructOpt usage.")
+//! .arg(Arg::with_name("debug")
+//! .help("Activate debug mode")
+//! .short("debug")
+//! .long("debug"))
+//! .arg(Arg::with_name("speed")
+//! .help("Set speed")
+//! .short("v")
+//! .long("velocity")
+//! .default_value("42"))
+//! // and so on
+//! # ;
+//! ```
+//!
+//! ## Raw methods
+//!
+//! They are the reason why `structopt` is so flexible.
+//!
+//! Each and every method from `clap::App` and `clap::Arg` can be used directly -
+//! just `#[structopt(method_name = single_arg)]` or `#[structopt(method_name(arg1, arg2))]`
+//! and it just works. As long as `method_name` is not one of the magical methods -
+//! it's just a method call.
+//!
+//! **Note:**
+//! _________________
+//!
+//! "Raw methods" are direct replacement for pre-0.3 structopt's
+//! `#[structopt(raw(...))]` attributes, any time you would have used a `raw()` attribute
+//! in 0.2 you should use raw method in 0.3.
+//!
+//! Unfortunately, old raw attributes collide with `clap::Arg::raw` method. To explicitly
+//! warn users of this change we allow `#[structopt(raw())]` only with `true` or `false`
+//! literals (this method is supposed to be called only with `true` anyway).
+//! __________________
+//!
+//! ## Magical methods
+//!
+//! They are the reason why `structopt` is so easy to use and convenient in most cases.
+//! Many of them have defaults, some of them get used even if not mentioned.
+//!
+//! Methods may be used on "top level" (on top of a `struct`, `enum` or `enum` variant)
+//! and/or on "field-level" (on top of a `struct` field or *inside* of an enum variant).
+//! Top level (non-magical) methods correspond to `App::method` calls, field-level methods
+//! are `Arg::method` calls.
+//!
+//! ```ignore
+//! #[structopt(top_level)]
+//! struct Foo {
+//! #[structopt(field_level)]
+//! field: u32
+//! }
+//!
+//! #[structopt(top_level)]
+//! enum Bar {
+//! #[structopt(top_level)]
+//! Pineapple {
+//! #[structopt(field_level)]
+//! chocolate: String
+//! },
+//!
+//! #[structopt(top_level)]
+//! Orange,
+//! }
+//! ```
+//!
+//! - `name`: `[name = "name"]`
+//! - On top level: `App::new("name")`.
+//!
+//! The binary name displayed in help messages. Defaults to the crate name given by Cargo.
+//!
+//! - On field-level: `Arg::with_name("name")`.
+//!
+//! The name for the argument the field stands for, this name appears in help messages.
+//! Defaults to a name, deduced from a field, see also
+//! [`rename_all`](#specifying-argument-types).
+//!
+//! - `version`: `[version = "version"]`
+//!
+//! Usable only on top level: `App::version("version" or env!(CARGO_PKG_VERSION))`.
+//!
+//! The version displayed in help messages.
+//! Defaults to the crate version given by Cargo. If `CARGO_PKG_VERSION` is not
+//! set no `.version()` calls will be generated unless requested.
+//!
+//! - `no_version`: `no_version`
+//!
+//! Usable only on top level. Prevents default `App::version` call, i.e
+//! when no `version = "version"` mentioned.
+//!
+//! - `author`: `author [= "author"]`
+//!
+//! Usable only on top level: `App::author("author" or env!(CARGO_PKG_AUTHOR))`.
+//!
+//! Author/maintainer of the binary, this name appears in help messages.
+//! Defaults to the crate author given by cargo, but only when `author` explicitly mentioned.
+//!
+//! - `about`: `about [= "about"]`
+//!
+//! Usable only on top level: `App::about("about" or env!(CARGO_PKG_DESCRIPTION))`.
+//!
+//! Short description of the binary, appears in help messages.
+//! Defaults to the crate description given by cargo,
+//! but only when `about` explicitly mentioned.
+//!
+//! - [`short`](#specifying-argument-types): `short [= "short-opt-name"]`
+//!
+//! Usable only on field-level.
+//!
+//! - [`long`](#specifying-argument-types): `long [= "long-opt-name"]`
+//!
+//! Usable only on field-level.
+//!
+//! - [`rename_all`](#specifying-argument-types): [`rename_all = "kebab"/"snake"/"screaming-snake"/"camel"/"pascal"/"verbatim"]`
+//!
+//! Usable both on top level and field level.
+//!
+//! - [`parse`](#custom-string-parsers): `parse(type [= path::to::parser::fn])`
+//!
+//! Usable only on field-level.
+//!
+//! - [`skip`](#skipping-fields): `skip [= expr]`
+//!
+//! Usable only on field-level.
+//!
+//! - [`flatten`](#flattening): `flatten`
+//!
+//! Usable only on field-level.
+//!
+//! - [`subcommand`](#subcommands): `subcommand`
+//!
+//! Usable only on field-level.
+//!
+//! - [`env`](#environment-variable-fallback): `env [= str_literal]`
+//!
+//! Usable only on field-level.
+//!
+//! - [`rename_all_env`](##auto-deriving-environment-variables): [`rename_all_env = "kebab"/"snake"/"screaming-snake"/"camel"/"pascal"/"verbatim"]`
+//!
+//! Usable both on top level and field level.
+//!
+//! - [`verbatim_doc_comment`](#doc-comment-preprocessing-and-structoptverbatim_doc_comment):
+//! `verbatim_doc_comment`
+//!
+//! Usable both on top level and field level.
+//!
+//! ## Type magic
+//!
+//! One of major things that makes `structopt` so awesome is it's type magic.
+//! Do you want optional positional argument? Use `Option<T>`! Or perhaps optional argument
+//! that optionally takes value (`[--opt=[val]]`)? Use `Option<Option<T>>`!
+//!
+//! Here is the table of types and `clap` methods they correspond to:
+//!
+//! Type | Effect | Added method call to `clap::Arg`
+//! -----------------------------|---------------------------------------------------|--------------------------------------
+//! `bool` | `true` if the flag is present | `.takes_value(false).multiple(false)`
+//! `Option<T: FromStr>` | optional positional argument or option | `.takes_value(true).multiple(false)`
+//! `Option<Option<T: FromStr>>` | optional option with optional value | `.takes_value(true).multiple(false).min_values(0).max_values(1)`
+//! `Vec<T: FromStr>` | list of options or the other positional arguments | `.takes_value(true).multiple(true)`
+//! `Option<Vec<T: FromStr>` | optional list of options | `.takes_values(true).multiple(true).min_values(0)`
+//! `T: FromStr` | required option or positional argument | `.takes_value(true).multiple(false).required(!has_default)`
+//!
+//! The `FromStr` trait is used to convert the argument to the given
+//! type, and the `Arg::validator` method is set to a method using
+//! `to_string()` (`FromStr::Err` must implement `std::fmt::Display`).
+//! If you would like to use a custom string parser other than `FromStr`, see
+//! the [same titled section](#custom-string-parsers) below.
+//!
+//! **Note:**
+//! _________________
+//! Pay attention that *only literal occurrence* of this types is special, for example
+//! `Option<T>` is special while `::std::option::Option<T>` is not.
+//!
+//! If you need to avoid special casing you can make a `type` alias and
+//! use it in place of the said type.
+//! _________________
+//!
+//! **Note:**
+//! _________________
+//! `bool` cannot be used as positional argument unless you provide an explicit parser.
+//! If you need a positional bool, for example to parse `true` or `false`, you must
+//! annotate the field with explicit [`#[structopt(parse(...))]`](#custom-string-parsers).
+//! _________________
+//!
+//! Thus, the `speed` argument is generated as:
+//!
+//! ```
+//! # extern crate clap;
+//! # fn parse_validator<T>(_: String) -> Result<(), String> { unimplemented!() }
+//! # fn main() {
+//! clap::Arg::with_name("speed")
+//! .takes_value(true)
+//! .multiple(false)
+//! .required(false)
+//! .validator(parse_validator::<f64>)
+//! .short("v")
+//! .long("velocity")
+//! .help("Set speed")
+//! .default_value("42");
+//! # }
+//! ```
+//!
+//! ## Specifying argument types
+//!
+//! There are three types of arguments that can be supplied to each
+//! (sub-)command:
+//!
+//! - short (e.g. `-h`),
+//! - long (e.g. `--help`)
+//! - and positional.
+//!
+//! Like clap, structopt defaults to creating positional arguments.
+//!
+//! If you want to generate a long argument you can specify either
+//! `long = $NAME`, or just `long` to get a long flag generated using
+//! the field name. The generated casing style can be modified using
+//! the `rename_all` attribute. See the `rename_all` example for more.
+//!
+//! For short arguments, `short` will use the first letter of the
+//! field name by default, but just like the long option it's also
+//! possible to use a custom letter through `short = $LETTER`.
+//!
+//! If an argument is renamed using `name = $NAME` any following call to
+//! `short` or `long` will use the new name.
+//!
+//! **Attention**: If these arguments are used without an explicit name
+//! the resulting flag is going to be renamed using `kebab-case` if the
+//! `rename_all` attribute was not specified previously. The same is true
+//! for subcommands with implicit naming through the related data structure.
+//!
+//! ```
+//! use structopt::StructOpt;
+//!
+//! #[derive(StructOpt)]
+//! #[structopt(rename_all = "kebab-case")]
+//! struct Opt {
+//! /// This option can be specified with something like `--foo-option
+//! /// value` or `--foo-option=value`
+//! #[structopt(long)]
+//! foo_option: String,
+//!
+//! /// This option can be specified with something like `-b value` (but
+//! /// not `--bar-option value`).
+//! #[structopt(short)]
+//! bar_option: String,
+//!
+//! /// This option can be specified either `--baz value` or `-z value`.
+//! #[structopt(short = "z", long = "baz")]
+//! baz_option: String,
+//!
+//! /// This option can be specified either by `--custom value` or
+//! /// `-c value`.
+//! #[structopt(name = "custom", long, short)]
+//! custom_option: String,
+//!
+//! /// This option is positional, meaning it is the first unadorned string
+//! /// you provide (multiple others could follow).
+//! my_positional: String,
+//!
+//! /// This option is skipped and will be filled with the default value
+//! /// for its type (in this case 0).
+//! #[structopt(skip)]
+//! skipped: u32,
+//!
+//! }
+//!
+//! # fn main() {
+//! # Opt::from_clap(&Opt::clap().get_matches_from(
+//! # &["test", "--foo-option", "", "-b", "", "--baz", "", "--custom", "", "positional"]));
+//! # }
+//! ```
+//!
+//! ## Help messages
+//!
+//! In clap, help messages for the whole binary can be specified
+//! via [`App::about`] and [`App::long_about`] while help messages
+//! for individual arguments can be specified via [`Arg::help`] and [`Arg::long_help`]".
+//!
+//! `long_*` variants are used when user calls the program with
+//! `--help` and "short" variants are used with `-h` flag. In `structopt`,
+//! you can use them via [raw methods](#raw-methods), for example:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//!
+//! #[derive(StructOpt)]
+//! #[structopt(about = "I am a program and I work, just pass `-h`")]
+//! struct Foo {
+//! #[structopt(short, help = "Pass `-h` and you'll see me!")]
+//! bar: String
+//! }
+//! ```
+//!
+//! For convenience, doc comments can be used instead of raw methods
+//! (this example works exactly like the one above):
+//!
+//! ```
+//! # use structopt::StructOpt;
+//!
+//! #[derive(StructOpt)]
+//! /// I am a program and I work, just pass `-h`
+//! struct Foo {
+//! /// Pass `-h` and you'll see me!
+//! bar: String
+//! }
+//! ```
+//!
+//! Doc comments on [top-level](#magical-methods) will be turned into
+//! `App::about/long_about` call (see below), doc comments on field-level are
+//! `Arg::help/long_help` calls.
+//!
+//! **Important:**
+//! _________________
+//!
+//! Raw methods have priority over doc comments!
+//!
+//! **Top level doc comments always generate `App::about/long_about` calls!**
+//! If you really want to use the `App::help/long_help` methods (you likely don't),
+//! use a raw method to override the `App::about` call generated from the doc comment.
+//! __________________
+//!
+//! ### `long_help` and `--help`
+//!
+//! A message passed to [`App::long_help`] or [`Arg::long_about`] will be displayed whenever
+//! your program is called with `--help` instead of `-h`. Of course, you can
+//! use them via raw methods as described [above](#help-messages).
+//!
+//! The more convenient way is to use a so-called "long" doc comment:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//! #[derive(StructOpt)]
+//! /// Hi there, I'm Robo!
+//! ///
+//! /// I like beeping, stumbling, eating your electricity,
+//! /// and making records of you singing in a shower.
+//! /// Pay up, or I'll upload it to youtube!
+//! struct Robo {
+//! /// Call my brother SkyNet.
+//! ///
+//! /// I am artificial superintelligence. I won't rest
+//! /// until I'll have destroyed humanity. Enjoy your
+//! /// pathetic existence, you mere mortals.
+//! #[structopt(long)]
+//! kill_all_humans: bool
+//! }
+//! ```
+//!
+//! A long doc comment consists of three parts:
+//! * Short summary
+//! * A blank line (whitespace only)
+//! * Detailed description, all the rest
+//!
+//! In other words, "long" doc comment consists of two or more paragraphs,
+//! with the first being a summary and the rest being the detailed description.
+//!
+//! **A long comment will result in two method calls**, `help(<summary>)` and
+//! `long_help(<whole comment>)`, so clap will display the summary with `-h`
+//! and the whole help message on `--help` (see below).
+//!
+//! So, the example above will be turned into this (details omitted):
+//! ```
+//! clap::App::new("<name>")
+//! .about("Hi there, I'm Robo!")
+//! .long_about("Hi there, I'm Robo!\n\n\
+//! I like beeping, stumbling, eating your electricity,\
+//! and making records of you singing in a shower.\
+//! Pay up or I'll upload it to youtube!")
+//! // args...
+//! # ;
+//! ```
+//!
+//! ### `-h` vs `--help` (A.K.A `help()` vs `long_help()`)
+//!
+//! The `-h` flag is not the same as `--help`.
+//!
+//! -h corresponds to Arg::help/App::about and requests short "summary" messages
+//! while --help corresponds to Arg::long_help/App::long_about and requests more
+//! detailed, descriptive messages.
+//!
+//! It is entirely up to `clap` what happens if you used only one of
+//! [`Arg::help`]/[`Arg::long_help`], see `clap`'s documentation for these methods.
+//!
+//! As of clap v2.33, if only a short message ([`Arg::help`]) or only
+//! a long ([`Arg::long_help`]) message is provided, clap will use it
+//! for both -h and --help. The same logic applies to `about/long_about`.
+//!
+//! ### Doc comment preprocessing and `#[structopt(verbatim_doc_comment)]`
+//!
+//! `structopt` applies some preprocessing to doc comments to ease the most common uses:
+//!
+//! * Strip leading and trailing whitespace from every line, if present.
+//!
+//! * Strip leading and trailing blank lines, if present.
+//!
+//! * Interpret each group of non-empty lines as a word-wrapped paragraph.
+//!
+//! We replace newlines within paragraphs with spaces to allow the output
+//! to be re-wrapped to the terminal width.
+//!
+//! * Strip any excess blank lines so that there is exactly one per paragraph break.
+//!
+//! * If the first paragraph ends in exactly one period,
+//! remove the trailing period (i.e. strip trailing periods but not trailing ellipses).
+//!
+//! Sometimes you don't want this preprocessing to apply, for example the comment contains
+//! some ASCII art or markdown tables, you would need to preserve LFs along with
+//! blank lines and the leading/trailing whitespace. You can ask `structopt` to preserve them
+//! via `#[structopt(verbatim_doc_comment)]` attribute.
+//!
+//! **This attribute must be applied to each field separately**, there's no global switch.
+//!
+//! **Important:**
+//! ______________
+//! Keep in mind that `structopt` will *still* remove one leading space from each
+//! line, even if this attribute is present, to allow for a space between
+//! `///` and the content.
+//!
+//! Also, `structopt` will *still* remove leading and trailing blank lines so
+//! these formats are equivalent:
+//!
+//! ```ignore
+//! /** This is a doc comment
+//!
+//! Hello! */
+//!
+//! /**
+//! This is a doc comment
+//!
+//! Hello!
+//! */
+//!
+//! /// This is a doc comment
+//! ///
+//! /// Hello!
+//! ```
+//!
+//! Summary
+//! ______________
+//!
+//! [`App::about`]: https://docs.rs/clap/2/clap/struct.App.html#method.about
+//! [`App::long_about`]: https://docs.rs/clap/2/clap/struct.App.html#method.long_about
+//! [`Arg::help`]: https://docs.rs/clap/2/clap/struct.Arg.html#method.help
+//! [`Arg::long_help`]: https://docs.rs/clap/2/clap/struct.Arg.html#method.long_help
+//!
+//! ## Environment variable fallback
+//!
+//! It is possible to specify an environment variable fallback option for an arguments
+//! so that its value is taken from the specified environment variable if not
+//! given through the command-line:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//!
+//! #[derive(StructOpt)]
+//! struct Foo {
+//! #[structopt(short, long, env = "PARAMETER_VALUE")]
+//! parameter_value: String
+//! }
+//! # fn main() {}
+//! ```
+//!
+//! By default, values from the environment are shown in the help output (i.e. when invoking
+//! `--help`):
+//!
+//! ```shell
+//! $ cargo run -- --help
+//! ...
+//! OPTIONS:
+//! -p, --parameter-value <parameter-value> [env: PARAMETER_VALUE=env_value]
+//! ```
+//!
+//! In some cases this may be undesirable, for example when being used for passing
+//! credentials or secret tokens. In those cases you can use `hide_env_values` to avoid
+//! having structopt emit the actual secret values:
+//! ```
+//! # use structopt::StructOpt;
+//!
+//! #[derive(StructOpt)]
+//! struct Foo {
+//! #[structopt(long = "secret", env = "SECRET_VALUE", hide_env_values = true)]
+//! secret_value: String
+//! }
+//! ```
+//!
+//! ### Auto-deriving environment variables
+//!
+//! Environment variables tend to be called after the corresponding `struct`'s field,
+//! as in example above. The field is `secret_value` and the env var is "SECRET_VALUE";
+//! the name is the same, except casing is different.
+//!
+//! It's pretty tedious and error-prone to type the same name twice,
+//! so you can ask `structopt` to do that for you.
+//!
+//! ```
+//! # use structopt::StructOpt;
+//!
+//! #[derive(StructOpt)]
+//! struct Foo {
+//! #[structopt(long = "secret", env)]
+//! secret_value: String
+//! }
+//! ```
+//!
+//! It works just like `#[structopt(short/long)]`: if `env` is not set to some concrete
+//! value the value will be derived from the field's name. This is controlled by
+//! `#[structopt(rename_all_env)]`.
+//!
+//! `rename_all_env` works exactly as `rename_all` (including overriding)
+//! except default casing is `SCREAMING_SNAKE_CASE` instead of `kebab-case`.
+//!
+//! ## Skipping fields
+//!
+//! Sometimes you may want to add a field to your `Opt` struct that is not
+//! a command line option and `clap` should know nothing about it. You can ask
+//! `structopt` to skip the field entirely via `#[structopt(skip = value)]`
+//! (`value` must implement `Into<FieldType>`)
+//! or `#[structopt(skip)]` if you want assign the field with `Default::default()`
+//! (obviously, the field's type must implement `Default`).
+//!
+//! ```
+//! # use structopt::StructOpt;
+//! #[derive(StructOpt)]
+//! pub struct Opt {
+//! #[structopt(long, short)]
+//! number: u32,
+//!
+//! // these fields are to be assigned with Default::default()
+//!
+//! #[structopt(skip)]
+//! k: String,
+//! #[structopt(skip)]
+//! v: Vec<u32>,
+//!
+//! // these fields get set explicitly
+//!
+//! #[structopt(skip = vec![1, 2, 3])]
+//! k2: Vec<u32>,
+//! #[structopt(skip = "cake")] // &str implements Into<String>
+//! v2: String,
+//! }
+//! ```
+//!
+//! ## Subcommands
+//!
+//! Some applications, especially large ones, split their functionality
+//! through the use of "subcommands". Each of these act somewhat like a separate
+//! command, but is part of the larger group.
+//! One example is `git`, which has subcommands such as `add`, `commit`,
+//! and `clone`, to mention just a few.
+//!
+//! `clap` has this functionality, and `structopt` supports it through enums:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//!
+//! # use std::path::PathBuf;
+//! #[derive(StructOpt)]
+//! #[structopt(about = "the stupid content tracker")]
+//! enum Git {
+//! Add {
+//! #[structopt(short)]
+//! interactive: bool,
+//! #[structopt(short)]
+//! patch: bool,
+//! #[structopt(parse(from_os_str))]
+//! files: Vec<PathBuf>
+//! },
+//! Fetch {
+//! #[structopt(long)]
+//! dry_run: bool,
+//! #[structopt(long)]
+//! all: bool,
+//! repository: Option<String>
+//! },
+//! Commit {
+//! #[structopt(short)]
+//! message: Option<String>,
+//! #[structopt(short)]
+//! all: bool
+//! }
+//! }
+//! # fn main() {}
+//! ```
+//!
+//! Using `derive(StructOpt)` on an enum instead of a struct will produce
+//! a `clap::App` that only takes subcommands. So `git add`, `git fetch`,
+//! and `git commit` would be commands allowed for the above example.
+//!
+//! `structopt` also provides support for applications where certain flags
+//! need to apply to all subcommands, as well as nested subcommands:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//! # fn main() {}
+//! #[derive(StructOpt)]
+//! struct MakeCookie {
+//! #[structopt(name = "supervisor", default_value = "Puck", long = "supervisor")]
+//! supervising_faerie: String,
+//! /// The faerie tree this cookie is being made in.
+//! tree: Option<String>,
+//! #[structopt(subcommand)] // Note that we mark a field as a subcommand
+//! cmd: Command
+//! }
+//!
+//! #[derive(StructOpt)]
+//! enum Command {
+//! /// Pound acorns into flour for cookie dough.
+//! Pound {
+//! acorns: u32
+//! },
+//! /// Add magical sparkles -- the secret ingredient!
+//! Sparkle {
+//! #[structopt(short, parse(from_occurrences))]
+//! magicality: u64,
+//! #[structopt(short)]
+//! color: String
+//! },
+//! Finish(Finish),
+//! }
+//!
+//! // Subcommand can also be externalized by using a 1-uple enum variant
+//! #[derive(StructOpt)]
+//! struct Finish {
+//! #[structopt(short)]
+//! time: u32,
+//! #[structopt(subcommand)] // Note that we mark a field as a subcommand
+//! finish_type: FinishType
+//! }
+//!
+//! // subsubcommand!
+//! #[derive(StructOpt)]
+//! enum FinishType {
+//! Glaze {
+//! applications: u32
+//! },
+//! Powder {
+//! flavor: String,
+//! dips: u32
+//! }
+//! }
+//! ```
+//!
+//! Marking a field with `structopt(subcommand)` will add the subcommands of the
+//! designated enum to the current `clap::App`. The designated enum *must* also
+//! be derived `StructOpt`. So the above example would take the following
+//! commands:
+//!
+//! + `make-cookie pound 50`
+//! + `make-cookie sparkle -mmm --color "green"`
+//! + `make-cookie finish 130 glaze 3`
+//!
+//! ### Optional subcommands
+//!
+//! Subcommands may be optional:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//! # fn main() {}
+//! #[derive(StructOpt)]
+//! struct Foo {
+//! file: String,
+//! #[structopt(subcommand)]
+//! cmd: Option<Command>
+//! }
+//!
+//! #[derive(StructOpt)]
+//! enum Command {
+//! Bar,
+//! Baz,
+//! Quux
+//! }
+//! ```
+//!
+//! ## Flattening
+//!
+//! It can sometimes be useful to group related arguments in a substruct,
+//! while keeping the command-line interface flat. In these cases you can mark
+//! a field as `flatten` and give it another type that derives `StructOpt`:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//! # fn main() {}
+//! #[derive(StructOpt)]
+//! struct Cmdline {
+//! /// switch on verbosity
+//! #[structopt(short)]
+//! verbose: bool,
+//! #[structopt(flatten)]
+//! daemon_opts: DaemonOpts,
+//! }
+//!
+//! #[derive(StructOpt)]
+//! struct DaemonOpts {
+//! /// daemon user
+//! #[structopt(short)]
+//! user: String,
+//! /// daemon group
+//! #[structopt(short)]
+//! group: String,
+//! }
+//! ```
+//!
+//! In this example, the derived `Cmdline` parser will support the options `-v`,
+//! `-u` and `-g`.
+//!
+//! This feature also makes it possible to define a `StructOpt` struct in a
+//! library, parse the corresponding arguments in the main argument parser, and
+//! pass off this struct to a handler provided by that library.
+//!
+//! ## Custom string parsers
+//!
+//! If the field type does not have a `FromStr` implementation, or you would
+//! like to provide a custom parsing scheme other than `FromStr`, you may
+//! provide a custom string parser using `parse(...)` like this:
+//!
+//! ```
+//! # use structopt::StructOpt;
+//! # fn main() {}
+//! use std::num::ParseIntError;
+//! use std::path::PathBuf;
+//!
+//! fn parse_hex(src: &str) -> Result<u32, ParseIntError> {
+//! u32::from_str_radix(src, 16)
+//! }
+//!
+//! #[derive(StructOpt)]
+//! struct HexReader {
+//! #[structopt(short, parse(try_from_str = parse_hex))]
+//! number: u32,
+//! #[structopt(short, parse(from_os_str))]
+//! output: PathBuf,
+//! }
+//! ```
+//!
+//! There are five kinds of custom parsers:
+//!
+//! | Kind | Signature | Default |
+//! |-------------------|---------------------------------------|---------------------------------|
+//! | `from_str` | `fn(&str) -> T` | `::std::convert::From::from` |
+//! | `try_from_str` | `fn(&str) -> Result<T, E>` | `::std::str::FromStr::from_str` |
+//! | `from_os_str` | `fn(&OsStr) -> T` | `::std::convert::From::from` |
+//! | `try_from_os_str` | `fn(&OsStr) -> Result<T, OsString>` | (no default function) |
+//! | `from_occurrences`| `fn(u64) -> T` | `value as T` |
+//! | `from_flag` | `fn(bool) -> T` | `::std::convert::From::from` |
+//!
+//! The `from_occurrences` parser is special. Using `parse(from_occurrences)`
+//! results in the _number of flags occurrences_ being stored in the relevant
+//! field or being passed to the supplied function. In other words, it converts
+//! something like `-vvv` to `3`. This is equivalent to
+//! `.takes_value(false).multiple(true)`. Note that the default parser can only
+//! be used with fields of integer types (`u8`, `usize`, `i64`, etc.).
+//!
+//! The `from_flag` parser is also special. Using `parse(from_flag)` or
+//! `parse(from_flag = some_func)` will result in the field being treated as a
+//! flag even if it does not have type `bool`.
+//!
+//! When supplying a custom string parser, `bool` will not be treated specially:
+//!
+//! Type | Effect | Added method call to `clap::Arg`
+//! ------------|-------------------|--------------------------------------
+//! `Option<T>` | optional argument | `.takes_value(true).multiple(false)`
+//! `Vec<T>` | list of arguments | `.takes_value(true).multiple(true)`
+//! `T` | required argument | `.takes_value(true).multiple(false).required(!has_default)`
+//!
+//! In the `try_from_*` variants, the function will run twice on valid input:
+//! once to validate, and once to parse. Hence, make sure the function is
+//! side-effect-free.
+
+#[doc(hidden)]
+pub use structopt_derive::*;
+
+use std::ffi::OsString;
+
+/// Re-export of clap
+pub use clap;
+
+/// A struct that is converted from command line arguments.
+pub trait StructOpt {
+ /// Returns the corresponding `clap::App`.
+ fn clap<'a, 'b>() -> clap::App<'a, 'b>;
+
+ /// Creates the struct from `clap::ArgMatches`. It cannot fail
+ /// with a parameter generated by `clap` by construction.
+ fn from_clap(matches: &clap::ArgMatches<'_>) -> Self;
+
+ /// Gets the struct from the command line arguments. Print the
+ /// error message and quit the program in case of failure.
+ fn from_args() -> Self
+ where
+ Self: Sized,
+ {
+ Self::from_clap(&Self::clap().get_matches())
+ }
+
+ /// Gets the struct from any iterator such as a `Vec` of your making.
+ /// Print the error message and quit the program in case of failure.
+ fn from_iter<I>(iter: I) -> Self
+ where
+ Self: Sized,
+ I: IntoIterator,
+ I::Item: Into<OsString> + Clone,
+ {
+ Self::from_clap(&Self::clap().get_matches_from(iter))
+ }
+
+ /// Gets the struct from any iterator such as a `Vec` of your making.
+ ///
+ /// Returns a `clap::Error` in case of failure. This does *not* exit in the
+ /// case of `--help` or `--version`, to achieve the same behavior as
+ /// `from_iter()` you must call `.exit()` on the error value.
+ fn from_iter_safe<I>(iter: I) -> Result<Self, clap::Error>
+ where
+ Self: Sized,
+ I: IntoIterator,
+ I::Item: Into<OsString> + Clone,
+ {
+ Ok(Self::from_clap(&Self::clap().get_matches_from_safe(iter)?))
+ }
+}
+
+/// This trait is NOT API. **SUBJECT TO CHANGE WITHOUT NOTICE!**.
+#[doc(hidden)]
+pub trait StructOptInternal: StructOpt {
+ fn augment_clap<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
+ app
+ }
+
+ fn is_subcommand() -> bool {
+ false
+ }
+
+ fn from_subcommand<'a, 'b>(_sub: (&'b str, Option<&'b clap::ArgMatches<'a>>)) -> Option<Self>
+ where
+ Self: std::marker::Sized,
+ {
+ None
+ }
+}
+
+impl<T: StructOpt> StructOpt for Box<T> {
+ fn clap<'a, 'b>() -> clap::App<'a, 'b> {
+ <T as StructOpt>::clap()
+ }
+
+ fn from_clap(matches: &clap::ArgMatches<'_>) -> Self {
+ Box::new(<T as StructOpt>::from_clap(matches))
+ }
+}
+
+impl<T: StructOptInternal> StructOptInternal for Box<T> {
+ #[doc(hidden)]
+ fn is_subcommand() -> bool {
+ <T as StructOptInternal>::is_subcommand()
+ }
+
+ #[doc(hidden)]
+ fn from_subcommand<'a, 'b>(sub: (&'b str, Option<&'b clap::ArgMatches<'a>>)) -> Option<Self> {
+ <T as StructOptInternal>::from_subcommand(sub).map(Box::new)
+ }
+
+ #[doc(hidden)]
+ fn augment_clap<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
+ <T as StructOptInternal>::augment_clap(app)
+ }
+}
diff --git a/structopt/structopt-derive/Cargo.toml b/structopt/structopt-derive/Cargo.toml
new file mode 100644
index 0000000..ad547af
--- /dev/null
+++ b/structopt/structopt-derive/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "structopt-derive"
+version = "0.4.0"
+edition = "2018"
+authors = ["Guillaume Pinot <texitoi@texitoi.eu>"]
+description = "Parse command line argument by defining a struct, derive crate."
+documentation = "https://docs.rs/structopt-derive"
+repository = "https://github.com/TeXitoi/structopt"
+keywords = ["clap", "cli", "derive", "docopt"]
+categories = ["command-line-interface"]
+license = "Apache-2.0/MIT"
+
+[badges]
+travis-ci = { repository = "TeXitoi/structopt" }
+
+[dependencies]
+syn = { version = "1", features = ["full"] }
+quote = "1"
+proc-macro2 = "1"
+heck = "0.3.0"
+proc-macro-error = "0.4.3"
+
+[features]
+paw = []
+
+[lib]
+proc-macro = true
diff --git a/structopt/structopt-derive/LICENSE-APACHE b/structopt/structopt-derive/LICENSE-APACHE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/structopt/structopt-derive/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/structopt/structopt-derive/LICENSE-MIT b/structopt/structopt-derive/LICENSE-MIT
new file mode 100644
index 0000000..e931b83
--- /dev/null
+++ b/structopt/structopt-derive/LICENSE-MIT
@@ -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.
diff --git a/structopt/structopt-derive/src/attrs.rs b/structopt/structopt-derive/src/attrs.rs
new file mode 100644
index 0000000..ce684a2
--- /dev/null
+++ b/structopt/structopt-derive/src/attrs.rs
@@ -0,0 +1,620 @@
+// 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 crate::doc_comments::process_doc_comment;
+use crate::{parse::*, spanned::Sp, ty::Ty};
+
+use std::env;
+
+use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase};
+use proc_macro2::{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};
+
+#[derive(Clone)]
+pub enum Kind {
+ Arg(Sp<Ty>),
+ Subcommand(Sp<Ty>),
+ FlattenStruct,
+ Skip(Option<Expr>),
+}
+
+#[derive(Clone)]
+pub struct Method {
+ name: Ident,
+ args: TokenStream,
+}
+
+#[derive(Clone)]
+pub struct Parser {
+ pub kind: Sp<ParserKind>,
+ pub func: 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(Ident),
+ Assigned(LitStr),
+}
+
+#[derive(Clone)]
+pub struct Attrs {
+ name: Name,
+ casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ doc_comment: Vec<Method>,
+ methods: Vec<Method>,
+ parser: Sp<Parser>,
+ author: Option<Method>,
+ about: Option<Method>,
+ version: Option<Method>,
+ no_version: Option<Ident>,
+ verbatim_doc_comment: Option<Ident>,
+ has_custom_parser: bool,
+ kind: Sp<Kind>,
+}
+
+impl Method {
+ pub fn new(name: Ident, args: TokenStream) -> Self {
+ Method { name, args }
+ }
+
+ fn from_lit_or_env(ident: Ident, lit: Option<LitStr>, env_var: &str) -> Option<Self> {
+ let mut lit = match lit {
+ Some(lit) => lit,
+
+ None => match env::var(env_var) {
+ Ok(val) => 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 = LitStr::new(&edited, lit.span());
+ }
+
+ Some(Method::new(ident, quote!(#lit)))
+ }
+}
+
+impl ToTokens for Method {
+ fn to_tokens(&self, ts: &mut TokenStream) {
+ let Method { ref name, ref args } = self;
+ quote!(.#name(#args)).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: Ident, spec: ParserSpec) -> Sp<Self> {
+ use 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: LitStr) -> Sp<Self> {
+ use 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 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,
+ };
+ LitStr::new(&s, ident.span())
+ }
+ }
+ }
+}
+
+impl Attrs {
+ fn new(
+ default_span: Span,
+ name: Name,
+ casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ Self {
+ name,
+ casing,
+ env_casing,
+ doc_comment: vec![],
+ methods: vec![],
+ parser: Parser::default_spanned(default_span),
+ about: None,
+ author: None,
+ version: None,
+ no_version: None,
+ verbatim_doc_comment: 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>) {
+ if *name == "name" {
+ self.name = Name::Assigned(arg.as_lit());
+ } else {
+ self.methods
+ .push(Method::new(name.as_ident(), quote!(#arg)))
+ }
+ }
+
+ fn push_attrs(&mut self, attrs: &[Attribute]) {
+ use crate::parse::StructOptAttr::*;
+
+ for attr in parse_structopt_attributes(attrs) {
+ match attr {
+ Short(ident) | Long(ident) => {
+ self.push_str_method(
+ ident.into(),
+ self.name.clone().translate(*self.casing).into(),
+ );
+ }
+
+ Env(ident) => {
+ self.push_str_method(
+ ident.into(),
+ self.name.clone().translate(*self.env_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),
+
+ VerbatimDocComment(ident) => self.verbatim_doc_comment = 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);
+ }
+
+ RenameAllEnv(_, casing_lit) => {
+ self.env_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: &[Attribute], name: &str) {
+ use crate::Lit::*;
+ use crate::Meta::*;
+
+ let comment_parts: Vec<_> = attrs
+ .iter()
+ .filter(|attr| attr.path.is_ident("doc"))
+ .filter_map(|attr| {
+ if let Ok(NameValue(MetaNameValue { lit: Str(s), .. })) = attr.parse_meta() {
+ Some(s.value())
+ } else {
+ // non #[doc = "..."] attributes are not our concern
+ // we leave them for rustc to handle
+ None
+ }
+ })
+ .collect();
+
+ self.doc_comment =
+ process_doc_comment(comment_parts, name, self.verbatim_doc_comment.is_none());
+ }
+
+ pub fn from_struct(
+ span: Span,
+ attrs: &[Attribute],
+ name: Name,
+ argument_casing: Sp<CasingStyle>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ let mut res = Self::new(span, name, argument_casing, env_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>,
+ env_casing: Sp<CasingStyle>,
+ ) -> Self {
+ let name = field.ident.clone().unwrap();
+ let mut res = Self::new(
+ field.span(),
+ Name::Derived(name.clone()),
+ struct_casing,
+ env_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/TeXitoi/structopt/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) -> 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;
+ let doc_comment = &self.doc_comment;
+
+ quote!( #(#doc_comment)* #author #version #about #(#methods)* )
+ }
+
+ /// generate methods on top of a field
+ pub fn field_methods(&self) -> TokenStream {
+ let methods = &self.methods;
+ let doc_comment = &self.doc_comment;
+ quote!( #(#doc_comment)* #(#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 env_casing(&self) -> Sp<CasingStyle> {
+ self.env_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.doc_comment.is_empty()
+ || self.methods.iter().any(|m| {
+ m.name == "help"
+ || m.name == "long_help"
+ || m.name == "about"
+ || m.name == "long_about"
+ })
+ }
+}
+
+/// 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
+}
diff --git a/structopt/structopt-derive/src/doc_comments.rs b/structopt/structopt-derive/src/doc_comments.rs
new file mode 100644
index 0000000..06e1b14
--- /dev/null
+++ b/structopt/structopt-derive/src/doc_comments.rs
@@ -0,0 +1,103 @@
+//! The preprocessing we apply to doc comments.
+//!
+//! structopt works in terms of "paragraphs". Paragraph is a sequence of
+//! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines.
+
+use crate::attrs::Method;
+use quote::{format_ident, quote};
+use std::iter;
+
+pub fn process_doc_comment(lines: Vec<String>, name: &str, preprocess: bool) -> Vec<Method> {
+ // multiline comments (`/** ... */`) may have LFs (`\n`) in them,
+ // we need to split so we could handle the lines correctly
+ //
+ // we also need to remove leading and trailing blank lines
+ let mut lines: Vec<&str> = lines
+ .iter()
+ .skip_while(|s| is_blank(s))
+ .flat_map(|s| s.split('\n'))
+ .collect();
+
+ while let Some(true) = lines.last().map(|s| is_blank(s)) {
+ lines.pop();
+ }
+
+ // remove one leading space no matter what
+ for line in lines.iter_mut() {
+ if line.starts_with(' ') {
+ *line = &line[1..];
+ }
+ }
+
+ if lines.is_empty() {
+ return vec![];
+ }
+
+ let short_name = format_ident!("{}", name);
+ let long_name = format_ident!("long_{}", name);
+
+ if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) {
+ let (short, long) = if preprocess {
+ let paragraphs = split_paragraphs(&lines);
+ let short = paragraphs[0].clone();
+ let long = paragraphs.join("\n\n");
+ (remove_period(short), long)
+ } else {
+ let short = lines[..first_blank].join("\n");
+ let long = lines.join("\n");
+ (short, long)
+ };
+
+ vec![
+ Method::new(short_name, quote!(#short)),
+ Method::new(long_name, quote!(#long)),
+ ]
+ } else {
+ let short = if preprocess {
+ let s = merge_lines(&lines);
+ remove_period(s)
+ } else {
+ lines.join("\n")
+ };
+
+ vec![Method::new(short_name, quote!(#short))]
+ }
+}
+
+fn split_paragraphs(lines: &[&str]) -> Vec<String> {
+ let mut last_line = 0;
+ iter::from_fn(|| {
+ let slice = &lines[last_line..];
+ let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0);
+
+ let slice = &slice[start..];
+ let len = slice
+ .iter()
+ .position(|s| is_blank(s))
+ .unwrap_or(slice.len());
+
+ last_line += start + len;
+
+ if len != 0 {
+ Some(merge_lines(&slice[..len]))
+ } else {
+ None
+ }
+ })
+ .collect()
+}
+
+fn remove_period(mut s: String) -> String {
+ if s.ends_with('.') && !s.ends_with("..") {
+ s.pop();
+ }
+ s
+}
+
+fn is_blank(s: &str) -> bool {
+ s.trim().is_empty()
+}
+
+fn merge_lines(lines: &[&str]) -> String {
+ lines.iter().map(|s| s.trim()).collect::<Vec<_>>().join(" ")
+}
diff --git a/structopt/structopt-derive/src/lib.rs b/structopt/structopt-derive/src/lib.rs
new file mode 100644
index 0000000..87eaf1f
--- /dev/null
+++ b/structopt/structopt-derive/src/lib.rs
@@ -0,0 +1,667 @@
+// 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.
+
+//! This crate is custom derive for `StructOpt`. It should not be used
+//! directly. See [structopt documentation](https://docs.rs/structopt)
+//! for the usage of `#[derive(StructOpt)]`.
+
+#![allow(clippy::large_enum_variant)]
+
+extern crate proc_macro;
+
+mod attrs;
+mod doc_comments;
+mod parse;
+mod spanned;
+mod ty;
+
+use crate::{
+ attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
+ spanned::Sp,
+ ty::{sub_type, Ty},
+};
+
+use proc_macro2::{Span, TokenStream};
+use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
+use quote::{quote, quote_spanned};
+use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
+
+/// Default casing style for generated arguments.
+const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
+
+/// Default casing style for environment variables
+const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
+
+/// 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.
+struct GenOutput {
+ tokens: TokenStream,
+ attrs: Attrs,
+}
+
+/// Generates the `StructOpt` impl.
+#[proc_macro_derive(StructOpt, attributes(structopt))]
+#[proc_macro_error]
+pub fn structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input: DeriveInput = syn::parse(input).unwrap();
+ let gen = impl_structopt(&input);
+ gen.into()
+}
+
+/// Generate a block of code to add arguments/subcommands corresponding to
+/// the `fields` to an app.
+fn gen_augmentation(
+ fields: &Punctuated<Field, Comma>,
+ app_var: &Ident,
+ parent_attribute: &Attrs,
+) -> TokenStream {
+ let mut subcmds = fields.iter().filter_map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_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(
+ ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
+ );
+ }
+ };
+
+ let span = field.span();
+ let ts = quote! {
+ let #app_var = <#subcmd_type as ::structopt::StructOptInternal>::augment_clap(
+ #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(),
+ parent_attribute.env_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 as ::structopt::StructOptInternal>::augment_clap(#app_var);
+ let #app_var = if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
+ #app_var.setting(::structopt::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_spanned! { ty.span()=>
+ .takes_value(false)
+ .multiple(false)
+ },
+
+ Ty::Option => quote_spanned! { ty.span()=>
+ .takes_value(true)
+ .multiple(false)
+ #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()=>
+ .takes_value(false)
+ .multiple(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)
+ .multiple(false)
+ .required(#required)
+ #validator
+ }
+ }
+ };
+
+ let name = attrs.cased_name();
+ let methods = attrs.field_methods();
+
+ Some(quote_spanned! { field.span()=>
+ let #app_var = #app_var.arg(
+ ::structopt::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_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
+ let fields = fields.iter().map(|field| {
+ let attrs = Attrs::from_field(
+ field,
+ parent_attribute.casing(),
+ parent_attribute.env_casing(),
+ );
+ let field_name = field.ident.as_ref().unwrap();
+ let kind = attrs.kind();
+ match &*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 as ::structopt::StructOptInternal>::from_subcommand(
+ matches.subcommand())
+ #unwrapper
+ }
+ }
+
+ Kind::FlattenStruct => quote_spanned! { kind.span()=>
+ #field_name: ::structopt::StructOpt::from_clap(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 crate::attrs::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_or_else(Vec::new, |v| v.map(#parse).collect()))
+ } else {
+ None
+ }
+ },
+
+ Ty::Vec => quote_spanned! { ty.span()=>
+ matches.#values_of(#name)
+ .map_or_else(Vec::new, |v| v.map(#parse).collect())
+ },
+
+ 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 ),*
+ }}
+}
+
+fn gen_from_clap(
+ struct_name: &Ident,
+ fields: &Punctuated<Field, Comma>,
+ parent_attribute: &Attrs,
+) -> TokenStream {
+ let field_block = gen_constructor(fields, parent_attribute);
+
+ quote! {
+ fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
+ #struct_name #field_block
+ }
+ }
+}
+
+fn gen_clap(attrs: &[Attribute]) -> GenOutput {
+ let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
+
+ let attrs = Attrs::from_struct(
+ Span::call_site(),
+ attrs,
+ Name::Assigned(LitStr::new(&name, Span::call_site())),
+ Sp::call_site(DEFAULT_CASING),
+ Sp::call_site(DEFAULT_ENV_CASING),
+ );
+ let tokens = {
+ let name = attrs.cased_name();
+ quote!(::structopt::clap::App::new(#name))
+ };
+
+ GenOutput { tokens, attrs }
+}
+
+fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
+ let initial_clap_app_gen = gen_clap(struct_attrs);
+ let clap_tokens = initial_clap_app_gen.tokens;
+
+ let augmented_tokens = quote! {
+ fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
+ let app = #clap_tokens;
+ <Self as ::structopt::StructOptInternal>::augment_clap(app)
+ }
+ };
+
+ GenOutput {
+ tokens: augmented_tokens,
+ attrs: initial_clap_app_gen.attrs,
+ }
+}
+
+fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
+ let app_var = Ident::new("app", Span::call_site());
+ let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
+ quote! {
+ fn augment_clap<'a, 'b>(
+ #app_var: ::structopt::clap::App<'a, 'b>
+ ) -> ::structopt::clap::App<'a, 'b> {
+ #augmentation
+ }
+ }
+}
+
+fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
+ let initial_clap_app_gen = gen_clap(enum_attrs);
+ let clap_tokens = initial_clap_app_gen.tokens;
+
+ let tokens = quote! {
+ fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
+ let app = #clap_tokens
+ .setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp);
+ <Self as ::structopt::StructOptInternal>::augment_clap(app)
+ }
+ };
+
+ GenOutput {
+ tokens,
+ attrs: initial_clap_app_gen.attrs,
+ }
+}
+
+fn gen_augment_clap_enum(
+ variants: &Punctuated<Variant, Comma>,
+ parent_attribute: &Attrs,
+) -> 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(),
+ parent_attribute.env_casing(),
+ );
+ let app_var = Ident::new("subcommand", Span::call_site());
+ let arg_block = match variant.fields {
+ Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
+ Unit => quote!( #app_var ),
+ Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+ let ty = &unnamed[0];
+ quote_spanned! { ty.span()=>
+ {
+ let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(
+ #app_var
+ );
+ if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
+ #app_var.setting(
+ ::structopt::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 = ::structopt::clap::SubCommand::with_name(#name);
+ let #app_var = #arg_block;
+ #app_var#from_attrs
+ })
+ }
+ });
+
+ let app_methods = parent_attribute.top_level_methods();
+
+ quote! {
+ fn augment_clap<'a, 'b>(
+ app: ::structopt::clap::App<'a, 'b>
+ ) -> ::structopt::clap::App<'a, 'b> {
+ app #app_methods #( #subcommands )*
+ }
+ }
+}
+
+fn gen_from_clap_enum(name: &Ident) -> TokenStream {
+ quote! {
+ fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
+ <#name as ::structopt::StructOptInternal>::from_subcommand(matches.subcommand())
+ .unwrap()
+ }
+ }
+}
+
+fn gen_from_subcommand(
+ name: &Ident,
+ variants: &Punctuated<Variant, Comma>,
+ parent_attribute: &Attrs,
+) -> 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(),
+ parent_attribute.env_casing(),
+ );
+ let sub_name = attrs.cased_name();
+ let variant_name = &variant.ident;
+ let constructor_block = match variant.fields {
+ Named(ref fields) => gen_constructor(&fields.named, &attrs),
+ Unit => quote!(),
+ Unnamed(ref fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed[0];
+ quote!( ( <#ty as ::structopt::StructOpt>::from_clap(matches) ) )
+ }
+ Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
+ };
+
+ quote! {
+ (#sub_name, Some(matches)) =>
+ Some(#name :: #variant_name #constructor_block)
+ }
+ });
+
+ quote! {
+ fn from_subcommand<'a, 'b>(
+ sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)
+ ) -> Option<Self> {
+ match sub {
+ #( #match_arms ),*,
+ _ => None
+ }
+ }
+ }
+}
+
+#[cfg(feature = "paw")]
+fn gen_paw_impl(name: &Ident) -> TokenStream {
+ quote! {
+ impl paw::ParseArgs for #name {
+ type Error = std::io::Error;
+
+ fn parse_args() -> std::result::Result<Self, Self::Error> {
+ Ok(<#name as ::structopt::StructOpt>::from_args())
+ }
+ }
+ }
+}
+#[cfg(not(feature = "paw"))]
+fn gen_paw_impl(_: &Ident) -> TokenStream {
+ TokenStream::new()
+}
+
+fn impl_structopt_for_struct(
+ name: &Ident,
+ fields: &Punctuated<Field, Comma>,
+ attrs: &[Attribute],
+) -> TokenStream {
+ let basic_clap_app_gen = gen_clap_struct(attrs);
+ let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
+ let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
+ let paw_impl = gen_paw_impl(name);
+
+ let clap_tokens = basic_clap_app_gen.tokens;
+ quote! {
+ #[allow(unused_variables)]
+ #[allow(unknown_lints)]
+ #[allow(clippy::all)]
+ #[allow(dead_code, unreachable_code)]
+ impl ::structopt::StructOpt for #name {
+ #clap_tokens
+ #from_clap
+ }
+
+ #[allow(unused_variables)]
+ #[allow(unknown_lints)]
+ #[allow(clippy::all)]
+ #[allow(dead_code, unreachable_code)]
+ impl ::structopt::StructOptInternal for #name {
+ #augment_clap
+ fn is_subcommand() -> bool { false }
+ }
+
+ #paw_impl
+ }
+}
+
+fn impl_structopt_for_enum(
+ name: &Ident,
+ variants: &Punctuated<Variant, Comma>,
+ attrs: &[Attribute],
+) -> TokenStream {
+ let basic_clap_app_gen = gen_clap_enum(attrs);
+
+ let augment_clap = gen_augment_clap_enum(variants, &basic_clap_app_gen.attrs);
+ let from_clap = gen_from_clap_enum(name);
+ let from_subcommand = gen_from_subcommand(name, variants, &basic_clap_app_gen.attrs);
+ let paw_impl = gen_paw_impl(name);
+
+ let clap_tokens = basic_clap_app_gen.tokens;
+ quote! {
+ #[allow(unknown_lints)]
+ #[allow(unused_variables, dead_code, unreachable_code)]
+ #[allow(clippy)]
+ impl ::structopt::StructOpt for #name {
+ #clap_tokens
+ #from_clap
+ }
+
+ #[allow(unused_variables)]
+ #[allow(unknown_lints)]
+ #[allow(clippy::all)]
+ #[allow(dead_code, unreachable_code)]
+ impl ::structopt::StructOptInternal for #name {
+ #augment_clap
+ #from_subcommand
+ fn is_subcommand() -> bool { true }
+ }
+
+ #paw_impl
+ }
+}
+
+fn impl_structopt(input: &DeriveInput) -> TokenStream {
+ use syn::Data::*;
+
+ let struct_name = &input.ident;
+
+ set_dummy(quote! {
+ impl ::structopt::StructOpt for #struct_name {
+ fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
+ unimplemented!()
+ }
+ fn from_clap(_matches: &::structopt::clap::ArgMatches) -> Self {
+ unimplemented!()
+ }
+ }
+ });
+
+ match input.data {
+ Struct(DataStruct {
+ fields: syn::Fields::Named(ref fields),
+ ..
+ }) => impl_structopt_for_struct(struct_name, &fields.named, &input.attrs),
+ Enum(ref e) => impl_structopt_for_enum(struct_name, &e.variants, &input.attrs),
+ _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
+ }
+}
diff --git a/structopt/structopt-derive/src/parse.rs b/structopt/structopt-derive/src/parse.rs
new file mode 100644
index 0000000..a704742
--- /dev/null
+++ b/structopt/structopt-derive/src/parse.rs
@@ -0,0 +1,304 @@
+use std::iter::FromIterator;
+
+use proc_macro_error::{abort, ResultExt};
+use quote::ToTokens;
+use syn::{
+ self, parenthesized,
+ parse::{Parse, ParseBuffer, ParseStream},
+ parse2,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ Attribute, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Token,
+};
+
+pub struct StructOptAttributes {
+ pub paren_token: syn::token::Paren,
+ pub attrs: Punctuated<StructOptAttr, Token![,]>,
+}
+
+impl Parse for StructOptAttributes {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let content;
+ let paren_token = parenthesized!(content in input);
+ let attrs = content.parse_terminated(StructOptAttr::parse)?;
+
+ Ok(StructOptAttributes { paren_token, attrs })
+ }
+}
+
+pub enum StructOptAttr {
+ // single-identifier attributes
+ Short(Ident),
+ Long(Ident),
+ Env(Ident),
+ Flatten(Ident),
+ Subcommand(Ident),
+ NoVersion(Ident),
+ VerbatimDocComment(Ident),
+
+ // ident [= "string literal"]
+ About(Ident, Option<LitStr>),
+ Author(Ident, Option<LitStr>),
+
+ // ident = "string literal"
+ Version(Ident, LitStr),
+ RenameAllEnv(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 StructOptAttr {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ use self::StructOptAttr::*;
+
+ 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(),
+ "`#[structopt({} = \"\")]` is deprecated in structopt 0.3, \
+ now it's default behavior",
+ s
+ );
+ }
+ };
+
+ match &*name_str.to_string() {
+ "rename_all" => Ok(RenameAll(name, lit)),
+ "rename_all_env" => Ok(RenameAllEnv(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(),
+ "`#[structopt(raw(...))` attributes are removed in structopt 0.3, \
+ 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)),
+ "env" => Ok(Env(name)),
+ "flatten" => Ok(Flatten(name)),
+ "subcommand" => Ok(Subcommand(name)),
+ "no_version" => Ok(NoVersion(name)),
+ "verbatim_doc_comment" => Ok(VerbatimDocComment(name)),
+
+ "about" => (Ok(About(name, None))),
+ "author" => (Ok(Author(name, None))),
+
+ "skip" => Ok(Skip(name, None)),
+
+ "version" => abort!(
+ name.span(),
+ "#[structopt(version)] is invalid attribute, \
+ structopt 0.3 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,
+ })
+ }
+}
+
+struct CommaSeparated<T>(Punctuated<T, Token![,]>);
+
+impl<T: Parse> Parse for CommaSeparated<T> {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let res = Punctuated::parse_separated_nonempty(input)?;
+ Ok(CommaSeparated(res))
+ }
+}
+
+fn raw_method_suggestion(ts: ParseBuffer) -> String {
+ let do_parse = move || -> Result<(Ident, CommaSeparated<Expr>), syn::Error> {
+ let name = ts.parse()?;
+ let _eq: Token![=] = ts.parse()?;
+ let val: LitStr = ts.parse()?;
+ Ok((name, syn::parse_str(&val.value())?))
+ };
+
+ fn to_string<T: ToTokens>(val: &T) -> String {
+ val.to_token_stream()
+ .to_string()
+ .replace(" ", "")
+ .replace(",", ", ")
+ }
+
+ if let Ok((name, val)) = do_parse() {
+ let exprs = val.0;
+ let suggestion = if exprs.len() == 1 {
+ let val = to_string(&exprs[0]);
+ format!(" = {}", val)
+ } else {
+ let val = exprs
+ .into_iter()
+ .map(|expr| to_string(&expr))
+ .collect::<Vec<_>>()
+ .join(", ");
+
+ format!("({})", val)
+ };
+
+ format!(
+ "if you need to call `clap::Arg/App::{}` method you \
+ can do it like this: #[structopt({}{})]",
+ name, name, suggestion
+ )
+ } 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_structopt_attributes(all_attrs: &[Attribute]) -> Vec<StructOptAttr> {
+ all_attrs
+ .iter()
+ .filter(|attr| attr.path.is_ident("structopt"))
+ .flat_map(|attr| {
+ let attrs: StructOptAttributes = 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 `structopt`";
+ syn::Error::new(span, patch_msg)
+ }
+ _ => e,
+ })
+ .unwrap_or_abort();
+ attrs.attrs
+ })
+ .collect()
+}
diff --git a/structopt/structopt-derive/src/spanned.rs b/structopt/structopt-derive/src/spanned.rs
new file mode 100644
index 0000000..2dd595b
--- /dev/null
+++ b/structopt/structopt-derive/src/spanned.rs
@@ -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<U, T: PartialEq<U>> PartialEq<U> for Sp<T> {
+ fn eq(&self, other: &U) -> bool {
+ self.val == *other
+ }
+}
+
+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);
+ }
+}
diff --git a/structopt/structopt-derive/src/ty.rs b/structopt/structopt-derive/src/ty.rs
new file mode 100644
index 0000000..06eb3ec
--- /dev/null
+++ b/structopt/structopt-derive/src/ty.rs
@@ -0,0 +1,108 @@
+//! Special types handling
+
+use crate::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 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())
+}
diff --git a/structopt/tests/argument_naming.rs b/structopt/tests/argument_naming.rs
new file mode 100644
index 0000000..e7fe3d5
--- /dev/null
+++ b/structopt/tests/argument_naming.rs
@@ -0,0 +1,311 @@
+use structopt::StructOpt;
+
+#[test]
+fn test_single_word_enum_variant_is_default_renamed_into_kebab_case() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ enum Opt {
+ Command { foo: u32 },
+ }
+
+ assert_eq!(
+ Opt::Command { foo: 0 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "command", "0"]))
+ );
+}
+
+#[test]
+fn test_multi_word_enum_variant_is_renamed() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ enum Opt {
+ FirstCommand { foo: u32 },
+ }
+
+ assert_eq!(
+ Opt::FirstCommand { foo: 0 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "first-command", "0"]))
+ );
+}
+
+#[test]
+fn test_standalone_long_generates_kebab_case() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[allow(non_snake_case)]
+ struct Opt {
+ #[structopt(long)]
+ FOO_OPTION: bool,
+ }
+
+ assert_eq!(
+ Opt { FOO_OPTION: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo-option"]))
+ );
+}
+
+#[test]
+fn test_custom_long_overwrites_default_name() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(long = "foo")]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo"]))
+ );
+}
+
+#[test]
+fn test_standalone_long_uses_previous_defined_custom_name() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(name = "foo", long)]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo"]))
+ );
+}
+
+#[test]
+fn test_standalone_long_ignores_afterwards_defined_custom_name() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(long, name = "foo")]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo-option"]))
+ );
+}
+
+#[test]
+fn test_standalone_short_generates_kebab_case() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[allow(non_snake_case)]
+ struct Opt {
+ #[structopt(short)]
+ FOO_OPTION: bool,
+ }
+
+ assert_eq!(
+ Opt { FOO_OPTION: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-f"]))
+ );
+}
+
+#[test]
+fn test_custom_short_overwrites_default_name() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(short = "o")]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-o"]))
+ );
+}
+
+#[test]
+fn test_standalone_short_uses_previous_defined_custom_name() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(name = "option", short)]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-o"]))
+ );
+}
+
+#[test]
+fn test_standalone_short_ignores_afterwards_defined_custom_name() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(short, name = "option")]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-f"]))
+ );
+}
+
+#[test]
+fn test_standalone_long_uses_previous_defined_casing() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(rename_all = "screaming_snake", long)]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--FOO_OPTION"]))
+ );
+}
+
+#[test]
+fn test_standalone_short_uses_previous_defined_casing() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(rename_all = "screaming_snake", short)]
+ foo_option: bool,
+ }
+
+ assert_eq!(
+ Opt { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-F"]))
+ );
+}
+
+#[test]
+fn test_standalone_long_works_with_verbatim_casing() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[allow(non_snake_case)]
+ struct Opt {
+ #[structopt(rename_all = "verbatim", long)]
+ _fOO_oPtiON: bool,
+ }
+
+ assert_eq!(
+ Opt { _fOO_oPtiON: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--_fOO_oPtiON"]))
+ );
+}
+
+#[test]
+fn test_standalone_short_works_with_verbatim_casing() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(rename_all = "verbatim", short)]
+ _foo: bool,
+ }
+
+ assert_eq!(
+ Opt { _foo: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-_"]))
+ );
+}
+
+#[test]
+fn test_rename_all_is_propagated_from_struct_to_fields() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[structopt(rename_all = "screaming_snake")]
+ struct Opt {
+ #[structopt(long)]
+ foo: bool,
+ }
+
+ assert_eq!(
+ Opt { foo: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--FOO"]))
+ );
+}
+
+#[test]
+fn test_rename_all_is_not_propagated_from_struct_into_flattened() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[structopt(rename_all = "screaming_snake")]
+ struct Opt {
+ #[structopt(flatten)]
+ foo: Foo,
+ }
+
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Foo {
+ #[structopt(long)]
+ foo: bool,
+ }
+
+ assert_eq!(
+ Opt {
+ foo: Foo { foo: true }
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo"]))
+ );
+}
+
+#[test]
+fn test_rename_all_is_not_propagated_from_struct_into_subcommand() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[structopt(rename_all = "screaming_snake")]
+ struct Opt {
+ #[structopt(subcommand)]
+ foo: Foo,
+ }
+
+ #[derive(StructOpt, Debug, PartialEq)]
+ enum Foo {
+ Command {
+ #[structopt(long)]
+ foo: bool,
+ },
+ }
+
+ assert_eq!(
+ Opt {
+ foo: Foo::Command { foo: true }
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "command", "--foo"]))
+ );
+}
+
+#[test]
+fn test_rename_all_is_propagated_from_enum_to_variants_and_their_fields() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[structopt(rename_all = "screaming_snake")]
+ enum Opt {
+ FirstVariant,
+ SecondVariant {
+ #[structopt(long)]
+ foo: bool,
+ },
+ }
+
+ assert_eq!(
+ Opt::FirstVariant,
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "FIRST_VARIANT"]))
+ );
+
+ assert_eq!(
+ Opt::SecondVariant { foo: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "SECOND_VARIANT", "--FOO"]))
+ );
+}
+
+#[test]
+fn test_rename_all_is_propagation_can_be_overridden() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[structopt(rename_all = "screaming_snake")]
+ enum Opt {
+ #[structopt(rename_all = "kebab_case")]
+ FirstVariant {
+ #[structopt(long)]
+ foo_option: bool,
+ },
+ SecondVariant {
+ #[structopt(rename_all = "kebab_case", long)]
+ foo_option: bool,
+ },
+ }
+
+ assert_eq!(
+ Opt::FirstVariant { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "first-variant", "--foo-option"]))
+ );
+
+ assert_eq!(
+ Opt::SecondVariant { foo_option: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "SECOND_VARIANT", "--foo-option"]))
+ );
+}
diff --git a/structopt/tests/arguments.rs b/structopt/tests/arguments.rs
new file mode 100644
index 0000000..96a0938
--- /dev/null
+++ b/structopt/tests/arguments.rs
@@ -0,0 +1,86 @@
+// 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 structopt::clap;
+use structopt::StructOpt;
+
+#[test]
+fn required_argument() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ arg: i32,
+ }
+ assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test", "42"]));
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "42", "24"])
+ .is_err());
+}
+
+#[test]
+fn optional_argument() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ arg: Option<i32>,
+ }
+ assert_eq!(Opt { arg: Some(42) }, Opt::from_iter(&["test", "42"]));
+ assert_eq!(Opt { arg: None }, Opt::from_iter(&["test"]));
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "42", "24"])
+ .is_err());
+}
+
+#[test]
+fn argument_with_default() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(default_value = "42")]
+ arg: i32,
+ }
+ assert_eq!(Opt { arg: 24 }, Opt::from_iter(&["test", "24"]));
+ assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test"]));
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "42", "24"])
+ .is_err());
+}
+
+#[test]
+fn arguments() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ arg: Vec<i32>,
+ }
+ assert_eq!(Opt { arg: vec![24] }, Opt::from_iter(&["test", "24"]));
+ assert_eq!(Opt { arg: vec![] }, Opt::from_iter(&["test"]));
+ assert_eq!(
+ Opt { arg: vec![24, 42] },
+ Opt::from_iter(&["test", "24", "42"])
+ );
+}
+
+#[test]
+fn arguments_safe() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ arg: Vec<i32>,
+ }
+ assert_eq!(
+ Opt { arg: vec![24] },
+ Opt::from_iter_safe(&["test", "24"]).unwrap()
+ );
+ assert_eq!(Opt { arg: vec![] }, Opt::from_iter_safe(&["test"]).unwrap());
+ assert_eq!(
+ Opt { arg: vec![24, 42] },
+ Opt::from_iter_safe(&["test", "24", "42"]).unwrap()
+ );
+
+ assert_eq!(
+ clap::ErrorKind::ValueValidation,
+ Opt::from_iter_safe(&["test", "NOPE"]).err().unwrap().kind
+ );
+}
diff --git a/structopt/tests/author_version_about.rs b/structopt/tests/author_version_about.rs
new file mode 100644
index 0000000..0c4a4fb
--- /dev/null
+++ b/structopt/tests/author_version_about.rs
@@ -0,0 +1,46 @@
+// 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.
+
+mod utils;
+
+use structopt::StructOpt;
+use utils::*;
+
+#[test]
+fn no_author_version_about() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ #[structopt(name = "foo", no_version)]
+ struct Opt {}
+
+ let output = get_long_help::<Opt>();
+ assert!(output.starts_with("foo \n\nUSAGE:"));
+}
+
+#[test]
+fn use_env() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ #[structopt(author, about)]
+ struct Opt {}
+
+ let output = get_long_help::<Opt>();
+ assert!(output.starts_with("structopt 0."));
+ assert!(output.contains("Guillaume Pinot <texitoi@texitoi.eu>, others"));
+ assert!(output.contains("Parse command line argument by defining a struct."));
+}
+
+#[test]
+fn explicit_version_not_str() {
+ const VERSION: &str = "custom version";
+
+ #[derive(StructOpt)]
+ #[structopt(version = VERSION)]
+ pub struct Opt {}
+
+ let output = get_long_help::<Opt>();
+ assert!(output.contains("custom version"));
+}
diff --git a/structopt/tests/custom-string-parsers.rs b/structopt/tests/custom-string-parsers.rs
new file mode 100644
index 0000000..89070ed
--- /dev/null
+++ b/structopt/tests/custom-string-parsers.rs
@@ -0,0 +1,306 @@
+// 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 structopt::StructOpt;
+
+use std::ffi::{CString, OsStr, OsString};
+use std::num::ParseIntError;
+use std::path::PathBuf;
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct PathOpt {
+ #[structopt(short, long, parse(from_os_str))]
+ path: PathBuf,
+
+ #[structopt(short, default_value = "../", parse(from_os_str))]
+ default_path: PathBuf,
+
+ #[structopt(short, parse(from_os_str))]
+ vector_path: Vec<PathBuf>,
+
+ #[structopt(short, parse(from_os_str))]
+ option_path_1: Option<PathBuf>,
+
+ #[structopt(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::from_clap(&PathOpt::clap().get_matches_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(StructOpt, PartialEq, Debug)]
+struct HexOpt {
+ #[structopt(short, parse(try_from_str = parse_hex))]
+ number: u64,
+}
+
+#[test]
+#[allow(clippy::unreadable_literal)]
+fn test_parse_hex() {
+ assert_eq!(
+ HexOpt { number: 5 },
+ HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "5"]))
+ );
+ assert_eq!(
+ HexOpt { number: 0xabcdef },
+ HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"]))
+ );
+
+ let err = HexOpt::clap()
+ .get_matches_from_safe(&["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, OsString> {
+ Ok("D")
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct NoOpOpt {
+ #[structopt(short, parse(from_str = custom_parser_1))]
+ a: &'static str,
+ #[structopt(short, parse(try_from_str = custom_parser_2))]
+ b: &'static str,
+ #[structopt(short, parse(from_os_str = custom_parser_3))]
+ c: &'static str,
+ #[structopt(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::from_clap(
+ &NoOpOpt::clap().get_matches_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"])
+ )
+ );
+}
+
+// Note: can't use `Vec<u8>` directly, as structopt would instead look for
+// conversion function from `&str` to `u8`.
+type Bytes = Vec<u8>;
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct DefaultedOpt {
+ #[structopt(short, parse(from_str))]
+ bytes: Bytes,
+
+ #[structopt(short, parse(try_from_str))]
+ integer: u64,
+
+ #[structopt(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::from_clap(&DefaultedOpt::clap().get_matches_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(StructOpt, PartialEq, Debug)]
+struct Occurrences {
+ #[structopt(short, long, parse(from_occurrences))]
+ signed: i32,
+
+ #[structopt(short, parse(from_occurrences))]
+ little_signed: i8,
+
+ #[structopt(short, parse(from_occurrences))]
+ unsigned: usize,
+
+ #[structopt(short = "r", parse(from_occurrences))]
+ little_unsigned: u8,
+
+ #[structopt(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::from_clap(&Occurrences::clap().get_matches_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(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, parse(try_from_str = parse_bool))]
+ debug: bool,
+ #[structopt(
+ short,
+ default_value = "false",
+ parse(try_from_str = parse_bool)
+ )]
+ verbose: bool,
+ #[structopt(short, parse(try_from_str = parse_bool))]
+ tribool: Option<bool>,
+ #[structopt(short, parse(try_from_str = parse_bool))]
+ bitset: Vec<bool>,
+ }
+
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert!(Opt::clap().get_matches_from_safe(&["test", "-d"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-dfoo"])
+ .is_err());
+ assert_eq!(
+ Opt {
+ debug: false,
+ verbose: false,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dfalse"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-vfalse"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: true,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-vtrue"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: Some(false),
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-tfalse"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: Some(true),
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-ttrue"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: None,
+ bitset: vec![false, true, false, false],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"])
+ );
+}
+
+#[test]
+fn test_cstring() {
+ #[derive(StructOpt)]
+ struct Opt {
+ #[structopt(parse(try_from_str = CString::new))]
+ c_string: CString,
+ }
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert_eq!(Opt::from_iter(&["test", "bla"]).c_string.to_bytes(), b"bla");
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "bla\0bla"])
+ .is_err());
+}
diff --git a/structopt/tests/deny-warnings.rs b/structopt/tests/deny-warnings.rs
new file mode 100644
index 0000000..721204a
--- /dev/null
+++ b/structopt/tests/deny-warnings.rs
@@ -0,0 +1,47 @@
+// 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.
+
+#![deny(warnings)]
+
+use structopt::StructOpt;
+
+fn try_str(s: &str) -> Result<String, std::convert::Infallible> {
+ Ok(s.into())
+}
+
+#[test]
+fn warning_never_struct() {
+ #[derive(Debug, PartialEq, StructOpt)]
+ struct Opt {
+ #[structopt(parse(try_from_str = try_str))]
+ s: String,
+ }
+ assert_eq!(
+ Opt {
+ s: "foo".to_string()
+ },
+ Opt::from_iter(&["test", "foo"])
+ );
+}
+
+#[test]
+fn warning_never_enum() {
+ #[derive(Debug, PartialEq, StructOpt)]
+ enum Opt {
+ Foo {
+ #[structopt(parse(try_from_str = try_str))]
+ s: String,
+ },
+ }
+ assert_eq!(
+ Opt::Foo {
+ s: "foo".to_string()
+ },
+ Opt::from_iter(&["test", "foo", "foo"])
+ );
+}
diff --git a/structopt/tests/doc-comments-help.rs b/structopt/tests/doc-comments-help.rs
new file mode 100644
index 0000000..27649b8
--- /dev/null
+++ b/structopt/tests/doc-comments-help.rs
@@ -0,0 +1,162 @@
+// 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.
+
+mod utils;
+
+use structopt::StructOpt;
+use utils::*;
+
+#[test]
+fn doc_comments() {
+ /// Lorem ipsum
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct LoremIpsum {
+ /// Fooify a bar
+ /// and a baz
+ #[structopt(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(StructOpt, PartialEq, Debug)]
+ #[structopt(name = "lorem-ipsum", about = "Dolor sit amet")]
+ struct LoremIpsum {
+ /// Fooify a bar
+ #[structopt(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(StructOpt, PartialEq, Debug)]
+ #[structopt(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(StructOpt, PartialEq, Debug)]
+ #[structopt(name = "lorem-ipsum", about = "Dolor sit amet")]
+ struct LoremIpsum {
+ /// Dot is removed from multiline comments.
+ ///
+ /// Long help
+ #[structopt(long)]
+ foo: bool,
+
+ /// Dot is removed from one short comment.
+ #[structopt(long)]
+ bar: bool,
+ }
+
+ let short_help = get_help::<LoremIpsum>();
+ let long_help = get_long_help::<LoremIpsum>();
+
+ assert!(short_help.contains("Dot is removed from one short comment"));
+ assert!(!short_help.contains("Dot is removed from one short comment."));
+ assert!(short_help.contains("Dot is removed from multiline comments"));
+ assert!(!short_help.contains("Dot is removed from multiline comments."));
+ assert!(long_help.contains("Long help"));
+ assert!(!short_help.contains("Long help"));
+}
+
+#[test]
+fn top_long_doc_comment_both_help_long_help() {
+ /// Lorem ipsumclap
+ #[derive(StructOpt, Debug)]
+ #[structopt(name = "lorem-ipsum", about = "Dolor sit amet")]
+ struct LoremIpsum {
+ #[structopt(subcommand)]
+ foo: SubCommand,
+ }
+
+ #[derive(StructOpt, Debug)]
+ pub enum SubCommand {
+ /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
+ ///
+ /// Or something else
+ Foo {
+ #[structopt(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"));
+}
+
+#[test]
+fn verbatim_doc_comment() {
+ /// DANCE!
+ ///
+ /// ()
+ /// |
+ /// ( () )
+ /// ) ________ // )
+ /// () |\ \ //
+ /// ( \\__ \ ______\//
+ /// \__) | |
+ /// | | |
+ /// \ | |
+ /// \|_______|
+ /// // \\
+ /// (( ||
+ /// \\ ||
+ /// ( () ||
+ /// ( () ) )
+ #[derive(StructOpt, Debug)]
+ #[structopt(verbatim_doc_comment)]
+ struct SeeFigure1 {
+ #[structopt(long)]
+ foo: bool,
+ }
+
+ let help = get_long_help::<SeeFigure1>();
+ let sample = r#"
+ ()
+ |
+ ( () )
+ ) ________ // )
+ () |\ \ //
+( \\__ \ ______\//
+ \__) | |
+ | | |
+ \ | |
+ \|_______|
+ // \\
+ (( ||
+ \\ ||
+ ( () ||
+ ( () ) )"#;
+
+ assert!(help.contains(sample))
+}
diff --git a/structopt/tests/explicit_name_no_renaming.rs b/structopt/tests/explicit_name_no_renaming.rs
new file mode 100644
index 0000000..eff7a86
--- /dev/null
+++ b/structopt/tests/explicit_name_no_renaming.rs
@@ -0,0 +1,32 @@
+mod utils;
+
+use structopt::StructOpt;
+use utils::*;
+
+#[test]
+fn explicit_short_long_no_rename() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short = ".", long = ".foo")]
+ foo: Vec<String>,
+ }
+
+ assert_eq!(
+ Opt {
+ foo: vec!["short".into(), "long".into()]
+ },
+ Opt::from_iter(&["test", "-.", "short", "--.foo", "long"])
+ );
+}
+
+#[test]
+fn explicit_name_no_rename() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(name = ".options")]
+ foo: Vec<String>,
+ }
+
+ let help = get_long_help::<Opt>();
+ assert!(help.contains("[.options]..."))
+}
diff --git a/structopt/tests/flags.rs b/structopt/tests/flags.rs
new file mode 100644
index 0000000..39a5dc3
--- /dev/null
+++ b/structopt/tests/flags.rs
@@ -0,0 +1,162 @@
+// 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 structopt::StructOpt;
+
+#[test]
+fn unique_flag() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, long)]
+ alice: bool,
+ }
+
+ assert_eq!(
+ Opt { alice: false },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert_eq!(
+ Opt { alice: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
+ );
+ assert_eq!(
+ Opt { alice: true },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice"]))
+ );
+ assert!(Opt::clap().get_matches_from_safe(&["test", "-i"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a", "foo"])
+ .is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a", "-a"])
+ .is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a", "--alice"])
+ .is_err());
+}
+
+#[test]
+fn multiple_flag() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, long, parse(from_occurrences))]
+ alice: u64,
+ #[structopt(short, long, parse(from_occurrences))]
+ bob: u8,
+ }
+
+ assert_eq!(
+ Opt { alice: 0, bob: 0 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert_eq!(
+ Opt { alice: 1, bob: 0 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
+ );
+ assert_eq!(
+ Opt { alice: 2, bob: 0 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-a"]))
+ );
+ assert_eq!(
+ Opt { alice: 2, bob: 2 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--alice", "-bb"]))
+ );
+ assert_eq!(
+ Opt { alice: 3, bob: 1 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-aaa", "--bob"]))
+ );
+ assert!(Opt::clap().get_matches_from_safe(&["test", "-i"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["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(StructOpt, Debug)]
+ struct Opt {
+ #[structopt(short, long, parse(from_flag = parse_from_flag))]
+ alice: std::sync::atomic::AtomicBool,
+ #[structopt(short, long, parse(from_flag))]
+ bob: std::sync::atomic::AtomicBool,
+ }
+
+ let falsey = Opt::from_clap(&Opt::clap().get_matches_from(&["test"]));
+ assert!(!falsey.alice.load(std::sync::atomic::Ordering::Relaxed));
+ assert!(!falsey.bob.load(std::sync::atomic::Ordering::Relaxed));
+
+ let alice = Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]));
+ assert!(alice.alice.load(std::sync::atomic::Ordering::Relaxed));
+ assert!(!alice.bob.load(std::sync::atomic::Ordering::Relaxed));
+
+ let bob = Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b"]));
+ assert!(!bob.alice.load(std::sync::atomic::Ordering::Relaxed));
+ assert!(bob.bob.load(std::sync::atomic::Ordering::Relaxed));
+
+ let both = Opt::from_clap(&Opt::clap().get_matches_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(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, long)]
+ alice: bool,
+ #[structopt(short, long, parse(from_occurrences))]
+ bob: u64,
+ }
+
+ assert_eq!(
+ Opt {
+ alice: false,
+ bob: 0
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert_eq!(
+ Opt {
+ alice: true,
+ bob: 0
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
+ );
+ assert_eq!(
+ Opt {
+ alice: true,
+ bob: 0
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
+ );
+ assert_eq!(
+ Opt {
+ alice: false,
+ bob: 1
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b"]))
+ );
+ assert_eq!(
+ Opt {
+ alice: true,
+ bob: 1
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice", "--bob"]))
+ );
+ assert_eq!(
+ Opt {
+ alice: true,
+ bob: 4
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-bb", "-a", "-bb"]))
+ );
+}
diff --git a/structopt/tests/flatten.rs b/structopt/tests/flatten.rs
new file mode 100644
index 0000000..4983d86
--- /dev/null
+++ b/structopt/tests/flatten.rs
@@ -0,0 +1,95 @@
+// 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 structopt::StructOpt;
+
+#[test]
+fn flatten() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Common {
+ arg: i32,
+ }
+
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(flatten)]
+ common: Common,
+ }
+ assert_eq!(
+ Opt {
+ common: Common { arg: 42 }
+ },
+ Opt::from_iter(&["test", "42"])
+ );
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "42", "24"])
+ .is_err());
+}
+
+#[test]
+#[should_panic]
+fn flatten_twice() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Common {
+ arg: i32,
+ }
+
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(flatten)]
+ c1: Common,
+ // Defines "arg" twice, so this should not work.
+ #[structopt(flatten)]
+ c2: Common,
+ }
+ Opt::from_iter(&["test", "42", "43"]);
+}
+
+#[test]
+fn flatten_in_subcommand() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Common {
+ arg: i32,
+ }
+
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Add {
+ #[structopt(short)]
+ interactive: bool,
+ #[structopt(flatten)]
+ common: Common,
+ }
+
+ #[derive(StructOpt, PartialEq, Debug)]
+ enum Opt {
+ Fetch {
+ #[structopt(short)]
+ all: bool,
+ #[structopt(flatten)]
+ common: Common,
+ },
+
+ Add(Add),
+ }
+
+ assert_eq!(
+ Opt::Fetch {
+ all: false,
+ common: Common { arg: 42 }
+ },
+ Opt::from_iter(&["test", "fetch", "42"])
+ );
+ assert_eq!(
+ Opt::Add(Add {
+ interactive: true,
+ common: Common { arg: 43 }
+ }),
+ Opt::from_iter(&["test", "add", "-i", "43"])
+ );
+}
diff --git a/structopt/tests/issues.rs b/structopt/tests/issues.rs
new file mode 100644
index 0000000..4d250ae
--- /dev/null
+++ b/structopt/tests/issues.rs
@@ -0,0 +1,67 @@
+// https://github.com/TeXitoi/structopt/issues/151
+// https://github.com/TeXitoi/structopt/issues/289
+
+#[test]
+fn issue_151() {
+ use structopt::{clap::ArgGroup, StructOpt};
+
+ #[derive(StructOpt, Debug)]
+ #[structopt(group = ArgGroup::with_name("verb").required(true).multiple(true))]
+ struct Opt {
+ #[structopt(long, group = "verb")]
+ foo: bool,
+ #[structopt(long, group = "verb")]
+ bar: bool,
+ }
+
+ #[derive(Debug, StructOpt)]
+ struct Cli {
+ #[structopt(flatten)]
+ a: Opt,
+ }
+
+ assert!(Cli::clap().get_matches_from_safe(&["test"]).is_err());
+ assert!(Cli::clap()
+ .get_matches_from_safe(&["test", "--foo"])
+ .is_ok());
+ assert!(Cli::clap()
+ .get_matches_from_safe(&["test", "--bar"])
+ .is_ok());
+ assert!(Cli::clap()
+ .get_matches_from_safe(&["test", "--zebra"])
+ .is_err());
+ assert!(Cli::clap()
+ .get_matches_from_safe(&["test", "--foo", "--bar"])
+ .is_ok());
+}
+
+#[test]
+fn issue_289() {
+ use structopt::{clap::AppSettings, StructOpt};
+
+ #[derive(StructOpt)]
+ #[structopt(setting = AppSettings::InferSubcommands)]
+ enum Args {
+ SomeCommand(SubSubCommand),
+ AnotherCommand,
+ }
+
+ #[derive(StructOpt)]
+ #[structopt(setting = AppSettings::InferSubcommands)]
+ enum SubSubCommand {
+ TestCommand,
+ }
+
+ assert!(Args::clap()
+ .get_matches_from_safe(&["test", "some-command", "test-command"])
+ .is_ok());
+ assert!(Args::clap()
+ .get_matches_from_safe(&["test", "some", "test-command"])
+ .is_ok());
+ assert!(Args::clap()
+ .get_matches_from_safe(&["test", "some-command", "test"])
+ .is_ok());
+ assert!(Args::clap()
+ .get_matches_from_safe(&["test", "some", "test"])
+ .is_ok());
+}
diff --git a/structopt/tests/macro-errors.rs b/structopt/tests/macro-errors.rs
new file mode 100644
index 0000000..ae4f5a2
--- /dev/null
+++ b/structopt/tests/macro-errors.rs
@@ -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");
+}
diff --git a/structopt/tests/nested-subcommands.rs b/structopt/tests/nested-subcommands.rs
new file mode 100644
index 0000000..1fbd166
--- /dev/null
+++ b/structopt/tests/nested-subcommands.rs
@@ -0,0 +1,193 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct Opt {
+ #[structopt(short, long)]
+ force: bool,
+ #[structopt(short, long, parse(from_occurrences))]
+ verbose: u64,
+ #[structopt(subcommand)]
+ cmd: Sub,
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+enum Sub {
+ Fetch {},
+ Add {},
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct Opt2 {
+ #[structopt(short, long)]
+ force: bool,
+ #[structopt(short, long, parse(from_occurrences))]
+ verbose: u64,
+ #[structopt(subcommand)]
+ cmd: Option<Sub>,
+}
+
+#[test]
+fn test_no_cmd() {
+ let result = Opt::clap().get_matches_from_safe(&["test"]);
+ assert!(result.is_err());
+
+ assert_eq!(
+ Opt2 {
+ force: false,
+ verbose: 0,
+ cmd: None
+ },
+ Opt2::from_clap(&Opt2::clap().get_matches_from(&["test"]))
+ );
+}
+
+#[test]
+fn test_fetch() {
+ assert_eq!(
+ Opt {
+ force: false,
+ verbose: 3,
+ cmd: Sub::Fetch {}
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vvv", "fetch"]))
+ );
+ assert_eq!(
+ Opt {
+ force: true,
+ verbose: 0,
+ cmd: Sub::Fetch {}
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--force", "fetch"]))
+ );
+}
+
+#[test]
+fn test_add() {
+ assert_eq!(
+ Opt {
+ force: false,
+ verbose: 0,
+ cmd: Sub::Add {}
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"]))
+ );
+ assert_eq!(
+ Opt {
+ force: false,
+ verbose: 2,
+ cmd: Sub::Add {}
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vv", "add"]))
+ );
+}
+
+#[test]
+fn test_badinput() {
+ let result = Opt::clap().get_matches_from_safe(&["test", "badcmd"]);
+ assert!(result.is_err());
+ let result = Opt::clap().get_matches_from_safe(&["test", "add", "--verbose"]);
+ assert!(result.is_err());
+ let result = Opt::clap().get_matches_from_safe(&["test", "--badopt", "add"]);
+ assert!(result.is_err());
+ let result = Opt::clap().get_matches_from_safe(&["test", "add", "--badopt"]);
+ assert!(result.is_err());
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct Opt3 {
+ #[structopt(short, long)]
+ all: bool,
+ #[structopt(subcommand)]
+ cmd: Sub2,
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+enum Sub2 {
+ Foo {
+ file: String,
+ #[structopt(subcommand)]
+ cmd: Sub3,
+ },
+ Bar {},
+}
+
+#[derive(StructOpt, 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::from_clap(
+ &Opt3::clap().get_matches_from(&["test", "--all", "foo", "lib.rs", "quux"])
+ )
+ );
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+enum SubSubCmdWithOption {
+ Remote {
+ #[structopt(subcommand)]
+ cmd: Option<Remote>,
+ },
+ Stash {
+ #[structopt(subcommand)]
+ cmd: Stash,
+ },
+}
+#[derive(StructOpt, PartialEq, Debug)]
+enum Remote {
+ Add { name: String, url: String },
+ Remove { name: String },
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+enum Stash {
+ Save,
+ Pop,
+}
+
+#[test]
+fn sub_sub_cmd_with_option() {
+ fn make(args: &[&str]) -> Option<SubSubCmdWithOption> {
+ SubSubCmdWithOption::clap()
+ .get_matches_from_safe(args)
+ .ok()
+ .map(|m| SubSubCmdWithOption::from_clap(&m))
+ }
+ 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"]));
+}
diff --git a/structopt/tests/non_literal_attributes.rs b/structopt/tests/non_literal_attributes.rs
new file mode 100644
index 0000000..75b6b71
--- /dev/null
+++ b/structopt/tests/non_literal_attributes.rs
@@ -0,0 +1,147 @@
+// 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 structopt::clap::AppSettings;
+use structopt::StructOpt;
+
+pub const DISPLAY_ORDER: usize = 2;
+
+// Check if the global settings compile
+#[derive(StructOpt, Debug, PartialEq, Eq)]
+#[structopt(global_settings = &[AppSettings::ColoredHelp])]
+struct Opt {
+ #[structopt(
+ long = "x",
+ display_order = DISPLAY_ORDER,
+ next_line_help = true,
+ default_value = "0",
+ require_equals = true
+ )]
+ x: i32,
+
+ #[structopt(short = "l", long = "level", aliases = &["set-level", "lvl"])]
+ level: String,
+
+ #[structopt(long("values"))]
+ values: Vec<i32>,
+
+ #[structopt(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::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1"]))
+ );
+ assert_eq!(
+ Opt {
+ x: 0,
+ level: "1".to_string(),
+ files: Vec::new(),
+ values: vec![],
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--level", "1"]))
+ );
+ assert_eq!(
+ Opt {
+ x: 0,
+ level: "1".to_string(),
+ files: Vec::new(),
+ values: vec![],
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--set-level", "1"]))
+ );
+ assert_eq!(
+ Opt {
+ x: 0,
+ level: "1".to_string(),
+ files: Vec::new(),
+ values: vec![],
+ },
+ Opt::from_clap(&Opt::clap().get_matches_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::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "file"]))
+ );
+ assert_eq!(
+ Opt {
+ x: 0,
+ level: "1".to_string(),
+ files: vec!["FILE".to_string()],
+ values: vec![1],
+ },
+ Opt::from_clap(
+ &Opt::clap().get_matches_from(&["test", "-l", "1", "--values", "1", "--", "FILE"]),
+ )
+ );
+}
+
+#[test]
+fn test_multi_args_fail() {
+ let result = Opt::clap().get_matches_from_safe(&["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::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "--x=1"]))
+ );
+ let result = Opt::clap().get_matches_from_safe(&["test", "-l", "1", "--x", "1"]);
+ assert!(result.is_err());
+}
+
+fn parse_hex(input: &str) -> Result<u64, std::num::ParseIntError> {
+ u64::from_str_radix(input, 16)
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct HexOpt {
+ #[structopt(short = "n", parse(try_from_str = parse_hex))]
+ number: u64,
+}
+
+#[test]
+fn test_parse_hex_function_path() {
+ assert_eq!(
+ HexOpt { number: 5 },
+ HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "5"]))
+ );
+ assert_eq!(
+ HexOpt { number: 0xabcdef },
+ HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"]))
+ );
+
+ let err = HexOpt::clap()
+ .get_matches_from_safe(&["test", "-n", "gg"])
+ .unwrap_err();
+ assert!(err.message.contains("invalid digit found in string"), err);
+}
diff --git a/structopt/tests/options.rs b/structopt/tests/options.rs
new file mode 100644
index 0000000..803abb4
--- /dev/null
+++ b/structopt/tests/options.rs
@@ -0,0 +1,336 @@
+// 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 structopt::StructOpt;
+
+#[test]
+fn required_option() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, long)]
+ arg: i32,
+ }
+ assert_eq!(
+ Opt { arg: 42 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"]))
+ );
+ assert_eq!(
+ Opt { arg: 42 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "42"]))
+ );
+ assert_eq!(
+ Opt { arg: 42 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--arg", "42"]))
+ );
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a42", "-a24"])
+ .is_err());
+}
+
+#[test]
+fn optional_option() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short)]
+ arg: Option<i32>,
+ }
+ assert_eq!(
+ Opt { arg: Some(42) },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"]))
+ );
+ assert_eq!(
+ Opt { arg: None },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a42", "-a24"])
+ .is_err());
+}
+
+#[test]
+fn option_with_default() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, default_value = "42")]
+ arg: i32,
+ }
+ assert_eq!(
+ Opt { arg: 24 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"]))
+ );
+ assert_eq!(
+ Opt { arg: 42 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a42", "-a24"])
+ .is_err());
+}
+
+#[test]
+fn option_with_raw_default() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, default_value = "42")]
+ arg: i32,
+ }
+ assert_eq!(
+ Opt { arg: 24 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"]))
+ );
+ assert_eq!(
+ Opt { arg: 42 },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a42", "-a24"])
+ .is_err());
+}
+
+#[test]
+fn options() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, long)]
+ arg: Vec<i32>,
+ }
+ assert_eq!(
+ Opt { arg: vec![24] },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"]))
+ );
+ assert_eq!(
+ Opt { arg: vec![] },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert_eq!(
+ Opt { arg: vec![24, 42] },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24", "--arg", "42"]))
+ );
+}
+
+#[test]
+fn empy_default_value() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, default_value = "")]
+ arg: String,
+ }
+ assert_eq!(Opt { arg: "".into() }, Opt::from_iter(&["test"]));
+ assert_eq!(
+ Opt { arg: "foo".into() },
+ Opt::from_iter(&["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, StructOpt, PartialEq)]
+ struct Opt {
+ #[structopt(parse(from_str))]
+ a: Option<A>,
+ }
+
+ assert_eq!(Opt { a: None }, Opt::from_iter(&["test"]));
+ assert_eq!(Opt { a: Some(A) }, Opt::from_iter(&["test", "foo"]));
+}
+
+#[test]
+fn optional_argument_for_optional_option() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short)]
+ #[allow(clippy::option_option)]
+ arg: Option<Option<i32>>,
+ }
+ assert_eq!(
+ Opt {
+ arg: Some(Some(42))
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"]))
+ );
+ assert_eq!(
+ Opt { arg: Some(None) },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
+ );
+ assert_eq!(
+ Opt { arg: None },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-a42", "-a24"])
+ .is_err());
+}
+
+#[test]
+fn two_option_options() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ #[allow(clippy::option_option)]
+ struct Opt {
+ #[structopt(short)]
+ arg: Option<Option<i32>>,
+
+ #[structopt(long)]
+ field: Option<Option<String>>,
+ }
+ assert_eq!(
+ Opt {
+ arg: Some(Some(42)),
+ field: Some(Some("f".into()))
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42", "--field", "f"]))
+ );
+ assert_eq!(
+ Opt {
+ arg: Some(Some(42)),
+ field: Some(None)
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42", "--field"]))
+ );
+ assert_eq!(
+ Opt {
+ arg: Some(None),
+ field: Some(None)
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--field"]))
+ );
+ assert_eq!(
+ Opt {
+ arg: Some(None),
+ field: Some(Some("f".into()))
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--field", "f"]))
+ );
+ assert_eq!(
+ Opt {
+ arg: None,
+ field: Some(None)
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--field"]))
+ );
+ assert_eq!(
+ Opt {
+ arg: None,
+ field: None
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+}
+
+#[test]
+fn optional_vec() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short)]
+ arg: Option<Vec<i32>>,
+ }
+ assert_eq!(
+ Opt { arg: Some(vec![1]) },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1, 2])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a2"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1, 2])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a2", "-a"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1, 2])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a", "-a2"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1, 2])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1", "2"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1, 2, 3])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1", "2", "-a", "3"]))
+ );
+
+ assert_eq!(
+ Opt { arg: Some(vec![]) },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"]))
+ );
+
+ assert_eq!(
+ Opt { arg: Some(vec![]) },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-a"]))
+ );
+
+ assert_eq!(
+ Opt { arg: None },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+}
+
+#[test]
+fn two_optional_vecs() {
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short)]
+ arg: Option<Vec<i32>>,
+
+ #[structopt(short)]
+ b: Option<Vec<i32>>,
+ }
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1]),
+ b: Some(vec![])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1", "-b"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1]),
+ b: Some(vec![])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-b", "-a1"]))
+ );
+
+ assert_eq!(
+ Opt {
+ arg: Some(vec![1, 2]),
+ b: Some(vec![1, 2])
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a2", "-b1", "-b2"]))
+ );
+
+ assert_eq!(
+ Opt { arg: None, b: None },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test"]))
+ );
+}
diff --git a/structopt/tests/privacy.rs b/structopt/tests/privacy.rs
new file mode 100644
index 0000000..730bbce
--- /dev/null
+++ b/structopt/tests/privacy.rs
@@ -0,0 +1,32 @@
+// 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 structopt::StructOpt;
+
+mod options {
+ use super::StructOpt;
+
+ #[derive(Debug, StructOpt)]
+ pub struct Options {
+ #[structopt(subcommand)]
+ pub subcommand: super::subcommands::SubCommand,
+ }
+}
+
+mod subcommands {
+ use super::StructOpt;
+
+ #[derive(Debug, StructOpt)]
+ pub enum SubCommand {
+ /// foo
+ Foo {
+ /// foo
+ bars: Vec<String>,
+ },
+ }
+}
diff --git a/structopt/tests/raw_bool_literal.rs b/structopt/tests/raw_bool_literal.rs
new file mode 100644
index 0000000..faf8628
--- /dev/null
+++ b/structopt/tests/raw_bool_literal.rs
@@ -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 structopt::StructOpt;
+
+#[test]
+fn raw_bool_literal() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ #[structopt(no_version, name = "raw_bool")]
+ struct Opt {
+ #[structopt(raw(false))]
+ a: String,
+ #[structopt(raw(true))]
+ b: String,
+ }
+
+ assert_eq!(
+ Opt {
+ a: "one".into(),
+ b: "--help".into()
+ },
+ Opt::from_iter(&["test", "one", "--", "--help"])
+ );
+}
diff --git a/structopt/tests/raw_idents.rs b/structopt/tests/raw_idents.rs
new file mode 100644
index 0000000..c00ff66
--- /dev/null
+++ b/structopt/tests/raw_idents.rs
@@ -0,0 +1,17 @@
+use structopt::StructOpt;
+
+#[test]
+fn raw_idents() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(short, long)]
+ r#type: Vec<String>,
+ }
+
+ assert_eq!(
+ Opt {
+ r#type: vec!["long".into(), "short".into()]
+ },
+ Opt::from_iter(&["test", "--type", "long", "-t", "short"])
+ );
+}
diff --git a/structopt/tests/rename_all_env.rs b/structopt/tests/rename_all_env.rs
new file mode 100644
index 0000000..1979e84
--- /dev/null
+++ b/structopt/tests/rename_all_env.rs
@@ -0,0 +1,46 @@
+mod utils;
+
+use structopt::StructOpt;
+use utils::*;
+
+#[test]
+fn it_works() {
+ #[derive(Debug, PartialEq, StructOpt)]
+ #[structopt(rename_all_env = "kebab")]
+ struct BehaviorModel {
+ #[structopt(env)]
+ be_nice: String,
+ }
+
+ let help = get_help::<BehaviorModel>();
+ assert!(help.contains("[env: be-nice=]"));
+}
+
+#[test]
+fn default_is_screaming() {
+ #[derive(Debug, PartialEq, StructOpt)]
+ struct BehaviorModel {
+ #[structopt(env)]
+ be_nice: String,
+ }
+
+ let help = get_help::<BehaviorModel>();
+ assert!(help.contains("[env: BE_NICE=]"));
+}
+
+#[test]
+fn overridable() {
+ #[derive(Debug, PartialEq, StructOpt)]
+ #[structopt(rename_all_env = "kebab")]
+ struct BehaviorModel {
+ #[structopt(env)]
+ be_nice: String,
+
+ #[structopt(rename_all_env = "pascal", env)]
+ be_agressive: String,
+ }
+
+ let help = get_help::<BehaviorModel>();
+ assert!(help.contains("[env: be-nice=]"));
+ assert!(help.contains("[env: BeAgressive=]"));
+}
diff --git a/structopt/tests/skip.rs b/structopt/tests/skip.rs
new file mode 100644
index 0000000..47682d8
--- /dev/null
+++ b/structopt/tests/skip.rs
@@ -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 structopt::StructOpt;
+
+#[test]
+fn skip_1() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(short)]
+ x: u32,
+ #[structopt(skip)]
+ s: u32,
+ }
+
+ assert!(Opt::from_iter_safe(&["test", "-x", "10", "20"]).is_err());
+ assert_eq!(
+ Opt::from_iter(&["test", "-x", "10"]),
+ Opt {
+ x: 10,
+ s: 0, // default
+ }
+ );
+}
+
+#[test]
+fn skip_2() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(short)]
+ x: u32,
+ #[structopt(skip)]
+ ss: String,
+ #[structopt(skip)]
+ sn: u8,
+ y: u32,
+ #[structopt(skip)]
+ sz: u16,
+ t: u32,
+ }
+
+ assert_eq!(
+ Opt::from_iter(&["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(StructOpt, Debug, PartialEq)]
+ pub struct Opt {
+ #[structopt(long, short)]
+ number: u32,
+ #[structopt(skip)]
+ k: Kind,
+ #[structopt(skip)]
+ v: Vec<u32>,
+ }
+
+ assert_eq!(
+ Opt::from_iter(&["test", "-n", "10"]),
+ Opt {
+ number: 10,
+ k: Kind::B,
+ v: vec![],
+ }
+ );
+}
+
+#[test]
+fn skip_help_doc_comments() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ pub struct Opt {
+ #[structopt(skip, help = "internal_stuff")]
+ a: u32,
+
+ #[structopt(skip, long_help = "internal_stuff\ndo not touch")]
+ b: u32,
+
+ /// Not meant to be used by clap.
+ ///
+ /// I want a default here.
+ #[structopt(skip)]
+ c: u32,
+
+ #[structopt(short, parse(try_from_str))]
+ n: u32,
+ }
+
+ assert_eq!(
+ Opt::from_iter(&["test", "-n", "10"]),
+ Opt {
+ n: 10,
+ a: 0,
+ b: 0,
+ c: 0,
+ }
+ );
+}
+
+#[test]
+fn skip_val() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ pub struct Opt {
+ #[structopt(long, short)]
+ number: u32,
+
+ #[structopt(skip = "key")]
+ k: String,
+
+ #[structopt(skip = vec![1, 2, 3])]
+ v: Vec<u32>,
+ }
+
+ assert_eq!(
+ Opt::from_iter(&["test", "-n", "10"]),
+ Opt {
+ number: 10,
+ k: "key".into(),
+ v: vec![1, 2, 3]
+ }
+ );
+}
diff --git a/structopt/tests/special_types.rs b/structopt/tests/special_types.rs
new file mode 100644
index 0000000..ffed5e2
--- /dev/null
+++ b/structopt/tests/special_types.rs
@@ -0,0 +1,73 @@
+//! Checks that types like `::std::option::Option` are not special
+
+use structopt::StructOpt;
+
+#[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(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ arg: inner::bool,
+ }
+
+ assert_eq!(
+ Opt {
+ arg: inner::bool("success".into())
+ },
+ Opt::from_iter(&["test", "success"])
+ );
+}
+
+#[test]
+fn special_types_option() {
+ fn parser(s: &str) -> Option<String> {
+ Some(s.to_string())
+ }
+
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(parse(from_str = parser))]
+ arg: ::std::option::Option<String>,
+ }
+
+ assert_eq!(
+ Opt {
+ arg: Some("success".into())
+ },
+ Opt::from_iter(&["test", "success"])
+ );
+}
+
+#[test]
+fn special_types_vec() {
+ fn parser(s: &str) -> Vec<String> {
+ vec![s.to_string()]
+ }
+
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(parse(from_str = parser))]
+ arg: ::std::vec::Vec<String>,
+ }
+
+ assert_eq!(
+ Opt {
+ arg: vec!["success".into()]
+ },
+ Opt::from_iter(&["test", "success"])
+ );
+}
diff --git a/structopt/tests/subcommands.rs b/structopt/tests/subcommands.rs
new file mode 100644
index 0000000..170c0da
--- /dev/null
+++ b/structopt/tests/subcommands.rs
@@ -0,0 +1,213 @@
+// 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.
+
+mod utils;
+
+use structopt::StructOpt;
+use utils::*;
+
+#[derive(StructOpt, PartialEq, Debug)]
+enum Opt {
+ /// Fetch stuff from GitHub
+ Fetch {
+ #[structopt(long)]
+ all: bool,
+ #[structopt(short, long)]
+ /// Overwrite local branches.
+ force: bool,
+ repo: String,
+ },
+
+ Add {
+ #[structopt(short, long)]
+ interactive: bool,
+ #[structopt(short, long)]
+ verbose: bool,
+ },
+}
+
+#[test]
+fn test_fetch() {
+ assert_eq!(
+ Opt::Fetch {
+ all: true,
+ force: false,
+ repo: "origin".to_string()
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "--all", "origin"]))
+ );
+ assert_eq!(
+ Opt::Fetch {
+ all: false,
+ force: true,
+ repo: "origin".to_string()
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "-f", "origin"]))
+ );
+}
+
+#[test]
+fn test_add() {
+ assert_eq!(
+ Opt::Add {
+ interactive: false,
+ verbose: false
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"]))
+ );
+ assert_eq!(
+ Opt::Add {
+ interactive: true,
+ verbose: true
+ },
+ Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add", "-i", "-v"]))
+ );
+}
+
+#[test]
+fn test_no_parse() {
+ let result = Opt::clap().get_matches_from_safe(&["test", "badcmd", "-i", "-v"]);
+ assert!(result.is_err());
+
+ let result = Opt::clap().get_matches_from_safe(&["test", "add", "--badoption"]);
+ assert!(result.is_err());
+
+ let result = Opt::clap().get_matches_from_safe(&["test"]);
+ assert!(result.is_err());
+}
+
+#[derive(StructOpt, 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::from_clap(&Opt2::clap().get_matches_from(&["test", "do-something", "blah"]))
+ );
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+enum Opt3 {
+ Add,
+ Init,
+ Fetch,
+}
+
+#[test]
+fn test_null_commands() {
+ assert_eq!(
+ Opt3::Add,
+ Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "add"]))
+ );
+ assert_eq!(
+ Opt3::Init,
+ Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "init"]))
+ );
+ assert_eq!(
+ Opt3::Fetch,
+ Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "fetch"]))
+ );
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+#[structopt(about = "Not shown")]
+struct Add {
+ file: String,
+}
+/// Not shown
+#[derive(StructOpt, PartialEq, Debug)]
+struct Fetch {
+ remote: String,
+}
+#[derive(StructOpt, PartialEq, Debug)]
+enum Opt4 {
+ // Not shown
+ /// Add a file
+ Add(Add),
+ Init,
+ /// download history from remote
+ Fetch(Fetch),
+}
+
+#[test]
+fn test_tuple_commands() {
+ assert_eq!(
+ Opt4::Add(Add {
+ file: "f".to_string()
+ }),
+ Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "add", "f"]))
+ );
+ assert_eq!(
+ Opt4::Init,
+ Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "init"]))
+ );
+ assert_eq!(
+ Opt4::Fetch(Fetch {
+ remote: "origin".to_string()
+ }),
+ Opt4::from_clap(&Opt4::clap().get_matches_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(StructOpt, Debug, PartialEq)]
+ pub enum Opt {
+ Daemon(DaemonCommand),
+ }
+
+ #[derive(StructOpt, Debug, PartialEq)]
+ pub enum DaemonCommand {
+ Start,
+ Stop,
+ }
+
+ let result = Opt::clap().get_matches_from_safe(&["test"]);
+ assert!(result.is_err());
+
+ let result = Opt::clap().get_matches_from_safe(&["test", "daemon"]);
+ assert!(result.is_err());
+
+ let result = Opt::from_iter(&["test", "daemon", "start"]);
+ assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
+}
+
+#[test]
+fn flatten_enum() {
+ #[derive(StructOpt, Debug, PartialEq)]
+ struct Opt {
+ #[structopt(flatten)]
+ sub_cmd: SubCmd,
+ }
+ #[derive(StructOpt, Debug, PartialEq)]
+ enum SubCmd {
+ Foo,
+ Bar,
+ }
+
+ assert!(Opt::from_iter_safe(&["test"]).is_err());
+ assert_eq!(
+ Opt::from_iter(&["test", "foo"]),
+ Opt {
+ sub_cmd: SubCmd::Foo
+ }
+ );
+}
diff --git a/structopt/tests/ui/bool_default_value.rs b/structopt/tests/ui/bool_default_value.rs
new file mode 100644
index 0000000..9bdb0c9
--- /dev/null
+++ b/structopt/tests/ui/bool_default_value.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(short, default_value = true)]
+ b: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/bool_default_value.stderr b/structopt/tests/ui/bool_default_value.stderr
new file mode 100644
index 0000000..1e26a2d
--- /dev/null
+++ b/structopt/tests/ui/bool_default_value.stderr
@@ -0,0 +1,5 @@
+error: default_value is meaningless for bool
+ --> $DIR/bool_default_value.rs:14:24
+ |
+14 | #[structopt(short, default_value = true)]
+ | ^^^^^^^^^^^^^
diff --git a/structopt/tests/ui/bool_required.rs b/structopt/tests/ui/bool_required.rs
new file mode 100644
index 0000000..018223c
--- /dev/null
+++ b/structopt/tests/ui/bool_required.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(short, required = true)]
+ b: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/bool_required.stderr b/structopt/tests/ui/bool_required.stderr
new file mode 100644
index 0000000..0b80d48
--- /dev/null
+++ b/structopt/tests/ui/bool_required.stderr
@@ -0,0 +1,5 @@
+error: required is meaningless for bool
+ --> $DIR/bool_required.rs:14:24
+ |
+14 | #[structopt(short, required = true)]
+ | ^^^^^^^^
diff --git a/structopt/tests/ui/flatten_and_doc.rs b/structopt/tests/ui/flatten_and_doc.rs
new file mode 100644
index 0000000..2dc154d
--- /dev/null
+++ b/structopt/tests/ui/flatten_and_doc.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct DaemonOpts {
+ #[structopt(short)]
+ user: String,
+ #[structopt(short)]
+ group: String,
+}
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ /// Opts.
+ #[structopt(flatten)]
+ opts: DaemonOpts,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/flatten_and_doc.stderr b/structopt/tests/ui/flatten_and_doc.stderr
new file mode 100644
index 0000000..2724dbb
--- /dev/null
+++ b/structopt/tests/ui/flatten_and_doc.stderr
@@ -0,0 +1,5 @@
+error: methods and doc comments are not allowed for flattened entry
+ --> $DIR/flatten_and_doc.rs:23:17
+ |
+23 | #[structopt(flatten)]
+ | ^^^^^^^
diff --git a/structopt/tests/ui/flatten_and_methods.rs b/structopt/tests/ui/flatten_and_methods.rs
new file mode 100644
index 0000000..ff1af2e
--- /dev/null
+++ b/structopt/tests/ui/flatten_and_methods.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct DaemonOpts {
+ #[structopt(short)]
+ user: String,
+ #[structopt(short)]
+ group: String,
+}
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(short, flatten)]
+ opts: DaemonOpts,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/flatten_and_methods.stderr b/structopt/tests/ui/flatten_and_methods.stderr
new file mode 100644
index 0000000..f058eb3
--- /dev/null
+++ b/structopt/tests/ui/flatten_and_methods.stderr
@@ -0,0 +1,5 @@
+error: methods and doc comments are not allowed for flattened entry
+ --> $DIR/flatten_and_methods.rs:22:24
+ |
+22 | #[structopt(short, flatten)]
+ | ^^^^^^^
diff --git a/structopt/tests/ui/flatten_and_parse.rs b/structopt/tests/ui/flatten_and_parse.rs
new file mode 100644
index 0000000..3317272
--- /dev/null
+++ b/structopt/tests/ui/flatten_and_parse.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct DaemonOpts {
+ #[structopt(short)]
+ user: String,
+ #[structopt(short)]
+ group: String,
+}
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(flatten, parse(from_occurrences))]
+ opts: DaemonOpts,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/flatten_and_parse.stderr b/structopt/tests/ui/flatten_and_parse.stderr
new file mode 100644
index 0000000..e217a84
--- /dev/null
+++ b/structopt/tests/ui/flatten_and_parse.stderr
@@ -0,0 +1,5 @@
+error: parse attribute is not allowed for flattened entry
+ --> $DIR/flatten_and_parse.rs:22:26
+ |
+22 | #[structopt(flatten, parse(from_occurrences))]
+ | ^^^^^
diff --git a/structopt/tests/ui/non_existent_attr.rs b/structopt/tests/ui/non_existent_attr.rs
new file mode 100644
index 0000000..96daf45
--- /dev/null
+++ b/structopt/tests/ui/non_existent_attr.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(short, non_existing_attribute = 1)]
+ debug: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/non_existent_attr.stderr b/structopt/tests/ui/non_existent_attr.stderr
new file mode 100644
index 0000000..17bb87f
--- /dev/null
+++ b/structopt/tests/ui/non_existent_attr.stderr
@@ -0,0 +1,5 @@
+error[E0599]: no method named `non_existing_attribute` found for type `clap::args::arg::Arg<'_, '_>` in the current scope
+ --> $DIR/non_existent_attr.rs:14:24
+ |
+14 | #[structopt(short, non_existing_attribute = 1)]
+ | ^^^^^^^^^^^^^^^^^^^^^^ method not found in `clap::args::arg::Arg<'_, '_>`
diff --git a/structopt/tests/ui/opt_opt_nonpositional.rs b/structopt/tests/ui/opt_opt_nonpositional.rs
new file mode 100644
index 0000000..2a08105
--- /dev/null
+++ b/structopt/tests/ui/opt_opt_nonpositional.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ n: Option<Option<u32>>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/opt_opt_nonpositional.stderr b/structopt/tests/ui/opt_opt_nonpositional.stderr
new file mode 100644
index 0000000..586bf7a
--- /dev/null
+++ b/structopt/tests/ui/opt_opt_nonpositional.stderr
@@ -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>>,
+ | ^^^^^^
diff --git a/structopt/tests/ui/opt_vec_nonpositional.rs b/structopt/tests/ui/opt_vec_nonpositional.rs
new file mode 100644
index 0000000..0f6f078
--- /dev/null
+++ b/structopt/tests/ui/opt_vec_nonpositional.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ n: Option<Vec<u32>>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/opt_vec_nonpositional.stderr b/structopt/tests/ui/opt_vec_nonpositional.stderr
new file mode 100644
index 0000000..6f01de5
--- /dev/null
+++ b/structopt/tests/ui/opt_vec_nonpositional.stderr
@@ -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>>,
+ | ^^^^^^
diff --git a/structopt/tests/ui/option_default_value.rs b/structopt/tests/ui/option_default_value.rs
new file mode 100644
index 0000000..a86bc0e
--- /dev/null
+++ b/structopt/tests/ui/option_default_value.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(short, default_value = 1)]
+ n: Option<u32>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/option_default_value.stderr b/structopt/tests/ui/option_default_value.stderr
new file mode 100644
index 0000000..2215497
--- /dev/null
+++ b/structopt/tests/ui/option_default_value.stderr
@@ -0,0 +1,5 @@
+error: default_value is meaningless for Option
+ --> $DIR/option_default_value.rs:14:24
+ |
+14 | #[structopt(short, default_value = 1)]
+ | ^^^^^^^^^^^^^
diff --git a/structopt/tests/ui/option_required.rs b/structopt/tests/ui/option_required.rs
new file mode 100644
index 0000000..d91afbf
--- /dev/null
+++ b/structopt/tests/ui/option_required.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(short, required = true)]
+ n: Option<u32>,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/option_required.stderr b/structopt/tests/ui/option_required.stderr
new file mode 100644
index 0000000..0230d57
--- /dev/null
+++ b/structopt/tests/ui/option_required.stderr
@@ -0,0 +1,5 @@
+error: required is meaningless for Option
+ --> $DIR/option_required.rs:14:24
+ |
+14 | #[structopt(short, required = true)]
+ | ^^^^^^^^
diff --git a/structopt/tests/ui/parse_empty_try_from_os.rs b/structopt/tests/ui/parse_empty_try_from_os.rs
new file mode 100644
index 0000000..acfef0b
--- /dev/null
+++ b/structopt/tests/ui/parse_empty_try_from_os.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(parse(try_from_os_str))]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/parse_empty_try_from_os.stderr b/structopt/tests/ui/parse_empty_try_from_os.stderr
new file mode 100644
index 0000000..3dc9f24
--- /dev/null
+++ b/structopt/tests/ui/parse_empty_try_from_os.stderr
@@ -0,0 +1,5 @@
+error: you must set parser for `try_from_os_str` explicitly
+ --> $DIR/parse_empty_try_from_os.rs:14:23
+ |
+14 | #[structopt(parse(try_from_os_str))]
+ | ^^^^^^^^^^^^^^^
diff --git a/structopt/tests/ui/parse_function_is_not_path.rs b/structopt/tests/ui/parse_function_is_not_path.rs
new file mode 100644
index 0000000..5eebc57
--- /dev/null
+++ b/structopt/tests/ui/parse_function_is_not_path.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(parse(from_str = "2"))]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/parse_function_is_not_path.stderr b/structopt/tests/ui/parse_function_is_not_path.stderr
new file mode 100644
index 0000000..7cf7444
--- /dev/null
+++ b/structopt/tests/ui/parse_function_is_not_path.stderr
@@ -0,0 +1,5 @@
+error: `parse` argument must be a function path
+ --> $DIR/parse_function_is_not_path.rs:14:34
+ |
+14 | #[structopt(parse(from_str = "2"))]
+ | ^^^
diff --git a/structopt/tests/ui/parse_literal_spec.rs b/structopt/tests/ui/parse_literal_spec.rs
new file mode 100644
index 0000000..b6f125a
--- /dev/null
+++ b/structopt/tests/ui/parse_literal_spec.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(parse("from_str"))]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/parse_literal_spec.stderr b/structopt/tests/ui/parse_literal_spec.stderr
new file mode 100644
index 0000000..6e99e8b
--- /dev/null
+++ b/structopt/tests/ui/parse_literal_spec.stderr
@@ -0,0 +1,5 @@
+error: parser specification must start with identifier
+ --> $DIR/parse_literal_spec.rs:14:23
+ |
+14 | #[structopt(parse("from_str"))]
+ | ^^^^^^^^^^
diff --git a/structopt/tests/ui/parse_not_zero_args.rs b/structopt/tests/ui/parse_not_zero_args.rs
new file mode 100644
index 0000000..8729178
--- /dev/null
+++ b/structopt/tests/ui/parse_not_zero_args.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt(parse(from_str, from_str))]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/parse_not_zero_args.stderr b/structopt/tests/ui/parse_not_zero_args.stderr
new file mode 100644
index 0000000..11abe06
--- /dev/null
+++ b/structopt/tests/ui/parse_not_zero_args.stderr
@@ -0,0 +1,5 @@
+error: parse must have exactly one argument
+ --> $DIR/parse_not_zero_args.rs:14:17
+ |
+14 | #[structopt(parse(from_str, from_str))]
+ | ^^^^^
diff --git a/structopt/tests/ui/positional_bool.rs b/structopt/tests/ui/positional_bool.rs
new file mode 100644
index 0000000..4dbf538
--- /dev/null
+++ b/structopt/tests/ui/positional_bool.rs
@@ -0,0 +1,10 @@
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct Opt {
+ verbose: bool,
+}
+
+fn main() {
+ Opt::from_args();
+} \ No newline at end of file
diff --git a/structopt/tests/ui/positional_bool.stderr b/structopt/tests/ui/positional_bool.stderr
new file mode 100644
index 0000000..c3ed1ad
--- /dev/null
+++ b/structopt/tests/ui/positional_bool.stderr
@@ -0,0 +1,10 @@
+error: `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/TeXitoi/structopt/tree/master/examples/true_or_false.rs
+
+ --> $DIR/positional_bool.rs:5:14
+ |
+5 | verbose: bool,
+ | ^^^^
diff --git a/structopt/tests/ui/raw.rs b/structopt/tests/ui/raw.rs
new file mode 100644
index 0000000..b94f783
--- /dev/null
+++ b/structopt/tests/ui/raw.rs
@@ -0,0 +1,25 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+struct Opt {
+ #[structopt(raw(case_insensitive = "true"))]
+ s: String,
+}
+
+#[derive(StructOpt, Debug)]
+struct Opt2 {
+ #[structopt(raw(requires_if = r#""one", "two""#))]
+ s: String,
+}
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/raw.stderr b/structopt/tests/ui/raw.stderr
new file mode 100644
index 0000000..93b5e38
--- /dev/null
+++ b/structopt/tests/ui/raw.stderr
@@ -0,0 +1,19 @@
+error: `#[structopt(raw(...))` attributes are removed in structopt 0.3, 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: if you need to call `clap::Arg/App::case_insensitive` method you can do it like this: #[structopt(case_insensitive = true)]
+
+ --> $DIR/raw.rs:13:17
+ |
+13 | #[structopt(raw(case_insensitive = "true"))]
+ | ^^^
+
+error: `#[structopt(raw(...))` attributes are removed in structopt 0.3, 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: if you need to call `clap::Arg/App::requires_if` method you can do it like this: #[structopt(requires_if("one", "two"))]
+
+ --> $DIR/raw.rs:19:17
+ |
+19 | #[structopt(raw(requires_if = r#""one", "two""#))]
+ | ^^^
diff --git a/structopt/tests/ui/rename_all_wrong_casing.rs b/structopt/tests/ui/rename_all_wrong_casing.rs
new file mode 100644
index 0000000..4dabe14
--- /dev/null
+++ b/structopt/tests/ui/rename_all_wrong_casing.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic", rename_all = "fail")]
+struct Opt {
+ #[structopt(short)]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/rename_all_wrong_casing.stderr b/structopt/tests/ui/rename_all_wrong_casing.stderr
new file mode 100644
index 0000000..2a72080
--- /dev/null
+++ b/structopt/tests/ui/rename_all_wrong_casing.stderr
@@ -0,0 +1,5 @@
+error: unsupported casing: `fail`
+ --> $DIR/rename_all_wrong_casing.rs:12:42
+ |
+12 | #[structopt(name = "basic", rename_all = "fail")]
+ | ^^^^^^
diff --git a/structopt/tests/ui/skip_flatten.rs b/structopt/tests/ui/skip_flatten.rs
new file mode 100644
index 0000000..8668ec2
--- /dev/null
+++ b/structopt/tests/ui/skip_flatten.rs
@@ -0,0 +1,42 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(skip, flatten)]
+ cmd: Command,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+impl Default for Command {
+ fn default() -> Self {
+ Command::Pound { acorns: 0 }
+ }
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/skip_flatten.stderr b/structopt/tests/ui/skip_flatten.stderr
new file mode 100644
index 0000000..76477a3
--- /dev/null
+++ b/structopt/tests/ui/skip_flatten.stderr
@@ -0,0 +1,5 @@
+error: subcommand, flatten and skip cannot be used together
+ --> $DIR/skip_flatten.rs:17:23
+ |
+17 | #[structopt(skip, flatten)]
+ | ^^^^^^^
diff --git a/structopt/tests/ui/skip_subcommand.rs b/structopt/tests/ui/skip_subcommand.rs
new file mode 100644
index 0000000..5d21426
--- /dev/null
+++ b/structopt/tests/ui/skip_subcommand.rs
@@ -0,0 +1,42 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(subcommand, skip)]
+ cmd: Command,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+impl Default for Command {
+ fn default() -> Self {
+ Command::Pound { acorns: 0 }
+ }
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/skip_subcommand.stderr b/structopt/tests/ui/skip_subcommand.stderr
new file mode 100644
index 0000000..aba2d69
--- /dev/null
+++ b/structopt/tests/ui/skip_subcommand.stderr
@@ -0,0 +1,5 @@
+error: subcommand, flatten and skip cannot be used together
+ --> $DIR/skip_subcommand.rs:17:29
+ |
+17 | #[structopt(subcommand, skip)]
+ | ^^^^
diff --git a/structopt/tests/ui/skip_with_other_options.rs b/structopt/tests/ui/skip_with_other_options.rs
new file mode 100644
index 0000000..73c5342
--- /dev/null
+++ b/structopt/tests/ui/skip_with_other_options.rs
@@ -0,0 +1,15 @@
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "test")]
+pub struct Opt {
+ #[structopt(long)]
+ a: u32,
+ #[structopt(skip, long)]
+ b: u32,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/skip_with_other_options.stderr b/structopt/tests/ui/skip_with_other_options.stderr
new file mode 100644
index 0000000..3345f92
--- /dev/null
+++ b/structopt/tests/ui/skip_with_other_options.stderr
@@ -0,0 +1,5 @@
+error: methods are not allowed for skipped fields
+ --> $DIR/skip_with_other_options.rs:8:17
+ |
+8 | #[structopt(skip, long)]
+ | ^^^^
diff --git a/structopt/tests/ui/skip_without_default.rs b/structopt/tests/ui/skip_without_default.rs
new file mode 100644
index 0000000..bc47511
--- /dev/null
+++ b/structopt/tests/ui/skip_without_default.rs
@@ -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 structopt::StructOpt;
+
+#[derive(Debug)]
+enum Kind {
+ A,
+ B,
+}
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "test")]
+pub struct Opt {
+ #[structopt(short)]
+ number: u32,
+ #[structopt(skip)]
+ k: Kind,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/skip_without_default.stderr b/structopt/tests/ui/skip_without_default.stderr
new file mode 100644
index 0000000..330898f
--- /dev/null
+++ b/structopt/tests/ui/skip_without_default.stderr
@@ -0,0 +1,9 @@
+error[E0277]: the trait bound `Kind: std::default::Default` is not satisfied
+ --> $DIR/skip_without_default.rs:22:17
+ |
+22 | #[structopt(skip)]
+ | ^^^^ the trait `std::default::Default` is not implemented for `Kind`
+ |
+ = note: required by `std::default::Default::default`
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/structopt/tests/ui/struct_flatten.rs b/structopt/tests/ui/struct_flatten.rs
new file mode 100644
index 0000000..2b205f1
--- /dev/null
+++ b/structopt/tests/ui/struct_flatten.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic", flatten)]
+struct Opt {
+ #[structopt(short)]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/struct_flatten.stderr b/structopt/tests/ui/struct_flatten.stderr
new file mode 100644
index 0000000..7f0fc6d
--- /dev/null
+++ b/structopt/tests/ui/struct_flatten.stderr
@@ -0,0 +1,5 @@
+error: flatten is only allowed on fields
+ --> $DIR/struct_flatten.rs:12:29
+ |
+12 | #[structopt(name = "basic", flatten)]
+ | ^^^^^^^
diff --git a/structopt/tests/ui/struct_parse.rs b/structopt/tests/ui/struct_parse.rs
new file mode 100644
index 0000000..e428b23
--- /dev/null
+++ b/structopt/tests/ui/struct_parse.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic", parse(from_str))]
+struct Opt {
+ #[structopt(short)]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/struct_parse.stderr b/structopt/tests/ui/struct_parse.stderr
new file mode 100644
index 0000000..5518214
--- /dev/null
+++ b/structopt/tests/ui/struct_parse.stderr
@@ -0,0 +1,5 @@
+error: `parse` attribute is only allowed on fields
+ --> $DIR/struct_parse.rs:12:29
+ |
+12 | #[structopt(name = "basic", parse(from_str))]
+ | ^^^^^
diff --git a/structopt/tests/ui/struct_subcommand.rs b/structopt/tests/ui/struct_subcommand.rs
new file mode 100644
index 0000000..ac0b145
--- /dev/null
+++ b/structopt/tests/ui/struct_subcommand.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic", subcommand)]
+struct Opt {
+ #[structopt(short)]
+ s: String,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/struct_subcommand.stderr b/structopt/tests/ui/struct_subcommand.stderr
new file mode 100644
index 0000000..438f6f8
--- /dev/null
+++ b/structopt/tests/ui/struct_subcommand.stderr
@@ -0,0 +1,5 @@
+error: subcommand is only allowed on fields
+ --> $DIR/struct_subcommand.rs:12:29
+ |
+12 | #[structopt(name = "basic", subcommand)]
+ | ^^^^^^^^^^
diff --git a/structopt/tests/ui/structopt_empty_attr.rs b/structopt/tests/ui/structopt_empty_attr.rs
new file mode 100644
index 0000000..a7fc0b9
--- /dev/null
+++ b/structopt/tests/ui/structopt_empty_attr.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt]
+ debug: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
+
diff --git a/structopt/tests/ui/structopt_empty_attr.stderr b/structopt/tests/ui/structopt_empty_attr.stderr
new file mode 100644
index 0000000..dde3630
--- /dev/null
+++ b/structopt/tests/ui/structopt_empty_attr.stderr
@@ -0,0 +1,5 @@
+error: expected parentheses after `structopt`
+ --> $DIR/structopt_empty_attr.rs:14:7
+ |
+14 | #[structopt]
+ | ^^^^^^^^^
diff --git a/structopt/tests/ui/structopt_name_value_attr.rs b/structopt/tests/ui/structopt_name_value_attr.rs
new file mode 100644
index 0000000..3d9388f
--- /dev/null
+++ b/structopt/tests/ui/structopt_name_value_attr.rs
@@ -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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt {
+ #[structopt = "short"]
+ debug: bool,
+}
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
+
diff --git a/structopt/tests/ui/structopt_name_value_attr.stderr b/structopt/tests/ui/structopt_name_value_attr.stderr
new file mode 100644
index 0000000..f681978
--- /dev/null
+++ b/structopt/tests/ui/structopt_name_value_attr.stderr
@@ -0,0 +1,5 @@
+error: expected parentheses
+ --> $DIR/structopt_name_value_attr.rs:14:17
+ |
+14 | #[structopt = "short"]
+ | ^
diff --git a/structopt/tests/ui/subcommand_and_flatten.rs b/structopt/tests/ui/subcommand_and_flatten.rs
new file mode 100644
index 0000000..742ee6d
--- /dev/null
+++ b/structopt/tests/ui/subcommand_and_flatten.rs
@@ -0,0 +1,36 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(subcommand, flatten)]
+ cmd: Command,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/subcommand_and_flatten.stderr b/structopt/tests/ui/subcommand_and_flatten.stderr
new file mode 100644
index 0000000..cacea5e
--- /dev/null
+++ b/structopt/tests/ui/subcommand_and_flatten.stderr
@@ -0,0 +1,5 @@
+error: subcommand, flatten and skip cannot be used together
+ --> $DIR/subcommand_and_flatten.rs:17:29
+ |
+17 | #[structopt(subcommand, flatten)]
+ | ^^^^^^^
diff --git a/structopt/tests/ui/subcommand_and_methods.rs b/structopt/tests/ui/subcommand_and_methods.rs
new file mode 100644
index 0000000..890f10c
--- /dev/null
+++ b/structopt/tests/ui/subcommand_and_methods.rs
@@ -0,0 +1,36 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(subcommand, long)]
+ cmd: Command,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/subcommand_and_methods.stderr b/structopt/tests/ui/subcommand_and_methods.stderr
new file mode 100644
index 0000000..ccaf28d
--- /dev/null
+++ b/structopt/tests/ui/subcommand_and_methods.stderr
@@ -0,0 +1,5 @@
+error: methods in attributes are not allowed for subcommand
+ --> $DIR/subcommand_and_methods.rs:17:17
+ |
+17 | #[structopt(subcommand, long)]
+ | ^^^^^^^^^^
diff --git a/structopt/tests/ui/subcommand_and_parse.rs b/structopt/tests/ui/subcommand_and_parse.rs
new file mode 100644
index 0000000..f24e4bc
--- /dev/null
+++ b/structopt/tests/ui/subcommand_and_parse.rs
@@ -0,0 +1,36 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(subcommand, parse(from_occurrences))]
+ cmd: Command,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/subcommand_and_parse.stderr b/structopt/tests/ui/subcommand_and_parse.stderr
new file mode 100644
index 0000000..4070056
--- /dev/null
+++ b/structopt/tests/ui/subcommand_and_parse.stderr
@@ -0,0 +1,5 @@
+error: parse attribute is not allowed for subcommand
+ --> $DIR/subcommand_and_parse.rs:17:29
+ |
+17 | #[structopt(subcommand, parse(from_occurrences))]
+ | ^^^^^
diff --git a/structopt/tests/ui/subcommand_opt_opt.rs b/structopt/tests/ui/subcommand_opt_opt.rs
new file mode 100644
index 0000000..1dd84e5
--- /dev/null
+++ b/structopt/tests/ui/subcommand_opt_opt.rs
@@ -0,0 +1,36 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(subcommand)]
+ cmd: Option<Option<Command>>,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/subcommand_opt_opt.stderr b/structopt/tests/ui/subcommand_opt_opt.stderr
new file mode 100644
index 0000000..daad02f
--- /dev/null
+++ b/structopt/tests/ui/subcommand_opt_opt.stderr
@@ -0,0 +1,5 @@
+error: Option<Option<T>> type is not allowed for subcommand
+ --> $DIR/subcommand_opt_opt.rs:18:10
+ |
+18 | cmd: Option<Option<Command>>,
+ | ^^^^^^
diff --git a/structopt/tests/ui/subcommand_opt_vec.rs b/structopt/tests/ui/subcommand_opt_vec.rs
new file mode 100644
index 0000000..17bffbf
--- /dev/null
+++ b/structopt/tests/ui/subcommand_opt_vec.rs
@@ -0,0 +1,36 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "make-cookie")]
+struct MakeCookie {
+ #[structopt(short)]
+ s: String,
+
+ #[structopt(subcommand)]
+ cmd: Option<Vec<Command>>,
+}
+
+#[derive(StructOpt, Debug)]
+enum Command {
+ #[structopt(name = "pound")]
+ /// Pound acorns into flour for cookie dough.
+ Pound { acorns: u32 },
+
+ Sparkle {
+ #[structopt(short)]
+ color: String,
+ },
+}
+
+fn main() {
+ let opt = MakeCookie::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/subcommand_opt_vec.stderr b/structopt/tests/ui/subcommand_opt_vec.stderr
new file mode 100644
index 0000000..a9833a6
--- /dev/null
+++ b/structopt/tests/ui/subcommand_opt_vec.stderr
@@ -0,0 +1,5 @@
+error: Option<Vec<T>> type is not allowed for subcommand
+ --> $DIR/subcommand_opt_vec.rs:18:10
+ |
+18 | cmd: Option<Vec<Command>>,
+ | ^^^^^^
diff --git a/structopt/tests/ui/tuple_struct.rs b/structopt/tests/ui/tuple_struct.rs
new file mode 100644
index 0000000..af9b1d5
--- /dev/null
+++ b/structopt/tests/ui/tuple_struct.rs
@@ -0,0 +1,18 @@
+// 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 structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "basic")]
+struct Opt(u32);
+
+fn main() {
+ let opt = Opt::from_args();
+ println!("{:?}", opt);
+}
diff --git a/structopt/tests/ui/tuple_struct.stderr b/structopt/tests/ui/tuple_struct.stderr
new file mode 100644
index 0000000..9f2876f
--- /dev/null
+++ b/structopt/tests/ui/tuple_struct.stderr
@@ -0,0 +1,5 @@
+error: structopt only supports non-tuple structs and enums
+ --> $DIR/tuple_struct.rs:11:10
+ |
+11 | #[derive(StructOpt, Debug)]
+ | ^^^^^^^^^
diff --git a/structopt/tests/utils.rs b/structopt/tests/utils.rs
new file mode 100644
index 0000000..c0684a2
--- /dev/null
+++ b/structopt/tests/utils.rs
@@ -0,0 +1,45 @@
+#![allow(unused)]
+
+use structopt::StructOpt;
+
+pub fn get_help<T: StructOpt>() -> String {
+ let mut output = Vec::new();
+ <T as StructOpt>::clap().write_help(&mut output).unwrap();
+ let output = String::from_utf8(output).unwrap();
+
+ eprintln!("\n%%% HELP %%%:=====\n{}\n=====\n", output);
+ eprintln!("\n%%% HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output);
+
+ output
+}
+
+pub fn get_long_help<T: StructOpt>() -> String {
+ let mut output = Vec::new();
+ <T as StructOpt>::clap()
+ .write_long_help(&mut output)
+ .unwrap();
+ let output = String::from_utf8(output).unwrap();
+
+ eprintln!("\n%%% LONG_HELP %%%:=====\n{}\n=====\n", output);
+ eprintln!("\n%%% LONG_HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output);
+
+ output
+}
+
+pub fn get_subcommand_long_help<T: StructOpt>(subcmd: &str) -> String {
+ let output = <T as StructOpt>::clap()
+ .get_matches_from_safe(vec!["test", subcmd, "--help"])
+ .expect_err("")
+ .message;
+
+ eprintln!(
+ "\n%%% SUBCOMMAND `{}` HELP %%%:=====\n{}\n=====\n",
+ subcmd, output
+ );
+ eprintln!(
+ "\n%%% SUBCOMMAND `{}` HELP (DEBUG) %%%:=====\n{:?}\n=====\n",
+ subcmd, output
+ );
+
+ output
+}