Skip to content

Commit

Permalink
Prohibit positional bool args
Browse files Browse the repository at this point in the history
  • Loading branch information
CreepySkeleton authored and TeXitoi committed Dec 5, 2019
1 parent 1562e31 commit 8869d2f
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 0 deletions.
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,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.
41 changes: 41 additions & 0 deletions examples/true_or_false.rs
Original file line number Diff line number Diff line change
@@ -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());
}
16 changes: 16 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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:
//!
//! ```
Expand Down
9 changes: 9 additions & 0 deletions structopt-derive/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/positional_bool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
struct Opt {
verbose: bool,
}

fn main() {
Opt::from_args();
}
10 changes: 10 additions & 0 deletions tests/ui/positional_bool.stderr
Original file line number Diff line number Diff line change
@@ -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,
| ^^^^

0 comments on commit 8869d2f

Please sign in to comment.