From ec1bebd0248efe69b3d0cfe8cc077c9cd0f153f1 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sun, 28 Jan 2018 12:17:04 -0800 Subject: [PATCH 1/2] Add 'parse(multiple)' parser. Don't special case 'u64'. --- examples/basic.rs | 4 +- structopt-derive/src/lib.rs | 80 ++++++++++++++++++++----------------- tests/flags.rs | 20 +++++----- tests/nested-subcommands.rs | 4 +- 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 4197da52..84eec300 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -19,8 +19,8 @@ struct Opt { #[structopt(short = "d", long = "debug")] debug: bool, - /// Verbose mode - #[structopt(short = "v", long = "verbose")] + /// Verbose mode (-v, -vv, -vvv, etc.) + #[structopt(short = "v", long = "verbose", parse(multiple))] verbose: u64, /// Set speed diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index cdda3078..d3c8cc4c 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -50,9 +50,9 @@ //! extern crate structopt; //! #[macro_use] //! extern crate structopt_derive; -//! +//! //! use structopt::StructOpt; -//! +//! //! #[derive(StructOpt, Debug)] //! #[structopt(setting_raw = "clap::AppSettings::ColoredHelp")] //! struct Opt { @@ -61,7 +61,7 @@ //! #[structopt(short = "d")] //! debug: bool, //! } -//! +//! //! fn main() { //! let opt = Opt::from_args(); //! println!("{:?}", opt); @@ -82,7 +82,6 @@ //! Type | Effect | Added method call to `clap::Arg` //! ---------------------|---------------------------------------------------|-------------------------------------- //! `bool` | `true` if the flag is present | `.takes_value(false).multiple(false)` -//! `u64` | number of times the flag is used | `.takes_value(false).multiple(true)` //! `Option` | optional positional argument or option | `.takes_value(true).multiple(false)` //! `Vec` | list of options or the other positional arguments | `.takes_value(true).multiple(true)` //! `T: FromStr` | required option or positional argument | `.takes_value(true).multiple(false).required(!has_default)` @@ -197,7 +196,7 @@ //! #[structopt(name = "sparkle")] //! /// Add magical sparkles -- the secret ingredient! //! Sparkle { -//! #[structopt(short = "m")] +//! #[structopt(short = "m", parse(multiple))] //! magicality: u64, //! #[structopt(short = "c")] //! color: String @@ -278,7 +277,7 @@ //! } //! ``` //! -//! There are four kinds custom string parsers: +//! There are five kinds of custom parsers: //! //! | Kind | Signature | Default | //! |-------------------|---------------------------------------|---------------------------------| @@ -286,9 +285,15 @@ //! | `try_from_str` | `fn(&str) -> Result` | `::std::str::FromStr::from_str` | //! | `from_os_str` | `fn(&OsStr) -> T` | `::std::convert::From::from` | //! | `try_from_os_str` | `fn(&OsStr) -> Result` | (no default function) | +//! | `multiple` | (no signature) | (no default function) | +//! +//! The `multiple` parser is special. It can only be used with fields of +//! unsigned integer types (`u8`, `u32`, `u64`, and `usize`). Using +//! `parse(multiple)` results in the _number of flags occurences_ being stored +//! in the field's. In other words, it converts something like `-vvv` to `3`. +//! This is equivalent to `.takes_value(false).multiple(true)`. //! -//! When supplying a custom string parser, `bool` and `u64` will not be treated -//! specially: +//! When supplying a custom string parser, `bool` will not be treated specially: //! //! Type | Effect | Added method call to `clap::Arg` //! ------------|-------------------|-------------------------------------- @@ -321,7 +326,6 @@ pub fn structopt(input: TokenStream) -> TokenStream { #[derive(Copy, Clone, PartialEq)] enum Ty { Bool, - U64, Vec, Option, Other, @@ -331,7 +335,6 @@ fn ty(t: &syn::Ty) -> Ty { if let syn::Ty::Path(None, syn::Path { segments: ref segs, .. }) = *t { match segs.last().unwrap().ident.as_ref() { "bool" => Ty::Bool, - "u64" => Ty::U64, "Option" => Ty::Option, "Vec" => Ty::Vec, _ => Ty::Other, @@ -359,7 +362,7 @@ fn sub_type(t: &syn::Ty) -> Option<&syn::Ty> { #[derive(Debug, Clone, Copy)] enum AttrSource { Struct, Field, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum Parser { /// Parse an option to using a `fn(&str) -> T` function. The function should never fail. FromStr, @@ -370,6 +373,8 @@ enum Parser { FromOsStr, /// Parse an option to using a `fn(&OsStr) -> Result` function. TryFromOsStr, + /// Doesn't take a value. Instead, count the number of repitions. + Multiple, } fn extract_attrs<'a>(attrs: &'a [Attribute], attr_source: AttrSource) -> Box + 'a> { @@ -470,30 +475,24 @@ fn get_parser(field: &Field) -> Option<(Parser, quote::Tokens)> { match *attr { NestedMetaItem::MetaItem(MetaItem::NameValue(ref i, Lit::Str(ref v, _))) => { let function = parse_path(v).expect("parser function path"); - let parser = if i == "from_str" { - Parser::FromStr - } else if i == "try_from_str" { - Parser::TryFromStr - } else if i == "from_os_str" { - Parser::FromOsStr - } else if i == "try_from_os_str" { - Parser::TryFromOsStr - } else { - panic!("unsupported parser {}", i); + let parser = match i.as_ref() { + "from_str" => Parser::FromStr, + "try_from_str" => Parser::TryFromStr, + "from_os_str" => Parser::FromOsStr, + "try_from_os_str" => Parser::TryFromOsStr, + _ => panic!("unsupported parser {}", i) }; + (parser, quote!(#function)) } NestedMetaItem::MetaItem(MetaItem::Word(ref i)) => { - if i == "from_str" { - (Parser::FromStr, quote!(::std::convert::From::from)) - } else if i == "try_from_str" { - (Parser::TryFromStr, quote!(::std::str::FromStr::from_str)) - } else if i == "from_os_str" { - (Parser::FromOsStr, quote!(::std::convert::From::from)) - } else if i == "try_from_os_str" { - panic!("cannot omit parser function name with `try_from_os_str`") - } else { - panic!("unsupported parser {}", i); + match i.as_ref() { + "from_str" => (Parser::FromStr, quote!(::std::convert::From::from)), + "try_from_str" => (Parser::TryFromStr, quote!(::std::str::FromStr::from_str)), + "from_os_str" => (Parser::FromOsStr, quote!(::std::convert::From::from)), + "try_from_os_str" => panic!("cannot omit parser function name with `try_from_os_str`"), + "multiple" => (Parser::Multiple, quote!()), + _ => panic!("unsupported parser {}", i) } } _ => panic!("unknown value parser specification"), @@ -504,7 +503,7 @@ fn get_parser(field: &Field) -> Option<(Parser, quote::Tokens)> { fn convert_with_custom_parse(cur_type: Ty) -> Ty { match cur_type { - Ty::Bool | Ty::U64 => Ty::Other, + Ty::Bool => Ty::Other, rest => rest, } } @@ -540,15 +539,18 @@ fn gen_augmentation(fields: &[Field], app_var: &Ident) -> quote::Tokens { .map(|field| { let name = gen_name(field); let mut cur_type = ty(&field.ty); + let mut multiple = false; let convert_type = match cur_type { Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty), _ => &field.ty, }; let parser = get_parser(field); - if parser.is_some() { + if let Some((ref parser, _)) = parser { cur_type = convert_with_custom_parse(cur_type); + multiple = *parser == Parser::Multiple; } + let validator = match parser.unwrap_or_else(get_default_parser) { (Parser::TryFromStr, f) => quote! { .validator(|s| { @@ -565,9 +567,9 @@ fn gen_augmentation(fields: &[Field], app_var: &Ident) -> quote::Tokens { let modifier = match cur_type { Ty::Bool => quote!( .takes_value(false).multiple(false) ), - Ty::U64 => quote!( .takes_value(false).multiple(true) ), Ty::Option => quote!( .takes_value(true).multiple(false) #validator ), Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ), + Ty::Other if multiple => quote!( .takes_value(false).multiple(true) ), Ty::Other => { let required = extract_attrs(&field.attrs, AttrSource::Field) .find(|&(ref i, _)| i.as_ref() == "default_value" @@ -621,13 +623,15 @@ fn gen_constructor(fields: &[Field]) -> quote::Tokens { }; quote!( #field_name: #subcmd_type ::from_subcommand(matches.subcommand()) #unwrapper ) } else { - let mut cur_type = ty(&field.ty); + let real_ty = &field.ty; + let mut cur_type = ty(real_ty); let parser = get_parser(field); if parser.is_some() { cur_type = convert_with_custom_parse(cur_type); } - let (value_of, values_of, parse) = match parser.unwrap_or_else(get_default_parser) { + let parser = parser.unwrap_or_else(get_default_parser); + let (value_of, values_of, parse) = match parser { (Parser::FromStr, f) => ( quote!(value_of), quote!(values_of), @@ -648,11 +652,12 @@ fn gen_constructor(fields: &[Field]) -> quote::Tokens { quote!(values_of_os), quote!(|s| #f(s).unwrap()), ), + (Parser::Multiple, f) => ( quote!(), quote!(), f ), }; + let multiple = parser.0 == Parser::Multiple; let convert = match cur_type { Ty::Bool => quote!(is_present(stringify!(#name))), - Ty::U64 => quote!(occurrences_of(stringify!(#name))), Ty::Option => quote! { #value_of(stringify!(#name)) .as_ref() @@ -663,6 +668,7 @@ fn gen_constructor(fields: &[Field]) -> quote::Tokens { .map(|v| v.map(#parse).collect()) .unwrap_or_else(Vec::new) }, + Ty::Other if multiple => quote!(occurrences_of(stringify!(#name)) as #real_ty), Ty::Other => quote! { #value_of(stringify!(#name)) .map(#parse) diff --git a/tests/flags.rs b/tests/flags.rs index 40026e3d..fed21697 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -35,20 +35,22 @@ fn unique_flag() { fn multiple_flag() { #[derive(StructOpt, PartialEq, Debug)] struct Opt { - #[structopt(short = "a", long = "alice")] + #[structopt(short = "a", long = "alice", parse(multiple))] alice: u64, + #[structopt(short = "b", long = "bob", parse(multiple))] + bob: u8, } - assert_eq!(Opt { alice: 0 }, + assert_eq!(Opt { alice: 0, bob: 0 }, Opt::from_clap(Opt::clap().get_matches_from(&["test"]))); - assert_eq!(Opt { alice: 1 }, + assert_eq!(Opt { alice: 1, bob: 0}, Opt::from_clap(Opt::clap().get_matches_from(&["test", "-a"]))); - assert_eq!(Opt { alice: 2 }, + assert_eq!(Opt { alice: 2, bob: 0}, Opt::from_clap(Opt::clap().get_matches_from(&["test", "-a", "-a"]))); - assert_eq!(Opt { alice: 2 }, - Opt::from_clap(Opt::clap().get_matches_from(&["test", "-a", "--alice"]))); - assert_eq!(Opt { alice: 3 }, - Opt::from_clap(Opt::clap().get_matches_from(&["test", "-aaa"]))); + 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()); } @@ -59,7 +61,7 @@ fn combined_flags() { struct Opt { #[structopt(short = "a", long = "alice")] alice: bool, - #[structopt(short = "b", long = "bob")] + #[structopt(short = "b", long = "bob", parse(multiple))] bob: u64, } diff --git a/tests/nested-subcommands.rs b/tests/nested-subcommands.rs index db2b4097..b2de725c 100644 --- a/tests/nested-subcommands.rs +++ b/tests/nested-subcommands.rs @@ -14,7 +14,7 @@ use structopt::StructOpt; struct Opt { #[structopt(short = "f", long = "force")] force: bool, - #[structopt(short = "v", long = "verbose")] + #[structopt(short = "v", long = "verbose", parse(multiple))] verbose: u64, #[structopt(subcommand)] cmd: Sub @@ -32,7 +32,7 @@ enum Sub { struct Opt2 { #[structopt(short = "f", long = "force")] force: bool, - #[structopt(short = "v", long = "verbose")] + #[structopt(short = "v", long = "verbose", parse(multiple))] verbose: u64, #[structopt(subcommand)] cmd: Option From 863bc55c45217902e6c9229ba634d09f71be8869 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 30 Jan 2018 14:39:34 -0800 Subject: [PATCH 2/2] Remove 'parse(multiple)'. Add 'parse(from_occurrences)'. --- examples/basic.rs | 2 +- structopt-derive/src/lib.rs | 56 ++++++++++++++++++++-------------- tests/custom-string-parsers.rs | 42 +++++++++++++++++++++++++ tests/flags.rs | 14 ++++----- tests/nested-subcommands.rs | 4 +-- 5 files changed, 85 insertions(+), 33 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 84eec300..3959ccf7 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -20,7 +20,7 @@ struct Opt { debug: bool, /// Verbose mode (-v, -vv, -vvv, etc.) - #[structopt(short = "v", long = "verbose", parse(multiple))] + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] verbose: u64, /// Set speed diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index d3c8cc4c..7b37fd74 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -196,7 +196,7 @@ //! #[structopt(name = "sparkle")] //! /// Add magical sparkles -- the secret ingredient! //! Sparkle { -//! #[structopt(short = "m", parse(multiple))] +//! #[structopt(short = "m", parse(from_occurrences))] //! magicality: u64, //! #[structopt(short = "c")] //! color: String @@ -285,13 +285,14 @@ //! | `try_from_str` | `fn(&str) -> Result` | `::std::str::FromStr::from_str` | //! | `from_os_str` | `fn(&OsStr) -> T` | `::std::convert::From::from` | //! | `try_from_os_str` | `fn(&OsStr) -> Result` | (no default function) | -//! | `multiple` | (no signature) | (no default function) | +//! | `from_occurrences`| `fn(u64) -> T` | `value as T` | //! -//! The `multiple` parser is special. It can only be used with fields of -//! unsigned integer types (`u8`, `u32`, `u64`, and `usize`). Using -//! `parse(multiple)` results in the _number of flags occurences_ being stored -//! in the field's. In other words, it converts something like `-vvv` to `3`. -//! This is equivalent to `.takes_value(false).multiple(true)`. +//! 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.). //! //! When supplying a custom string parser, `bool` will not be treated specially: //! @@ -373,8 +374,9 @@ enum Parser { FromOsStr, /// Parse an option to using a `fn(&OsStr) -> Result` function. TryFromOsStr, - /// Doesn't take a value. Instead, count the number of repitions. - Multiple, + /// Counts the number of flag occurrences. Parses using a `fn(u64) -> T` function. The function + /// should never fail. + FromOccurrences, } fn extract_attrs<'a>(attrs: &'a [Attribute], attr_source: AttrSource) -> Box + 'a> { @@ -480,6 +482,7 @@ fn get_parser(field: &Field) -> Option<(Parser, quote::Tokens)> { "try_from_str" => Parser::TryFromStr, "from_os_str" => Parser::FromOsStr, "try_from_os_str" => Parser::TryFromOsStr, + "from_occurrences" => Parser::FromOccurrences, _ => panic!("unsupported parser {}", i) }; @@ -491,8 +494,8 @@ fn get_parser(field: &Field) -> Option<(Parser, quote::Tokens)> { "try_from_str" => (Parser::TryFromStr, quote!(::std::str::FromStr::from_str)), "from_os_str" => (Parser::FromOsStr, quote!(::std::convert::From::from)), "try_from_os_str" => panic!("cannot omit parser function name with `try_from_os_str`"), - "multiple" => (Parser::Multiple, quote!()), - _ => panic!("unsupported parser {}", i) + "from_occurrences" => (Parser::FromOccurrences, quote!({|v| v as _})), + _ => panic!("unsupported parser {}", i) } } _ => panic!("unknown value parser specification"), @@ -539,16 +542,16 @@ fn gen_augmentation(fields: &[Field], app_var: &Ident) -> quote::Tokens { .map(|field| { let name = gen_name(field); let mut cur_type = ty(&field.ty); - let mut multiple = false; let convert_type = match cur_type { Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty), _ => &field.ty, }; + let mut occurences = false; let parser = get_parser(field); if let Some((ref parser, _)) = parser { cur_type = convert_with_custom_parse(cur_type); - multiple = *parser == Parser::Multiple; + occurences = *parser == Parser::FromOccurrences; } let validator = match parser.unwrap_or_else(get_default_parser) { @@ -569,7 +572,7 @@ fn gen_augmentation(fields: &[Field], app_var: &Ident) -> quote::Tokens { Ty::Bool => quote!( .takes_value(false).multiple(false) ), Ty::Option => quote!( .takes_value(true).multiple(false) #validator ), Ty::Vec => quote!( .takes_value(true).multiple(true) #validator ), - Ty::Other if multiple => quote!( .takes_value(false).multiple(true) ), + Ty::Other if occurences => quote!( .takes_value(false).multiple(true) ), Ty::Other => { let required = extract_attrs(&field.attrs, AttrSource::Field) .find(|&(ref i, _)| i.as_ref() == "default_value" @@ -652,30 +655,37 @@ fn gen_constructor(fields: &[Field]) -> quote::Tokens { quote!(values_of_os), quote!(|s| #f(s).unwrap()), ), - (Parser::Multiple, f) => ( quote!(), quote!(), f ), + (Parser::FromOccurrences, f) => ( + quote!(occurrences_of), + quote!(), + f, + ), }; - let multiple = parser.0 == Parser::Multiple; - let convert = match cur_type { - Ty::Bool => quote!(is_present(stringify!(#name))), + let occurences = parser.0 == Parser::FromOccurrences; + let field_value = match cur_type { + Ty::Bool => quote!(matches.is_present(stringify!(#name))), Ty::Option => quote! { - #value_of(stringify!(#name)) + matches.#value_of(stringify!(#name)) .as_ref() .map(#parse) }, Ty::Vec => quote! { - #values_of(stringify!(#name)) + matches.#values_of(stringify!(#name)) .map(|v| v.map(#parse).collect()) .unwrap_or_else(Vec::new) }, - Ty::Other if multiple => quote!(occurrences_of(stringify!(#name)) as #real_ty), + Ty::Other if occurences => quote! { + #parse(matches.#value_of(stringify!(#name))) + }, Ty::Other => quote! { - #value_of(stringify!(#name)) + matches.#value_of(stringify!(#name)) .map(#parse) .unwrap() }, }; - quote!( #field_name: matches.#convert ) + + quote!( #field_name: #field_value ) } }); diff --git a/tests/custom-string-parsers.rs b/tests/custom-string-parsers.rs index 9db105d7..905c1e1e 100644 --- a/tests/custom-string-parsers.rs +++ b/tests/custom-string-parsers.rs @@ -157,3 +157,45 @@ fn test_parser_with_default_value() { ])) ); } + + +#[derive(PartialEq, Debug)] +struct Foo(u8); + +fn foo(value: u64) -> Foo { + Foo(value as u8) +} + +#[derive(StructOpt, PartialEq, Debug)] +struct Occurrences { + #[structopt(short = "s", long = "signed", parse(from_occurrences))] + signed: i32, + + #[structopt(short = "l", parse(from_occurrences))] + little_signed: i8, + + #[structopt(short = "u", parse(from_occurrences))] + unsigned: usize, + + #[structopt(short = "r", parse(from_occurrences))] + little_unsigned: u8, + + #[structopt(short = "c", long = "custom", 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" + ])) + ); +} diff --git a/tests/flags.rs b/tests/flags.rs index fed21697..22e79df2 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -35,21 +35,21 @@ fn unique_flag() { fn multiple_flag() { #[derive(StructOpt, PartialEq, Debug)] struct Opt { - #[structopt(short = "a", long = "alice", parse(multiple))] + #[structopt(short = "a", long = "alice", parse(from_occurrences))] alice: u64, - #[structopt(short = "b", long = "bob", parse(multiple))] + #[structopt(short = "b", long = "bob", 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}, + assert_eq!(Opt { alice: 1, bob: 0 }, Opt::from_clap(Opt::clap().get_matches_from(&["test", "-a"]))); - assert_eq!(Opt { alice: 2, bob: 0}, + 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}, + 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}, + 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()); @@ -61,7 +61,7 @@ fn combined_flags() { struct Opt { #[structopt(short = "a", long = "alice")] alice: bool, - #[structopt(short = "b", long = "bob", parse(multiple))] + #[structopt(short = "b", long = "bob", parse(from_occurrences))] bob: u64, } diff --git a/tests/nested-subcommands.rs b/tests/nested-subcommands.rs index b2de725c..382c97e8 100644 --- a/tests/nested-subcommands.rs +++ b/tests/nested-subcommands.rs @@ -14,7 +14,7 @@ use structopt::StructOpt; struct Opt { #[structopt(short = "f", long = "force")] force: bool, - #[structopt(short = "v", long = "verbose", parse(multiple))] + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] verbose: u64, #[structopt(subcommand)] cmd: Sub @@ -32,7 +32,7 @@ enum Sub { struct Opt2 { #[structopt(short = "f", long = "force")] force: bool, - #[structopt(short = "v", long = "verbose", parse(multiple))] + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] verbose: u64, #[structopt(subcommand)] cmd: Option