diff --git a/examples/README.md b/examples/README.md index 02c22290..cc727660 100644 --- a/examples/README.md +++ b/examples/README.md @@ -72,3 +72,7 @@ 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/examples/true_or_false.rs b/examples/true_or_false.rs new file mode 100644 index 00000000..630d0445 --- /dev/null +++ b/examples/true_or_false.rs @@ -0,0 +1,36 @@ +//! How to parse `--foo=true --bar=false` and turn them into bool. + +use structopt::StructOpt; + +fn true_or_false(s: &str) -> Result { + 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, +} + +fn main() { + assert_eq!( + Opt::from_iter(&["test", "--foo=true", "--bar=false"]), + Opt { + foo: true, + bar: false + } + ); + // no beauty, only truth and falseness + assert!(Opt::from_iter_safe(&["test", "--foo=beauty"]).is_err()); +} diff --git a/src/lib.rs b/src/lib.rs index 598c9325..7d921cf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,6 +302,22 @@ //! 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` is special while `::std::option::Option` 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 with an implicit parser. If you need +//! a positional `bool`, for example to parse `true` or `false` add an explicit +//! [`#[structopt(parse(...))]` annotation](#custom-string-parsers). +//! _________________ +//! //! Thus, the `speed` argument is generated as: //! //! ``` diff --git a/structopt-derive/src/attrs.rs b/structopt-derive/src/attrs.rs index 9de1827b..92e6bffb 100644 --- a/structopt-derive/src/attrs.rs +++ b/structopt-derive/src/attrs.rs @@ -479,6 +479,15 @@ impl Attrs { 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") } diff --git a/tests/ui/positional_bool.rs b/tests/ui/positional_bool.rs new file mode 100644 index 00000000..4dbf5389 --- /dev/null +++ b/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/tests/ui/positional_bool.stderr b/tests/ui/positional_bool.stderr new file mode 100644 index 00000000..c3ed1ad9 --- /dev/null +++ b/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, + | ^^^^