Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support skipping struct fields #213

Merged
merged 2 commits into from
Jul 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

## improvements

* Support skipping struct fields by [@sphynx](https://github.com/sphynx)
([#174](https://github.com/TeXitoi/structopt/issues/174))

* Add optional feature to support `paw` by [@gameldar](https://github.com/gameldar)
([#187](https://github.com/TeXitoi/structopt/issues/187))

Expand Down
5 changes: 5 additions & 0 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ struct Opt {
/// argument list is provided (e.g. `--optv a b c`).
#[structopt(long = "optv")]
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 '').
#[structopt(skip)]
skipped: String,
}

fn main() {
Expand Down
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@
//! If an argument is renamed using `name = $NAME` any following call to
//! `short` or `long` will use the new name.
//!
//! If you want to omit a struct field from the parsing process
//! altogether and just use a default value for it, you can annotate
//! the field with `#[structopt(skip)]`. Note that the field type has
//! to implement `std::default::Default` then.
//!
//! **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
Expand Down Expand Up @@ -212,6 +217,12 @@
//! /// 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() {
Expand Down
13 changes: 12 additions & 1 deletion structopt-derive/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum Kind {
Arg(Ty),
Subcommand(Ty),
FlattenStruct,
Skip,
}

#[derive(Copy, Clone, PartialEq, Debug)]
Expand Down Expand Up @@ -179,6 +180,10 @@ impl Attrs {
self.set_kind(Kind::FlattenStruct);
}

Skip => {
self.set_kind(Kind::Skip);
}

NameLitStr(name, lit) => {
self.push_str_method(&name.to_string(), &lit.value());
}
Expand Down Expand Up @@ -346,6 +351,7 @@ impl Attrs {
match res.kind {
Kind::Subcommand(_) => panic!("subcommand is only allowed on fields"),
Kind::FlattenStruct => panic!("flatten is only allowed on fields"),
Kind::Skip => panic!("skip is only allowed on fields"),
Kind::Arg(_) => res,
}
}
Expand Down Expand Up @@ -405,6 +411,11 @@ impl Attrs {

res.kind = Kind::Subcommand(ty);
}
Kind::Skip => {
if !res.methods.iter().all(|m| m.name == "help") {
panic!("methods are not allowed for skipped fields");
}
}
Kind::Arg(_) => {
let mut ty = Self::ty_from_field(&field.ty);
if res.has_custom_parser {
Expand Down Expand Up @@ -455,7 +466,7 @@ impl Attrs {
if let Kind::Arg(_) = self.kind {
self.kind = kind;
} else {
panic!("subcommands cannot be flattened");
panic!("subcommand, flatten and skip cannot be used together");
}
}
pub fn has_method(&self, method: &str) -> bool {
Expand Down
3 changes: 2 additions & 1 deletion structopt-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fn gen_augmentation(
let args = fields.iter().filter_map(|field| {
let attrs = Attrs::from_field(field, parent_attribute.casing());
match attrs.kind() {
Kind::Subcommand(_) => None,
Kind::Subcommand(_) | Kind::Skip => None,
Kind::FlattenStruct => {
let ty = &field.ty;
Some(quote! {
Expand Down Expand Up @@ -183,6 +183,7 @@ fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs)
quote!(#field_name: <#subcmd_type>::from_subcommand(matches.subcommand())#unwrapper)
}
Kind::FlattenStruct => quote!(#field_name: ::structopt::StructOpt::from_clap(matches)),
Kind::Skip => quote!(#field_name: Default::default()),
Kind::Arg(ty) => {
use crate::Parser::*;
let (value_of, values_of, parse) = match *attrs.parser() {
Expand Down
2 changes: 2 additions & 0 deletions structopt-derive/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum StructOptAttr {
Long,
Flatten,
Subcommand,
Skip,
Parse(ParserSpec),
RenameAll(LitStr),
NameLitStr(Ident, LitStr),
Expand Down Expand Up @@ -94,6 +95,7 @@ impl Parse for StructOptAttr {
"short" => Ok(Short),
"flatten" => Ok(Flatten),
"subcommand" => Ok(Subcommand),
"skip" => Ok(Skip),
_ => {
let msg = format!("unexpected attribute: {}", name_str);
Err(input.error(&msg))
Expand Down
94 changes: 94 additions & 0 deletions tests/skip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]>
//
// 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)]
#[structopt(name = "a")]
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![],
}
);
}
42 changes: 42 additions & 0 deletions tests/ui/skip_flatten.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]>
//
// 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)]
#[structopt(name = "make-cookie")]
struct MakeCookie {
#[structopt(short)]
s: String,

#[structopt(skip, flatten)]
cmd: Command,
}

#[derive(StructOpt)]
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 {
Pound { acorns: 0 }
}
}

fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
}
7 changes: 7 additions & 0 deletions tests/ui/skip_flatten.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: proc-macro derive panicked
--> $DIR/skip_flatten.rs:11:10
|
11 | #[derive(StructOpt)]
| ^^^^^^^^^
|
= help: message: subcommand, flatten and skip cannot be used together
42 changes: 42 additions & 0 deletions tests/ui/skip_subcommand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]>
//
// 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)]
#[structopt(name = "make-cookie")]
struct MakeCookie {
#[structopt(short)]
s: String,

#[structopt(subcommand, skip)]
cmd: Command,
}

#[derive(StructOpt)]
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 {
Pound { acorns: 0 }
}
}

fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
}
7 changes: 7 additions & 0 deletions tests/ui/skip_subcommand.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: proc-macro derive panicked
--> $DIR/skip_subcommand.rs:11:10
|
11 | #[derive(StructOpt)]
| ^^^^^^^^^
|
= help: message: subcommand, flatten and skip cannot be used together
15 changes: 15 additions & 0 deletions tests/ui/skip_with_other_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "test")]
pub struct Opts {
#[structopt(long)]
a: u32,
#[structopt(skip, long)]
b: u32,
}

fn main() {
let opts = Opts::from_args();
println!("{:?}", opts);
}
7 changes: 7 additions & 0 deletions tests/ui/skip_with_other_options.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: proc-macro derive panicked
--> $DIR/skip_with_other_options.rs:3:10
|
3 | #[derive(StructOpt, Debug)]
| ^^^^^^^^^
|
= help: message: methods are not allowed for skipped fields
29 changes: 29 additions & 0 deletions tests/ui/skip_without_default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]>
//
// 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 Opts {
#[structopt(short)]
number: u32,
#[structopt(skip)]
k: Kind,
}

fn main() {
let opts = Opts::from_args();
println!("{:?}", opts);
}
9 changes: 9 additions & 0 deletions tests/ui/skip_without_default.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0277]: the trait bound `Kind: std::default::Default` is not satisfied
--> $DIR/skip_without_default.rs:17:10
|
17 | #[derive(StructOpt, Debug)]
| ^^^^^^^^^ 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`.
2 changes: 1 addition & 1 deletion tests/ui/subcommand_and_flatten.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ error: proc-macro derive panicked
11 | #[derive(StructOpt)]
| ^^^^^^^^^
|
= help: message: subcommands cannot be flattened
= help: message: subcommand, flatten and skip cannot be used together