diff --git a/FAQ.md b/FAQ.md index df4a4d1c..d905cef0 100644 --- a/FAQ.md +++ b/FAQ.md @@ -73,14 +73,12 @@ To build an `App` there are three: * Derive Macros * Builder Pattern -* Yaml To build an `Arg` there are four: * Derive Macros * Builder Pattern * Usage Strings -* Yaml ### Why is there a default subcommand of help? diff --git a/README.md b/README.md index 350996fb..29ff30e5 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ We are currently hard at work trying to release `3.0`. We have a `3.0.0-beta.5` 4. [Quick Example](#quick-example) 1. [Using Derive Macros](#using-derive-macros) 2. [Using Builder Pattern](#using-builder-pattern) - 3. [Using YAML](#using-yaml) - 4. [Running it](#running-it) + 3. [Running it](#running-it) 5. [Try it!](#try-it) 1. [Pre-Built Test](#pre-built-test) 2. [Build Your Own Binary](#build-your-own-binary) @@ -92,7 +91,6 @@ Below are a few of the features which `clap` supports, full descriptions and usa * **Sub-Commands** (i.e. `git add ` where `add` is a sub-command of `git`) - Support their own sub-arguments, and sub-sub-commands independent of the parent - Get their own auto-generated Help, Version, and Usage independent of parent -* **Support for building CLIs from YAML** - This keeps your Rust source nice and tidy and makes supporting localized translation very simple! * **Requirement Rules**: Arguments can define the following types of requirement rules - Can be required by default - Can be required only if certain arguments are present @@ -283,93 +281,27 @@ The next example shows a far less verbose method, but sacrifices some of the adv // // This example demonstrates clap's "usage strings" method of creating arguments // which is less verbose -use clap::App; +use clap::{App, arg}; fn main() { let matches = App::new("myapp") .version("1.0") .author("Kevin K. ") .about("Does awesome things") - .arg("-c, --config=[FILE] 'Sets a custom config file'") - .arg(" 'Sets the input file to use'") - .arg("-v... 'Sets the level of verbosity'") + .arg(arg!(-c --config "Sets a custom config file").required(false)) + .arg(arg!( "Sets the input file to use")) + .arg(arg!(-v --verbose ... "Sets the level of verbosity")) .subcommand(App::new("test") .about("controls testing features") .version("1.3") .author("Someone E. ") - .arg("-d, --debug 'Print debug information'")) + .arg(arg!(-d --debug "Print debug information"))) .get_matches(); // Same as previous example... } ``` -#### Using YAML - -This third method shows how you can use a YAML file to build your CLI and keep your Rust source tidy -or support multiple localized translations by having different YAML files for each localization. - -First, create the `cli.yaml` file to hold your CLI options, but it could be called anything we like: - -```yaml -name: myapp -version: "1.0" -author: Kevin K. -about: Does awesome things -args: - - config: - short: c - long: config - value_name: FILE - help: Sets a custom config file - takes_value: true - - INPUT: - help: Sets the input file to use - required: true - index: 1 - - verbose: - short: v - multiple_occurrences: true - help: Sets the level of verbosity -subcommands: - - test: - about: controls testing features - version: "1.3" - author: Someone E. - args: - - debug: - short: d - long: debug - help: Print debug information -``` - -Since this feature requires additional dependencies that not everyone may want, it is *not* compiled in by default and we need to enable a feature flag in Cargo.toml: - -Simply add the `yaml` feature flag to your `Cargo.toml`. - -```toml -[dependencies] -clap = { version = "3.0.0-beta.5", features = ["yaml"] } -``` - -Finally we create our `main.rs` file just like we would have with the previous two examples: - -```rust,ignore -// (Full example with detailed comments in examples/17_yaml.rs) -// -// This example demonstrates clap's building from YAML style of creating arguments which is far -// more clean, but takes a very small performance hit compared to the other two methods. -use clap::{App, load_yaml}; - -fn main() { - // The YAML file is found relative to the current file, similar to how modules are found - let yaml = load_yaml!("cli.yaml"); - let matches = App::from(yaml).get_matches(); - - // Same as previous examples... -} -``` - #### Running it If you were to compile any of the above programs and run them with the flag `--help` or `-h` (or `help` subcommand, since we defined `test` as a subcommand) the following would be output (except the first example where the help message sort of explains the Rust code). diff --git a/benches/02_simple.rs b/benches/02_simple.rs index 77e83b2b..46a725bb 100644 --- a/benches/02_simple.rs +++ b/benches/02_simple.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg}; +use clap::{arg, App, Arg}; use criterion::{criterion_group, criterion_main, Criterion}; macro_rules! create_app { @@ -7,9 +7,9 @@ macro_rules! create_app { .version("0.1") .about("tests clap library") .author("Kevin K. ") - .arg("-f --flag 'tests flags'") - .arg("-o --option=[opt] 'tests options'") - .arg("[positional] 'tests positional'") + .arg(arg!(-f --flag "tests flags")) + .arg(arg!(-o --option "tests options").required(false)) + .arg(arg!([positional] "tests positional")) }}; } @@ -19,14 +19,14 @@ pub fn build_simple(c: &mut Criterion) { pub fn build_with_flag(c: &mut Criterion) { c.bench_function("build_with_flag", |b| { - b.iter(|| App::new("claptests").arg(Arg::from("-s, --some 'something'"))) + b.iter(|| App::new("claptests").arg(arg!(-s --some "something"))) }); } pub fn build_with_flag_ref(c: &mut Criterion) { c.bench_function("build_with_flag_ref", |b| { b.iter(|| { - let arg = Arg::from("-s, --some 'something'"); + let arg = arg!(-s --some "something"); App::new("claptests").arg(&arg) }) }); @@ -34,14 +34,14 @@ pub fn build_with_flag_ref(c: &mut Criterion) { pub fn build_with_opt(c: &mut Criterion) { c.bench_function("build_with_opt", |b| { - b.iter(|| App::new("claptests").arg(Arg::from("-s, --some 'something'"))) + b.iter(|| App::new("claptests").arg(arg!(-s --some "something"))) }); } pub fn build_with_opt_ref(c: &mut Criterion) { c.bench_function("build_with_opt_ref", |b| { b.iter(|| { - let arg = Arg::from("-s, --some 'something'"); + let arg = arg!(-s --some "something"); App::new("claptests").arg(&arg) }) }); diff --git a/benches/03_complex.rs b/benches/03_complex.rs index b149c2ad..aff631c2 100644 --- a/benches/03_complex.rs +++ b/benches/03_complex.rs @@ -1,4 +1,4 @@ -use clap::{App, AppSettings, Arg, ArgSettings}; +use clap::{arg, App, AppSettings, Arg, ArgSettings}; use criterion::{criterion_group, criterion_main, Criterion}; static OPT3_VALS: [&str; 2] = ["fast", "slow"]; @@ -10,43 +10,41 @@ macro_rules! create_app { .version("0.1") .about("tests clap library") .author("Kevin K. ") - .arg("-o --option=[opt]... 'tests options'") - .arg("[positional] 'tests positionals'") - .arg(Arg::from("-f --flag... 'tests flags'").global(true)) + .arg(arg!(-o --option ... "tests options").required(false)) + .arg(arg!([positional] "tests positionals")) + .arg(arg!(-f --flag ... "tests flags").global(true)) .args(&[ - Arg::from("[flag2] -F 'tests flags with exclusions'") + arg!(flag2: -F "tests flags with exclusions") .conflicts_with("flag") .requires("option2"), - Arg::from( - "[option2] --long-option-2 [option2] 'tests long options with exclusions'", - ) - .conflicts_with("option") - .requires("positional2"), - Arg::from("[positional2] 'tests positionals with exclusions'"), - Arg::from("-O --Option [option3] 'tests options with specific value sets'") + arg!(option2: --"long-option-2" "tests long options with exclusions") + .required(false) + .conflicts_with("option") + .requires("positional2"), + arg!([positional2] "tests positionals with exclusions"), + arg!(-O --Option "tests options with specific value sets") + .required(false) .possible_values(OPT3_VALS), - Arg::from("[positional3]... 'tests positionals with specific values'") + arg!([positional3] ... "tests positionals with specific values") .possible_values(POS3_VALS), - Arg::from("--multvals [one] [two] 'Tests multiple values, not mult occs'"), - Arg::from("--multvalsmo... [one] [two] 'Tests multiple values, not mult occs'"), - Arg::from("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), - Arg::from("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3), + arg!(--multvals "Tests multiple values not mult occs").required(false).value_names(&["one", "two"]), + arg!( + --multvalsmo "Tests multiple values, not mult occs" + ).multiple_values(true).required(false).value_names(&["one", "two"]), + arg!(--minvals2 ... "Tests 2 min vals").min_values(2).multiple_values(true).required(false), + arg!(--maxvals3 ... "Tests 3 max vals").max_values(3).multiple_values(true).required(false), ]) .subcommand( App::new("subcmd") .about("tests subcommands") .version("0.1") .author("Kevin K. ") - .arg("-o --option [scoption]... 'tests options'") - .arg("[scpositional] 'tests positionals'"), + .arg(arg!(-o --option ... "tests options").required(false)) + .arg(arg!([scpositional] "tests positionals")) ) }}; } -pub fn build_from_usage(c: &mut Criterion) { - c.bench_function("build_from_usage", |b| b.iter(|| create_app!())); -} - pub fn build_from_builder(c: &mut Criterion) { c.bench_function("build_from_builder", |b| { b.iter(|| { @@ -291,7 +289,6 @@ pub fn parse_complex_with_sc_complex(c: &mut Criterion) { criterion_group!( benches, - build_from_usage, build_from_builder, parse_complex, parse_complex_with_flag, diff --git a/benches/04_new_help.rs b/benches/04_new_help.rs index f2f6e249..044ed490 100644 --- a/benches/04_new_help.rs +++ b/benches/04_new_help.rs @@ -1,5 +1,5 @@ use clap::App; -use clap::{Arg, ArgSettings}; +use clap::{arg, Arg, ArgSettings}; use criterion::{criterion_group, criterion_main, Criterion}; use std::io::Cursor; @@ -15,13 +15,18 @@ fn app_example1<'c>() -> App<'c> { .version("1.0") .author("Kevin K. ") .about("Does awesome things") - .arg("-c, --config=[FILE] 'Sets a custom config file'") - .arg(" 'Sets an optional output file'") - .arg("-d... 'Turn debugging information on'") + .arg( + arg!( + -c --config "Sets a custom config file" + ) + .required(false), + ) + .arg(arg!( "Sets an optional output file")) + .arg(arg!(d: -d ... "Turn debugging information on")) .subcommand( App::new("test") .about("does testing things") - .arg("-l, --list 'lists test values'"), + .arg(arg!(-l --list "lists test values")), ) } @@ -49,9 +54,14 @@ fn app_example3<'c>() -> App<'c> { .help("the input file to use") .setting(ArgSettings::Required), ]) - .arg("--license 'display the license file'") - .arg("[output] 'Supply an output file to use'") - .arg("-i, --int=[IFACE] 'Set an interface to use'") + .arg(arg!(--license "display the license file")) + .arg(arg!([output] "Supply an output file to use")) + .arg( + arg!( + -i --int "Set an interface to use" + ) + .required(false), + ) } fn app_example4<'c>() -> App<'c> { diff --git a/examples/01a_quick_example.rs b/examples/01a_quick_example.rs index b9e7888b..fad61f06 100644 --- a/examples/01a_quick_example.rs +++ b/examples/01a_quick_example.rs @@ -1,4 +1,4 @@ -use clap::App; +use clap::{arg, App}; fn main() { // This example shows how to create an application with several arguments using usage strings, which can be @@ -33,13 +33,20 @@ fn main() { .version("1.0") .author("Kevin K. ") .about("Does awesome things") - .arg("-c, --config=[FILE] 'Sets a custom config file'") - .arg("[output] 'Sets an optional output file'") - .arg("-d..., --debug... 'Turn debugging information on'") + .arg( + arg!( + -c --config "Sets a custom config file" + ) + .required(false), + ) + .arg(arg!([output] "Sets an optional output file")) + .arg(arg!( + -d --debug ... "Turn debugging information on" + )) .subcommand( App::new("test") .about("does testing things") - .arg("-l, --list 'lists test values'"), + .arg(arg!(-l --list "lists test values")), ) .get_matches(); diff --git a/examples/02_apps.rs b/examples/02_apps.rs index 03555317..c5e688cb 100644 --- a/examples/02_apps.rs +++ b/examples/02_apps.rs @@ -9,8 +9,8 @@ fn main() { // another option, usage(), which is an exception to the rule. This should only be used when // the default usage string automatically generated by clap doesn't suffice. // - // You also set all the valid arguments your App should accept via the arg(), args(), arg() - // and args_from_usage() (as well as subcommands via the subcommand() and subcommands() methods) which + // You also set all the valid arguments your App should accept via the arg() and args() + // (as well as subcommands via the subcommand() and subcommands() methods) which // will be covered later. // // Once all options have been set, call one of the .get_matches* family of methods in order to diff --git a/examples/03_args.rs b/examples/03_args.rs index 4d7e35b1..47b8b619 100644 --- a/examples/03_args.rs +++ b/examples/03_args.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg}; +use clap::{arg, App, Arg}; fn main() { // Args describe a possible valid argument which may be supplied by the user at runtime. There @@ -9,9 +9,8 @@ fn main() { // methods describing various settings for the individual arguments. Or by supplying a "usage" // string. Both methods have their pros and cons. // - // Arguments can be added to applications in two manners, one at a time with the arg(), and - // arg() method, or multiple arguments at once via a Vec inside the args() method, - // or a single &str describing multiple Args (one per line) supplied to args_from_usage(). + // Arguments can be added to applications in two manners, one at a time with the arg() + // method, or multiple arguments at once via a `&[Arg]` inside the args() method. // // There are various options which can be set for a given argument, some apply to any of the // three types of arguments, some only apply one or two of the types. *NOTE* if you set @@ -50,10 +49,15 @@ fn main() { // // // One "Flag" using a usage string - .arg("--license 'display the license file'") - // Two args, one "Positional", and one "Option" using a usage string - .arg("[output] 'Supply an output file to use'") - .arg("-i, --int=[IFACE] 'Set an interface to use'") + .arg(arg!(--license "display the license file")) + // Two args one Positional and one Option using a usage string + .arg(arg!([output] "Supply an output file to use")) + .arg( + arg!( + -i --int "Set an interface to use" + ) + .required(false), + ) .get_matches(); // Here are some examples of using the arguments defined above. Keep in mind that this is only diff --git a/examples/05_flag_args.rs b/examples/05_flag_args.rs index ef0eec2b..40df9455 100644 --- a/examples/05_flag_args.rs +++ b/examples/05_flag_args.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg}; +use clap::{arg, App, Arg}; fn main() { // Of the three argument types, flags are the most simple. Flags are simple switches which can @@ -29,8 +29,13 @@ fn main() { // also has a conflicts_with_all(Vec<&str>) // and an exclusive(true) ) - .arg("-c, --config=[FILE] 'sets a custom config file'") - .arg("[output] 'sets an output file'") + .arg( + arg!( + -c --config "sets a custom config file" + ) + .required(false), + ) + .arg(arg!([output] "sets an output file")) .get_matches(); // We can find out whether or not awesome was used diff --git a/examples/07_option_args.rs b/examples/07_option_args.rs index 6c12dea8..e19cebee 100644 --- a/examples/07_option_args.rs +++ b/examples/07_option_args.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg}; +use clap::{arg, App, Arg}; fn main() { // Option arguments are those that take an additional value, such as "-c value". In clap they @@ -33,8 +33,13 @@ fn main() { // also has a conflicts_with_all(Vec<&str>) // and an exclusive(true) ) - .arg("-c, --config=[FILE] 'the config file to use'") - .arg("[output] 'the output file to use'") + .arg( + arg!( + -c --config "the config file to use" + ) + .required(false), + ) + .arg(arg!([output] "the output file to use")) .get_matches(); // We can find out whether or not "input" was used diff --git a/examples/12_typed_values.rs b/examples/12_typed_values.rs index 211f4ac7..819a5636 100644 --- a/examples/12_typed_values.rs +++ b/examples/12_typed_values.rs @@ -1,4 +1,4 @@ -use clap::App; +use clap::{arg, App}; fn main() { // You can use some convenience methods provided by clap to get typed values, so long as the @@ -22,8 +22,15 @@ fn main() { let matches = App::new("myapp") // Create two arguments, a required positional which accepts multiple values // and an optional '-l value' - .arg("... 'A sequence of whole positive numbers, i.e. 20 25 30'") - .arg("-l [len] 'A length to use, defaults to 10 when omitted'") + .arg(arg!( + ... "A sequence of whole positive numbers, i.e. 20 25 30" + )) + .arg( + arg!( + l: -l "A length to use, defaults to 10 when omitted" + ) + .required(false), + ) .get_matches(); // This code loops through all the values provided to "seq" and adds 2 diff --git a/examples/13_enum_values.rs b/examples/13_enum_values.rs index 2ef5b1b1..d67acc8d 100644 --- a/examples/13_enum_values.rs +++ b/examples/13_enum_values.rs @@ -5,7 +5,7 @@ use std::str::FromStr; // Add clap like normal -use clap::{App, Arg}; +use clap::{arg, App}; // Define your enum enum Vals { @@ -35,7 +35,7 @@ fn main() { let m = App::new("myapp") // Use a single positional argument that is required .arg( - Arg::from(" 'The type to use'") + arg!( "The type to use") // Define the list of possible values .possible_values(["Foo", "Bar", "Baz", "Qux"]), ) diff --git a/examples/14_groups.rs b/examples/14_groups.rs index 12768f2b..738187e8 100644 --- a/examples/14_groups.rs +++ b/examples/14_groups.rs @@ -20,16 +20,16 @@ /// of the three numbers. So you create three flags `--major`, `--minor`, and `--patch`. All of /// these arguments shouldn't be used at one time but you want to specify that *at least one* of /// them is used. For this, you can create a group. -use clap::{App, Arg, ArgGroup}; +use clap::{arg, App, Arg, ArgGroup}; fn main() { // Create application like normal let matches = App::new("myapp") // Add the version arguments - .arg("--set-ver [ver] 'set version manually'") - .arg("--major 'auto inc major'") - .arg("--minor 'auto inc minor'") - .arg("--patch 'auto inc patch'") + .arg(arg!(--"set-ver" "set version manually").required(false)) + .arg(arg!(--major "auto inc major")) + .arg(arg!(--minor "auto inc minor")) + .arg(arg!(--patch "auto inc patch")) // Create a group, make it required, and add the above arguments .group( ArgGroup::new("vers") @@ -38,8 +38,12 @@ fn main() { ) // Arguments can also be added to a group individually, these two arguments // are part of the "input" group which is not required - .arg(Arg::from("[INPUT_FILE] 'some regular input'").group("input")) - .arg(Arg::from("--spec-in [SPEC_IN] 'some special input argument'").group("input")) + .arg(arg!([INPUT_FILE] "some regular input").group("input")) + .arg( + arg!(--"spec-in" "some special input argument") + .required(false) + .group("input"), + ) // Now let's assume we have a -c [config] argument which requires one of // (but **not** both) the "input" arguments .arg( diff --git a/examples/16_app_settings.rs b/examples/16_app_settings.rs index 4e5d799b..905a8830 100644 --- a/examples/16_app_settings.rs +++ b/examples/16_app_settings.rs @@ -1,4 +1,4 @@ -use clap::{App, AppSettings}; +use clap::{arg, App, AppSettings}; fn main() { // You can use AppSettings to change the application level behavior of clap. .setting() function @@ -11,7 +11,7 @@ fn main() { let matches = App::new("myapp") .setting(AppSettings::SubcommandsNegateReqs) // Negates requirement of parent command. - .arg(" 'input file to use'") + .arg(arg!( "input file to use")) // Required positional argument called input. This // will be only required if subcommand is not present. .subcommand(App::new("test").about("does some testing")) diff --git a/examples/17_yaml.rs b/examples/17_yaml.rs deleted file mode 100644 index ac0f92b4..00000000 --- a/examples/17_yaml.rs +++ /dev/null @@ -1,50 +0,0 @@ -// In order to use YAML to define your CLI you must compile clap with the "yaml" feature because -// it's **not** included by default. -// -// In order to do this, ensure your Cargo.toml looks like one of the following: -// -// [dependencies.clap] -// features = ["yaml"] -// -// __OR__ -// -// [dependencies] -// clap = { features = ["yaml"] } - -// Using yaml requires calling a clap macro `load_yaml!()`. -// Note: If you're using clap as a dependency and don't have a feature for your users called -// "yaml", you'll need to remove the #[cfg(feature = "yaml")] conditional compilation attribute -#[cfg(feature = "yaml")] -fn main() { - use clap::{load_yaml, App}; - - // To load a yaml file containing our CLI definition such as the example '17_yaml.yaml' we can - // use the convenience macro which loads the file at compile relative to the current file - // similar to how modules are found. - // - // Then we pass that yaml object to App to build the CLI. - // - // Finally we call get_matches() to start the parsing process. We use the matches just as we - // normally would - let yaml = load_yaml!("17_yaml.yaml"); - let m = App::from(yaml).get_matches(); - - // Because the example 17_yaml.yaml is rather large we'll just look a single arg so you can - // see that it works... - if let Some(mode) = m.value_of("mode") { - match mode { - "vi" => println!("You are using vi"), - "emacs" => println!("You are using emacs..."), - _ => unreachable!(), - } - } else { - println!("--mode wasn't used..."); - } -} - -#[cfg(not(feature = "yaml"))] -fn main() { - // As stated above, if clap is not compiled with the YAML feature, it is disabled. - println!("YAML feature is disabled."); - println!("Pass --features yaml to cargo when trying this example."); -} diff --git a/examples/17_yaml.yaml b/examples/17_yaml.yaml deleted file mode 100644 index d40ae6ca..00000000 --- a/examples/17_yaml.yaml +++ /dev/null @@ -1,98 +0,0 @@ -name: yaml_app -version: "1.0" -about: an example using a .yaml file to build a CLI -author: Kevin K. - -# AppSettings can be defined as a list and are **not** ascii case sensitive -settings: - - ArgRequiredElseHelp - -# All Args must be defined in the 'args:' list where the name of the arg, is the -# key to a Hash object -args: - # The name of this argument, is 'opt' which will be used to access the value - # later in your Rust code - - opt: - help: example option argument from yaml - short: o - long: option - multiple_occurrences: true - takes_value: true - - pos: - help: example positional argument from yaml - index: 1 - # A list of possible values can be defined as a list - possible_values: - - fast - - slow - - flag: - help: demo flag argument - short: F - multiple_occurrences: true - takes_value: true - global: true - # Conflicts, mutual overrides, and requirements can all be defined as a - # list, where the key is the name of the other argument - conflicts_with: - - opt - requires: - - pos - - mode: - long: mode - help: shows an option with specific values - # possible_values can also be defined in this list format - possible_values: [ vi, emacs ] - takes_value: true - - mvals: - long: mult-vals - help: demos an option which has two named values - # value names can be described in a list, where the help will be shown - # --mult-vals - value_names: - - one - - two - - minvals: - long: min-vals - multiple_values: true - help: you must supply at least two values to satisfy me - min_values: 2 - - maxvals: - long: max-vals - multiple_values: true - help: you can only supply a max of 3 values for me! - max_values: 3 - -# All subcommands must be listed in the 'subcommand:' object, where the key to -# the list is the name of the subcommand, and all settings for that command are -# are part of a Hash object -subcommands: - # The name of this subcommand will be 'subcmd' which can be accessed in your - # Rust code later - - subcmd: - about: demos subcommands from yaml - version: "0.1" - author: Kevin K. - # Subcommand args are exactly like App args - args: - - scopt: - short: B - multiple_occurrences: true - help: example subcommand option - takes_value: true - - scpos1: - help: example subcommand positional - index: 1 - -# ArgGroups are supported as well, and must be specified in the 'groups:' -# object of this file -groups: - # the name of the ArgGoup is specified here - - min-max-vals: - # All args and groups that are a part of this group are set here - args: - - minvals - - maxvals - # setting conflicts is done the same manner as setting 'args:' - # - # to make this group required, you could set 'required: true' but for - # this example we won't do that. diff --git a/src/build/app/mod.rs b/src/build/app/mod.rs index 4503ea5b..2d2ac65e 100644 --- a/src/build/app/mod.rs +++ b/src/build/app/mod.rs @@ -83,7 +83,7 @@ pub struct App<'help> { pub(crate) usage_str: Option<&'help str>, pub(crate) usage: Option, pub(crate) help_str: Option<&'help str>, - pub(crate) disp_ord: usize, + pub(crate) disp_ord: Option, pub(crate) term_w: Option, pub(crate) max_w: Option, pub(crate) template: Option<&'help str>, @@ -379,7 +379,6 @@ impl<'help> App<'help> { App { id: Id::from(&*name), name, - disp_ord: 999, ..Default::default() } .arg( @@ -398,11 +397,109 @@ impl<'help> App<'help> { ) } - /// Deprecated, see [`App::from`] + /// Deprecated in [Issue #9](https://github.com/epage/clapng/issues/9), maybe [`clap::Parser`][crate::Parser] would fit your use case? #[cfg(feature = "yaml")] - #[deprecated(since = "3.0.0", note = "Replaced with `App::from`")] - pub fn from_yaml(yaml: &'help Yaml) -> Self { - Self::from(yaml) + #[deprecated( + since = "3.0.0", + note = "Maybe clap::Parser would fit your use case? (Issue #9)" + )] + pub fn from_yaml(y: &'help Yaml) -> Self { + #![allow(deprecated)] + let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); + // We WANT this to panic on error...so expect() is good. + let (mut a, yaml, err) = if let Some(name) = y["name"].as_str() { + (App::new(name), yaml_file_hash, "app".into()) + } else { + let (name_yaml, value_yaml) = yaml_file_hash + .iter() + .next() + .expect("There must be one subcommand in the YAML file"); + let name_str = name_yaml + .as_str() + .expect("Subcommand name must be a string"); + + ( + App::new(name_str), + value_yaml.as_hash().expect("Subcommand must be a hash"), + format!("subcommand '{}'", name_str), + ) + }; + + let mut has_metadata = false; + + for (k, v) in yaml { + a = match k.as_str().expect("App fields must be strings") { + "_has_metadata" => { + has_metadata = true; + a + } + "bin_name" => yaml_to_str!(a, v, bin_name), + "version" => yaml_to_str!(a, v, version), + "long_version" => yaml_to_str!(a, v, long_version), + "author" => yaml_to_str!(a, v, author), + "about" => yaml_to_str!(a, v, about), + "before_help" => yaml_to_str!(a, v, before_help), + "before_long_help" => yaml_to_str!(a, v, before_long_help), + "after_help" => yaml_to_str!(a, v, after_help), + "after_long_help" => yaml_to_str!(a, v, after_long_help), + "help_heading" => yaml_to_str!(a, v, help_heading), + "help_template" => yaml_to_str!(a, v, help_template), + "override_help" => yaml_to_str!(a, v, override_help), + "override_usage" => yaml_to_str!(a, v, override_usage), + "alias" => yaml_to_str!(a, v, alias), + "aliases" => yaml_vec_or_str!(a, v, alias), + "visible_alias" => yaml_to_str!(a, v, visible_alias), + "visible_aliases" => yaml_vec_or_str!(a, v, visible_alias), + "display_order" => yaml_to_usize!(a, v, display_order), + "term_width" => yaml_to_usize!(a, v, term_width), + "max_term_width" => yaml_to_usize!(a, v, max_term_width), + "args" => { + if let Some(vec) = v.as_vec() { + for arg_yaml in vec { + a = a.arg(Arg::from_yaml(arg_yaml)); + } + } else { + panic!("Failed to convert YAML value {:?} to a vec", v); + } + a + } + "subcommands" => { + if let Some(vec) = v.as_vec() { + for sc_yaml in vec { + a = a.subcommand(App::from_yaml(sc_yaml)); + } + } else { + panic!("Failed to convert YAML value {:?} to a vec", v); + } + a + } + "groups" => { + if let Some(vec) = v.as_vec() { + for ag_yaml in vec { + a = a.group(ArgGroup::from(ag_yaml)); + } + } else { + panic!("Failed to convert YAML value {:?} to a vec", v); + } + a + } + "setting" | "settings" => { + yaml_to_setting!(a, v, setting, AppSettings, "AppSetting", err) + } + "global_setting" | "global_settings" => { + yaml_to_setting!(a, v, global_setting, AppSettings, "AppSetting", err) + } + "name" => continue, + s => { + if !has_metadata { + panic!("Unknown setting '{}' in YAML file for {}", s, err) + } + continue; + } + } + } + + a } /// Sets a string of author(s) that will be displayed to the user when they @@ -793,7 +890,7 @@ impl<'help> App<'help> { self } - /// Deprecated, see [`App::override_usage`] + /// Deprecated, replaced with [`App::override_usage`] #[deprecated(since = "3.0.0", note = "Replaced with `App::override_usage`")] pub fn usage>(self, usage: S) -> Self { self.override_usage(usage) @@ -839,7 +936,7 @@ impl<'help> App<'help> { self } - /// Deprecated, see [`App::override_help`] + /// Deprecated, replaced with [`App::override_help`] #[deprecated(since = "3.0.0", note = "Replaced with `App::override_help`")] pub fn help>(self, help: S) -> Self { self.override_help(help) @@ -893,7 +990,7 @@ impl<'help> App<'help> { self } - /// Deprecated, see [`App::help_template`] + /// Deprecated, replaced with [`App::help_template`] #[deprecated(since = "3.0.0", note = "Replaced with `App::help_template`")] pub fn template>(self, s: S) -> Self { self.help_template(s) @@ -1061,7 +1158,7 @@ impl<'help> App<'help> { self } - /// Deprecated, see [`App::term_width`] + /// Deprecated, replaced with [`App::term_width`] #[deprecated(since = "3.0.0", note = "Replaced with `App::term_width`")] pub fn set_term_width(self, width: usize) -> Self { self.term_width(width) @@ -1102,7 +1199,7 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; + /// # use clap::{App, arg, Arg}; /// App::new("myprog") /// // Adding a single "flag" argument with a short and help text, using Arg::new() /// .arg( @@ -1113,7 +1210,7 @@ impl<'help> App<'help> { /// // Adding a single "option" argument with a short, a long, and help text using the less /// // verbose Arg::from() /// .arg( - /// Arg::from("-c --config=[CONFIG] 'Optionally sets a config file to use'") + /// arg!(-c --config "Optionally sets a config file to use") /// ) /// # ; /// ``` @@ -1150,10 +1247,10 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; + /// # use clap::{App, arg, Arg}; /// App::new("myprog") /// .args(&[ - /// Arg::from("[debug] -d 'turns on debugging info'"), + /// arg!("[debug] -d 'turns on debugging info'"), /// Arg::new("input").index(1).help("the input file to use") /// ]) /// # ; @@ -1174,10 +1271,25 @@ impl<'help> App<'help> { self } - /// Deprecated, see [`App::arg`] - #[deprecated(since = "3.0.0", note = "Replaced with `App::arg`")] + /// Deprecated in [Issue #8](https://github.com/epage/clapng/issues/8), see [`arg!`][crate::arg!]. + #[deprecated(since = "3.0.0", note = "Replaced with `arg!`")] pub fn arg_from_usage(self, usage: &'help str) -> Self { - self.arg(usage) + #![allow(deprecated)] + self.arg(Arg::from_usage(usage)) + } + + /// Deprecated in [Issue #8](https://github.com/epage/clapng/issues/8), see [`arg!`][crate::arg!]. + #[deprecated(since = "3.0.0", note = "Replaced with `arg!`")] + pub fn args_from_usage(mut self, usage: &'help str) -> Self { + #![allow(deprecated)] + for line in usage.lines() { + let l = line.trim(); + if l.is_empty() { + continue; + } + self = self.arg(Arg::from_usage(l)); + } + self } /// If this `App` instance is a subcommand, this method adds an alias, which @@ -1615,12 +1727,12 @@ impl<'help> App<'help> { /// of the arguments from the specified group is present at runtime. /// /// ```no_run - /// # use clap::{App, ArgGroup}; + /// # use clap::{App, arg, ArgGroup}; /// App::new("app") - /// .arg("--set-ver [ver] 'set the version manually'") - /// .arg("--major 'auto increase major'") - /// .arg("--minor 'auto increase minor'") - /// .arg("--patch 'auto increase patch'") + /// .arg(arg!("--set-ver [ver] 'set the version manually'")) + /// .arg(arg!("--major 'auto increase major'")) + /// .arg(arg!("--minor 'auto increase minor'")) + /// .arg(arg!("--patch 'auto increase patch'")) /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) @@ -1637,14 +1749,14 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, ArgGroup}; + /// # use clap::{App, arg, ArgGroup}; /// App::new("app") - /// .arg("--set-ver [ver] 'set the version manually'") - /// .arg("--major 'auto increase major'") - /// .arg("--minor 'auto increase minor'") - /// .arg("--patch 'auto increase patch'") - /// .arg("-c [FILE] 'a config file'") - /// .arg("-i [IFACE] 'an interface'") + /// .arg(arg!("--set-ver [ver] 'set the version manually'")) + /// .arg(arg!("--major 'auto increase major'")) + /// .arg(arg!("--minor 'auto increase minor'")) + /// .arg(arg!("--patch 'auto increase patch'")) + /// .arg(arg!("-c [FILE] 'a config file'")) + /// .arg(arg!("-i [IFACE] 'an interface'")) /// .groups(&[ /// ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) @@ -1673,11 +1785,11 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; + /// # use clap::{App, arg}; /// App::new("myprog") /// .subcommand(App::new("config") /// .about("Controls configuration features") - /// .arg(" 'Required configuration file to use'")) + /// .arg(arg!(" 'Required configuration file to use'"))) /// # ; /// ``` #[inline] @@ -1760,7 +1872,7 @@ impl<'help> App<'help> { /// ``` #[inline] pub fn display_order(mut self, ord: usize) -> Self { - self.disp_ord = ord; + self.disp_ord = Some(ord); self } @@ -2066,7 +2178,7 @@ impl<'help> App<'help> { self.try_get_matches_from(&mut env::args_os()) } - /// Deprecated, see [`App::try_get_matches`] + /// Deprecated, replaced with [`App::try_get_matches`] #[deprecated(since = "3.0.0", note = "Replaced with `App::try_get_matches`")] pub fn get_matches_safe(self) -> ClapResult { self.try_get_matches() @@ -2142,7 +2254,7 @@ impl<'help> App<'help> { self.try_get_matches_from_mut(itr) } - /// Deprecated, see [`App::try_get_matches_from`] + /// Deprecated, replaced with [`App::try_get_matches_from`] #[deprecated(since = "3.0.0", note = "Replaced with `App::try_get_matches_from`")] pub fn get_matches_from_safe(self, itr: I) -> ClapResult where @@ -2251,7 +2363,7 @@ impl<'help> App<'help> { self._do_parse(&mut it) } - /// Deprecated, see [`App::try_get_matches_from_mut`] + /// Deprecated, replaced with [`App::try_get_matches_from_mut`] #[deprecated( since = "3.0.0", note = "Replaced with `App::try_get_matches_from_mut`" @@ -2654,19 +2766,19 @@ impl<'help> App<'help> { .args .args_mut() .filter(|a| !a.is_positional()) - .filter(|a| a.disp_ord == 999) + .filter(|a| a.get_display_order() == 999) .filter(|a| a.provider != ArgProvider::Generated) .enumerate() { - a.disp_ord = i; + a.disp_ord = Some(i); } for (i, mut sc) in &mut self .subcommands .iter_mut() .enumerate() - .filter(|&(_, ref sc)| sc.disp_ord == 999) + .filter(|&(_, ref sc)| sc.get_display_order() == 999) { - sc.disp_ord = i; + sc.disp_ord = Some(i); } } for sc in &mut self.subcommands { @@ -2955,6 +3067,10 @@ impl<'help> App<'help> { .find(|sc| sc.long_flag_aliases_to(long)) .map(|sc| sc.get_name()) } + + pub(crate) fn get_display_order(&self) -> usize { + self.disp_ord.unwrap_or(999) + } } impl<'help> Index<&'_ Id> for App<'help> { @@ -2965,108 +3081,6 @@ impl<'help> Index<&'_ Id> for App<'help> { } } -#[cfg(feature = "yaml")] -impl<'help> From<&'help Yaml> for App<'help> { - #[allow(clippy::cognitive_complexity)] - fn from(y: &'help Yaml) -> Self { - let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); - // We WANT this to panic on error...so expect() is good. - let (mut a, yaml, err) = if let Some(name) = y["name"].as_str() { - (App::new(name), yaml_file_hash, "app".into()) - } else { - let (name_yaml, value_yaml) = yaml_file_hash - .iter() - .next() - .expect("There must be one subcommand in the YAML file"); - let name_str = name_yaml - .as_str() - .expect("Subcommand name must be a string"); - - ( - App::new(name_str), - value_yaml.as_hash().expect("Subcommand must be a hash"), - format!("subcommand '{}'", name_str), - ) - }; - - let mut has_metadata = false; - - for (k, v) in yaml { - a = match k.as_str().expect("App fields must be strings") { - "_has_metadata" => { - has_metadata = true; - a - } - "bin_name" => yaml_to_str!(a, v, bin_name), - "version" => yaml_to_str!(a, v, version), - "long_version" => yaml_to_str!(a, v, long_version), - "author" => yaml_to_str!(a, v, author), - "about" => yaml_to_str!(a, v, about), - "before_help" => yaml_to_str!(a, v, before_help), - "before_long_help" => yaml_to_str!(a, v, before_long_help), - "after_help" => yaml_to_str!(a, v, after_help), - "after_long_help" => yaml_to_str!(a, v, after_long_help), - "help_heading" => yaml_to_str!(a, v, help_heading), - "help_template" => yaml_to_str!(a, v, help_template), - "override_help" => yaml_to_str!(a, v, override_help), - "override_usage" => yaml_to_str!(a, v, override_usage), - "alias" => yaml_to_str!(a, v, alias), - "aliases" => yaml_vec_or_str!(a, v, alias), - "visible_alias" => yaml_to_str!(a, v, visible_alias), - "visible_aliases" => yaml_vec_or_str!(a, v, visible_alias), - "display_order" => yaml_to_usize!(a, v, display_order), - "term_width" => yaml_to_usize!(a, v, term_width), - "max_term_width" => yaml_to_usize!(a, v, max_term_width), - "args" => { - if let Some(vec) = v.as_vec() { - for arg_yaml in vec { - a = a.arg(Arg::from(arg_yaml)); - } - } else { - panic!("Failed to convert YAML value {:?} to a vec", v); - } - a - } - "subcommands" => { - if let Some(vec) = v.as_vec() { - for sc_yaml in vec { - a = a.subcommand(App::from(sc_yaml)); - } - } else { - panic!("Failed to convert YAML value {:?} to a vec", v); - } - a - } - "groups" => { - if let Some(vec) = v.as_vec() { - for ag_yaml in vec { - a = a.group(ArgGroup::from(ag_yaml)); - } - } else { - panic!("Failed to convert YAML value {:?} to a vec", v); - } - a - } - "setting" | "settings" => { - yaml_to_setting!(a, v, setting, AppSettings, "AppSetting", err) - } - "global_setting" | "global_settings" => { - yaml_to_setting!(a, v, global_setting, AppSettings, "AppSetting", err) - } - "name" => continue, - s => { - if !has_metadata { - panic!("Unknown setting '{}' in YAML file for {}", s, err) - } - continue; - } - } - } - - a - } -} - impl fmt::Display for App<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) diff --git a/src/build/app/settings.rs b/src/build/app/settings.rs index 3c50e406..b9b805d2 100644 --- a/src/build/app/settings.rs +++ b/src/build/app/settings.rs @@ -519,11 +519,11 @@ pub enum AppSettings { #[deprecated(since = "3.0.0", note = "Replaced with `App::color`")] ColorAuto, - /// Deprecated, see [`App::color`][crate::App::color] + /// Deprecated, replaced with [`App::color`][crate::App::color] #[deprecated(since = "3.0.0", note = "Replaced with `App::color`")] ColorAlways, - /// Deprecated, see [`App::color`][crate::App::color] + /// Deprecated, replaced with [`App::color`][crate::App::color] #[deprecated(since = "3.0.0", note = "Replaced with `App::color`")] ColorNever, @@ -574,7 +574,7 @@ pub enum AppSettings { /// ``` DisableHelpFlag, - /// Deprecated, see [`AppSettings::DisableHelpFlag`] + /// Deprecated, replaced with [`AppSettings::DisableHelpFlag`] #[deprecated(since = "3.0.0", note = "Replaced with `AppSettings::DisableHelpFlag`")] DisableHelpFlags, @@ -615,7 +615,7 @@ pub enum AppSettings { /// ``` DisableVersionFlag, - /// Deprecated, see [`AppSettings::DisableVersionFlag`] + /// Deprecated, replaced with [`AppSettings::DisableVersionFlag`] #[deprecated( since = "3.0.0", note = "Replaced with `AppSettings::DisableVersionFlag`" @@ -743,7 +743,7 @@ pub enum AppSettings { /// [`subcommands`]: crate::App::subcommand() PropagateVersion, - /// Deprecated, see [`AppSettings::PropagateVersion`] + /// Deprecated, replaced with [`AppSettings::PropagateVersion`] #[deprecated( since = "3.0.0", note = "Replaced with `AppSettings::PropagateVersion`" @@ -819,12 +819,12 @@ pub enum AppSettings { /// avoided in many cases. /// /// ```rust - /// # use clap::{App, AppSettings}; + /// # use clap::{App, arg, AppSettings}; /// let app = App::new("app") /// .setting(AppSettings::IgnoreErrors) - /// .arg("-c, --config=[FILE] 'Sets a custom config file'") - /// .arg("-x, --stuff=[FILE] 'Sets a custom stuff file'") - /// .arg("-f 'Flag'"); + /// .arg(arg!(-c --config "Sets a custom config file").required(false)) + /// .arg(arg!(-x --stuff "Sets a custom stuff file").required(false)) + /// .arg(arg!(f: -f "Flag")); /// /// let r = app.try_get_matches_from(vec!["app", "-c", "file", "-f", "-x"]); /// @@ -883,10 +883,10 @@ pub enum AppSettings { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, AppSettings}; + /// # use clap::{App, arg, AppSettings}; /// let m = App::new("myprog") /// .setting(AppSettings::NoBinaryName) - /// .arg(Arg::from("... 'commands to run'")) + /// .arg(arg!( ... "commands to run")) /// .get_matches_from(vec!["command", "set"]); /// /// let cmds: Vec<&str> = m.values_of("cmd").unwrap().collect(); @@ -1028,10 +1028,10 @@ pub enum AppSettings { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, AppSettings}; + /// # use clap::{App, arg, AppSettings}; /// let m = App::new("myprog") /// .setting(AppSettings::TrailingVarArg) - /// .arg(Arg::from("... 'commands to run'")) + /// .arg(arg!( ... "commands to run")) /// .get_matches_from(vec!["myprog", "arg1", "-r", "val1"]); /// /// let trail: Vec<&str> = m.values_of("cmd").unwrap().collect(); diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs index 8b27fb1e..60ea7fe0 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg/mod.rs @@ -69,7 +69,7 @@ impl Default for ArgProvider { /// # Examples /// /// ```rust -/// # use clap::Arg; +/// # use clap::{Arg, arg}; /// // Using the traditional builder pattern and setting each option manually /// let cfg = Arg::new("config") /// .short('c') @@ -78,7 +78,7 @@ impl Default for ArgProvider { /// .value_name("FILE") /// .help("Provides a config file to myprog"); /// // Using a usage string (setting a similar argument to the one above) -/// let input = Arg::from("-i, --input=[FILE] 'Provides an input file to the program'"); +/// let input = arg!(-i --input "Provides an input file to the program"); /// ``` #[allow(missing_debug_implementations)] #[derive(Default, Clone)] @@ -101,7 +101,7 @@ pub struct Arg<'help> { pub(crate) long: Option<&'help str>, pub(crate) aliases: Vec<(&'help str, bool)>, // (name, visible) pub(crate) short_aliases: Vec<(char, bool)>, // (name, visible) - pub(crate) disp_ord: usize, + pub(crate) disp_ord: Option, pub(crate) possible_vals: Vec>, pub(crate) val_names: Vec<&'help str>, pub(crate) num_vals: Option, @@ -338,28 +338,154 @@ impl<'help> Arg<'help> { Arg { id: Id::from(&*name), name, - disp_ord: 999, ..Default::default() } } - /// Deprecated, see [`Arg::new`] + /// Deprecated, replaced with [`Arg::new`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::new`")] pub fn with_name>(n: S) -> Self { Self::new(n) } - /// Deprecated, see [`Arg::from`] + /// Deprecated in [Issue #9](https://github.com/epage/clapng/issues/9), maybe [`clap::Parser`][crate::Parser] would fit your use case? #[cfg(feature = "yaml")] - #[deprecated(since = "3.0.0", note = "Replaced with `Arg::from`")] + #[deprecated( + since = "3.0.0", + note = "Maybe clap::Parser would fit your use case? (Issue #9)" + )] pub fn from_yaml(y: &'help Yaml) -> Self { - Self::from(y) + let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); + // We WANT this to panic on error...so expect() is good. + let (name_yaml, yaml) = yaml_file_hash + .iter() + .next() + .expect("There must be one arg in the YAML file"); + let name_str = name_yaml.as_str().expect("Arg name must be a string"); + let mut a = Arg::new(name_str); + + let mut has_metadata = false; + + for (k, v) in yaml.as_hash().expect("Arg must be a hash") { + a = match k.as_str().expect("Arg fields must be strings") { + "_has_metadata" => { + has_metadata = true; + a + } + "short" => yaml_to_char!(a, v, short), + "long" => yaml_to_str!(a, v, long), + "alias" => yaml_to_str!(a, v, alias), + "aliases" => yaml_vec_or_str!(a, v, alias), + "short_alias" => yaml_to_str!(a, v, alias), + "short_aliases" => yaml_to_chars!(a, v, short_aliases), + "help" => yaml_to_str!(a, v, help), + "long_help" => yaml_to_str!(a, v, long_help), + "required" => yaml_to_bool!(a, v, required), + "required_if_eq" => yaml_tuple2!(a, v, required_if_eq), + "required_if_eq_any" => yaml_array_tuple2!(a, v, required_if_eq_any), + "required_if_eq_all" => yaml_array_tuple2!(a, v, required_if_eq_all), + "takes_value" => yaml_to_bool!(a, v, takes_value), + "index" => yaml_to_usize!(a, v, index), + "global" => yaml_to_bool!(a, v, global), + "multiple_occurrences" => yaml_to_bool!(a, v, multiple_occurrences), + "multiple_values" => yaml_to_bool!(a, v, multiple_values), + "hidden" => yaml_to_bool!(a, v, hidden), + "hidden_long_help" => yaml_to_bool!(a, v, hidden_long_help), + "hidden_short_help" => yaml_to_bool!(a, v, hidden_short_help), + "next_line_help" => yaml_to_bool!(a, v, next_line_help), + "group" => yaml_to_str!(a, v, group), + "number_of_values" => yaml_to_usize!(a, v, number_of_values), + "max_values" => yaml_to_usize!(a, v, max_values), + "min_values" => yaml_to_usize!(a, v, min_values), + "value_name" => yaml_to_str!(a, v, value_name), + "use_delimiter" => yaml_to_bool!(a, v, use_delimiter), + "allow_hyphen_values" => yaml_to_bool!(a, v, allow_hyphen_values), + "raw" => yaml_to_bool!(a, v, raw), + "require_equals" => yaml_to_bool!(a, v, require_equals), + "require_delimiter" => yaml_to_bool!(a, v, require_delimiter), + "value_terminator" => yaml_to_str!(a, v, value_terminator), + "value_delimiter" => yaml_to_char!(a, v, value_delimiter), + "required_unless_present" => yaml_to_str!(a, v, required_unless_present), + "display_order" => yaml_to_usize!(a, v, display_order), + "default_value" => yaml_to_str!(a, v, default_value), + "default_value_if" => yaml_tuple3!(a, v, default_value_if), + "default_value_ifs" => yaml_tuple3!(a, v, default_value_if), + "default_missing_value" => yaml_to_str!(a, v, default_missing_value), + #[cfg(feature = "env")] + "env" => yaml_to_str!(a, v, env), + "value_names" => yaml_vec_or_str!(a, v, value_name), + "groups" => yaml_vec_or_str!(a, v, group), + "requires" => yaml_vec_or_str!(a, v, requires), + "requires_if" => yaml_tuple2!(a, v, requires_if), + "requires_ifs" => yaml_tuple2!(a, v, requires_if), + "conflicts_with" => yaml_vec_or_str!(a, v, conflicts_with), + "exclusive" => yaml_to_bool!(a, v, exclusive), + "last" => yaml_to_bool!(a, v, last), + "help_heading" => yaml_to_str!(a, v, help_heading), + "value_hint" => yaml_str_parse!(a, v, value_hint), + "hide_default_value" => yaml_to_bool!(a, v, hide_default_value), + #[cfg(feature = "env")] + "hide_env" => yaml_to_bool!(a, v, hide_env), + #[cfg(feature = "env")] + "hide_env_values" => yaml_to_bool!(a, v, hide_env_values), + "hide_possible_values" => yaml_to_bool!(a, v, hide_possible_values), + "overrides_with" => yaml_to_str!(a, v, overrides_with), + "overrides_with_all" => yaml_vec_or_str!(a, v, overrides_with), + "possible_value" => yaml_to_str!(a, v, possible_value), + "possible_values" => yaml_vec_or_str!(a, v, possible_value), + "case_insensitive" => yaml_to_bool!(a, v, case_insensitive), + "required_unless_present_any" => yaml_vec!(a, v, required_unless_present_any), + "required_unless_present_all" => yaml_vec!(a, v, required_unless_present_all), + "visible_alias" => yaml_to_str!(a, v, visible_alias), + "visible_aliases" => yaml_vec_or_str!(a, v, visible_alias), + "visible_short_alias" => yaml_to_char!(a, v, visible_short_alias), + "visible_short_aliases" => yaml_to_chars!(a, v, visible_short_aliases), + #[cfg(feature = "regex")] + "validator_regex" => { + if let Some(vec) = v.as_vec() { + debug_assert_eq!(2, vec.len()); + let regex = yaml_str!(vec[0]); + + match Regex::new(regex) { + Err(e) => panic!( + "Failed to convert \"{}\" into regular expression: {}", + regex, e + ), + Ok(regex) => a.validator_regex(regex, yaml_str!(vec[1])), + } + } else { + panic!("Failed to convert YAML value to vector") + } + } + "setting" | "settings" => { + yaml_to_setting!( + a, + v, + setting, + ArgSettings, + "ArgSetting", + format!("arg '{}'", name_str) + ) + } + s => { + if !has_metadata { + panic!( + "Unknown setting '{}' in YAML file for arg '{}'", + s, name_str + ) + } + continue; + } + } + } + + a } - /// Deprecated, see [`Arg::from`] - #[deprecated(since = "3.0.0", note = "Replaced with `Arg::from`")] + /// Deprecated in [Issue #8](https://github.com/epage/clapng/issues/8), see [`arg!`][crate::arg!]. + #[deprecated(since = "3.0.0", note = "Replaced with `arg!`")] pub fn from_usage(u: &'help str) -> Self { - Self::from(u) + UsageParser::from_usage(u).parse() } pub(crate) fn generated(mut self) -> Self { @@ -367,6 +493,17 @@ impl<'help> Arg<'help> { self } + /// Set the identifier used for referencing this argument in the clap API. + /// + /// **NOTE:** This will shown to the user in usage/help if no [`value_name`][Arg::value_name] + /// is provided. + pub fn name>(mut self, n: S) -> Self { + let name = n.into(); + self.id = Id::from(&*name); + self.name = name; + self + } + /// Sets the short version of the argument without the preceding `-`. /// /// By default `clap` automatically assigns `V` and `h` to the auto-generated `version` and @@ -834,7 +971,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::required_unless_present`] + /// Deprecated, replaced with [`Arg::required_unless_present`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::required_unless_present`")] pub fn required_unless(self, arg_id: T) -> Self { self.required_unless_present(arg_id) @@ -914,7 +1051,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::required_unless_present_all`] + /// Deprecated, replaced with [`Arg::required_unless_present_all`] #[deprecated( since = "3.0.0", note = "Replaced with `Arg::required_unless_present_all`" @@ -1003,7 +1140,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::required_unless_present_any`] + /// Deprecated, replaced with [`Arg::required_unless_present_any`] #[deprecated( since = "3.0.0", note = "Replaced with `Arg::required_unless_present_any`" @@ -1180,12 +1317,12 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust # use clap::{App, Arg}; - /// # use clap::{App, Arg}; + /// # use clap::{App, arg}; /// let m = App::new("prog") - /// .arg(Arg::from("-f, --flag 'some flag'") + /// .arg(arg!(-f --flag "some flag") /// .conflicts_with("debug")) - /// .arg(Arg::from("-d, --debug 'other flag'")) - /// .arg(Arg::from("-c, --color 'third flag'") + /// .arg(arg!(-d --debug "other flag")) + /// .arg(arg!(-c --color "third flag") /// .overrides_with("flag")) /// .get_matches_from(vec![ /// "prog", "-f", "-d", "-c"]); @@ -1205,9 +1342,9 @@ impl<'help> Arg<'help> { /// preventing a "Unexpected multiple usage" error): /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{App, arg}; /// let m = App::new("posix") - /// .arg(Arg::from("--flag 'some flag'").overrides_with("flag")) + /// .arg(arg!(--flag "some flag").overrides_with("flag")) /// .get_matches_from(vec!["posix", "--flag", "--flag"]); /// assert!(m.is_present("flag")); /// assert_eq!(m.occurrences_of("flag"), 1); @@ -1218,9 +1355,9 @@ impl<'help> Arg<'help> { /// if it's a flag and it already accepts multiple occurrences. /// /// ``` - /// # use clap::{App, Arg}; + /// # use clap::{App, arg}; /// let m = App::new("posix") - /// .arg(Arg::from("--flag... 'some flag'").overrides_with("flag")) + /// .arg(arg!(--flag ... "some flag").overrides_with("flag")) /// .get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); /// assert!(m.is_present("flag")); /// assert_eq!(m.occurrences_of("flag"), 4); @@ -1231,9 +1368,9 @@ impl<'help> Arg<'help> { /// occurrence happened. /// /// ``` - /// # use clap::{App, Arg}; + /// # use clap::{App, arg}; /// let m = App::new("posix") - /// .arg(Arg::from("--opt [val] 'some option'").overrides_with("opt")) + /// .arg(arg!(--opt "some option").overrides_with("opt")) /// .get_matches_from(vec!["", "--opt=some", "--opt=other"]); /// assert!(m.is_present("opt")); /// assert_eq!(m.occurrences_of("opt"), 1); @@ -1262,9 +1399,10 @@ impl<'help> Arg<'help> { /// will ignore the "override self" setting. /// /// ``` - /// # use clap::{App, Arg}; + /// # use clap::{App, arg}; /// let m = App::new("posix") - /// .arg(Arg::from("[opt]... --opt [val]... 'some option'") + /// .arg(arg!(--opt ... "some option") + /// .multiple_values(true) /// .overrides_with("opt")) /// .get_matches_from(vec!["", "--opt", "first", "over", "--opt", "other", "val"]); /// assert!(m.is_present("opt")); @@ -1286,12 +1424,12 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{App, arg}; /// let m = App::new("prog") - /// .arg(Arg::from("-f, --flag 'some flag'") + /// .arg(arg!(-f --flag "some flag") /// .conflicts_with("color")) - /// .arg(Arg::from("-d, --debug 'other flag'")) - /// .arg(Arg::from("-c, --color 'third flag'") + /// .arg(arg!(-d --debug "other flag")) + /// .arg(arg!(-c --color "third flag") /// .overrides_with_all(&["flag", "debug"])) /// .get_matches_from(vec![ /// "prog", "-f", "-d", "-c"]); @@ -1583,7 +1721,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::required_if_eq`] + /// Deprecated, replaced with [`Arg::required_if_eq`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::required_if_eq`")] pub fn required_if(self, arg_id: T, val: &'help str) -> Self { self.required_if_eq(arg_id, val) @@ -1675,7 +1813,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::required_if_eq_any`] + /// Deprecated, replaced with [`Arg::required_if_eq_any`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::required_if_eq_any`")] pub fn required_ifs(self, ifs: &[(T, &'help str)]) -> Self { self.required_if_eq_any(ifs) @@ -3382,7 +3520,7 @@ impl<'help> Arg<'help> { /// [index]: Arg::index() #[inline] pub fn display_order(mut self, ord: usize) -> Self { - self.disp_ord = ord; + self.disp_ord = Some(ord); self } @@ -4269,7 +4407,7 @@ impl<'help> Arg<'help> { } } - /// Deprecated, see [`Arg::forbid_empty_values`] + /// Deprecated, replaced with [`Arg::forbid_empty_values`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::forbid_empty_values`")] pub fn empty_values(self, empty: bool) -> Self { self.forbid_empty_values(!empty) @@ -4514,7 +4652,7 @@ impl<'help> Arg<'help> { } } - /// Deprecated, see [`Arg::multiple_occurrences`] (most likely what you want) and + /// Deprecated, replaced with [`Arg::multiple_occurrences`] (most likely what you want) and /// [`Arg::multiple_values`] #[deprecated( since = "3.0.0", @@ -4745,7 +4883,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::setting`] + /// Deprecated, replaced with [`Arg::setting`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::setting`")] pub fn set(self, s: ArgSettings) -> Self { self.setting(s) @@ -4780,7 +4918,7 @@ impl<'help> Arg<'help> { self } - /// Deprecated, see [`Arg::unset_setting`] + /// Deprecated, replaced with [`Arg::unset_setting`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::unset_setting`")] pub fn unset(self, s: ArgSettings) -> Self { self.unset_setting(s) @@ -4903,146 +5041,9 @@ impl<'help> Arg<'help> { pub(crate) fn is_multiple(&self) -> bool { self.is_set(ArgSettings::MultipleValues) | self.is_set(ArgSettings::MultipleOccurrences) } -} -#[cfg(feature = "yaml")] -impl<'help> From<&'help Yaml> for Arg<'help> { - /// Creates a new instance of [`Arg`] from a .yaml (YAML) file. - /// - /// # Examples - /// - /// ```ignore - /// use clap::{Arg, load_yaml}; - /// let yaml = load_yaml!("arg.yaml"); - /// let arg = Arg::from(yaml); - /// ``` - #[allow(clippy::cognitive_complexity)] - fn from(y: &'help Yaml) -> Self { - let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); - // We WANT this to panic on error...so expect() is good. - let (name_yaml, yaml) = yaml_file_hash - .iter() - .next() - .expect("There must be one arg in the YAML file"); - let name_str = name_yaml.as_str().expect("Arg name must be a string"); - let mut a = Arg::new(name_str); - - let mut has_metadata = false; - - for (k, v) in yaml.as_hash().expect("Arg must be a hash") { - a = match k.as_str().expect("Arg fields must be strings") { - "_has_metadata" => { - has_metadata = true; - a - } - "short" => yaml_to_char!(a, v, short), - "long" => yaml_to_str!(a, v, long), - "alias" => yaml_to_str!(a, v, alias), - "aliases" => yaml_vec_or_str!(a, v, alias), - "short_alias" => yaml_to_str!(a, v, alias), - "short_aliases" => yaml_to_chars!(a, v, short_aliases), - "help" => yaml_to_str!(a, v, help), - "long_help" => yaml_to_str!(a, v, long_help), - "required" => yaml_to_bool!(a, v, required), - "required_if_eq" => yaml_tuple2!(a, v, required_if_eq), - "required_if_eq_any" => yaml_array_tuple2!(a, v, required_if_eq_any), - "required_if_eq_all" => yaml_array_tuple2!(a, v, required_if_eq_all), - "takes_value" => yaml_to_bool!(a, v, takes_value), - "index" => yaml_to_usize!(a, v, index), - "global" => yaml_to_bool!(a, v, global), - "multiple_occurrences" => yaml_to_bool!(a, v, multiple_occurrences), - "multiple_values" => yaml_to_bool!(a, v, multiple_values), - "hidden" => yaml_to_bool!(a, v, hidden), - "hidden_long_help" => yaml_to_bool!(a, v, hidden_long_help), - "hidden_short_help" => yaml_to_bool!(a, v, hidden_short_help), - "next_line_help" => yaml_to_bool!(a, v, next_line_help), - "group" => yaml_to_str!(a, v, group), - "number_of_values" => yaml_to_usize!(a, v, number_of_values), - "max_values" => yaml_to_usize!(a, v, max_values), - "min_values" => yaml_to_usize!(a, v, min_values), - "value_name" => yaml_to_str!(a, v, value_name), - "use_delimiter" => yaml_to_bool!(a, v, use_delimiter), - "allow_hyphen_values" => yaml_to_bool!(a, v, allow_hyphen_values), - "raw" => yaml_to_bool!(a, v, raw), - "require_equals" => yaml_to_bool!(a, v, require_equals), - "require_delimiter" => yaml_to_bool!(a, v, require_delimiter), - "value_terminator" => yaml_to_str!(a, v, value_terminator), - "value_delimiter" => yaml_to_char!(a, v, value_delimiter), - "required_unless_present" => yaml_to_str!(a, v, required_unless_present), - "display_order" => yaml_to_usize!(a, v, display_order), - "default_value" => yaml_to_str!(a, v, default_value), - "default_value_if" => yaml_tuple3!(a, v, default_value_if), - "default_value_ifs" => yaml_tuple3!(a, v, default_value_if), - "default_missing_value" => yaml_to_str!(a, v, default_missing_value), - #[cfg(feature = "env")] - "env" => yaml_to_str!(a, v, env), - "value_names" => yaml_vec_or_str!(a, v, value_name), - "groups" => yaml_vec_or_str!(a, v, group), - "requires" => yaml_vec_or_str!(a, v, requires), - "requires_if" => yaml_tuple2!(a, v, requires_if), - "requires_ifs" => yaml_tuple2!(a, v, requires_if), - "conflicts_with" => yaml_vec_or_str!(a, v, conflicts_with), - "exclusive" => yaml_to_bool!(a, v, exclusive), - "last" => yaml_to_bool!(a, v, last), - "help_heading" => yaml_to_str!(a, v, help_heading), - "value_hint" => yaml_str_parse!(a, v, value_hint), - "hide_default_value" => yaml_to_bool!(a, v, hide_default_value), - #[cfg(feature = "env")] - "hide_env" => yaml_to_bool!(a, v, hide_env), - #[cfg(feature = "env")] - "hide_env_values" => yaml_to_bool!(a, v, hide_env_values), - "hide_possible_values" => yaml_to_bool!(a, v, hide_possible_values), - "overrides_with" => yaml_to_str!(a, v, overrides_with), - "overrides_with_all" => yaml_vec_or_str!(a, v, overrides_with), - "possible_value" => yaml_to_str!(a, v, possible_value), - "possible_values" => yaml_vec_or_str!(a, v, possible_value), - "case_insensitive" => yaml_to_bool!(a, v, case_insensitive), - "required_unless_present_any" => yaml_vec!(a, v, required_unless_present_any), - "required_unless_present_all" => yaml_vec!(a, v, required_unless_present_all), - "visible_alias" => yaml_to_str!(a, v, visible_alias), - "visible_aliases" => yaml_vec_or_str!(a, v, visible_alias), - "visible_short_alias" => yaml_to_char!(a, v, visible_short_alias), - "visible_short_aliases" => yaml_to_chars!(a, v, visible_short_aliases), - #[cfg(feature = "regex")] - "validator_regex" => { - if let Some(vec) = v.as_vec() { - debug_assert_eq!(2, vec.len()); - let regex = yaml_str!(vec[0]); - - match Regex::new(regex) { - Err(e) => panic!( - "Failed to convert \"{}\" into regular expression: {}", - regex, e - ), - Ok(regex) => a.validator_regex(regex, yaml_str!(vec[1])), - } - } else { - panic!("Failed to convert YAML value to vector") - } - } - "setting" | "settings" => { - yaml_to_setting!( - a, - v, - setting, - ArgSettings, - "ArgSetting", - format!("arg '{}'", name_str) - ) - } - s => { - if !has_metadata { - panic!( - "Unknown setting '{}' in YAML file for arg '{}'", - s, name_str - ) - } - continue; - } - } - } - - a + pub(crate) fn get_display_order(&self) -> usize { + self.disp_ord.unwrap_or(999) } } @@ -5052,12 +5053,6 @@ impl<'help> From<&'_ Arg<'help>> for Arg<'help> { } } -impl<'help> From<&'help str> for Arg<'help> { - fn from(s: &'help str) -> Self { - UsageParser::from_usage(s).parse() - } -} - impl<'help> PartialEq for Arg<'help> { fn eq(&self, other: &Arg<'help>) -> bool { self.name == other.name diff --git a/src/build/arg/settings.rs b/src/build/arg/settings.rs index 65df5ddb..dcdf5b9d 100644 --- a/src/build/arg/settings.rs +++ b/src/build/arg/settings.rs @@ -130,7 +130,7 @@ pub enum ArgSettings { HideDefaultValue, /// Possible values become case insensitive IgnoreCase, - /// Deprecated, see [`ArgSettings::IgnoreCase`] + /// Deprecated, replaced with [`ArgSettings::IgnoreCase`] #[deprecated(since = "3.0.0", note = "Replaced with `ArgSettings::IgnoreCase`")] CaseInsensitive, /// Hides environment variable arguments from the help message diff --git a/src/build/arg/tests.rs b/src/build/arg/tests.rs index 520572bf..6a6506e4 100644 --- a/src/build/arg/tests.rs +++ b/src/build/arg/tests.rs @@ -1,28 +1,4 @@ -use super::{settings::ArgSettings, Arg}; - -#[test] -fn short_flag_misspell() { - let a = Arg::from("-f1, --flag 'some flag'"); - assert_eq!(a.name, "flag"); - assert_eq!(a.short.unwrap(), 'f'); - assert_eq!(a.long.unwrap(), "flag"); - assert_eq!(a.help.unwrap(), "some flag"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.val_names.is_empty()); - assert!(a.num_vals.is_none()); -} - -#[test] -fn short_flag_name_missing() { - let a = Arg::from("-f 'some flag'"); - assert_eq!(a.name, "f"); - assert_eq!(a.short.unwrap(), 'f'); - assert!(a.long.is_none()); - assert_eq!(a.help.unwrap(), "some flag"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.val_names.is_empty()); - assert!(a.num_vals.is_none()); -} +use crate::Arg; // This test will *fail to compile* if Arg is not Send + Sync #[test] diff --git a/src/build/arg_group.rs b/src/build/arg_group.rs index 809441c5..71c9c0ac 100644 --- a/src/build/arg_group.rs +++ b/src/build/arg_group.rs @@ -36,12 +36,12 @@ use yaml_rust::Yaml; /// the arguments from the specified group is present at runtime. /// /// ```rust -/// # use clap::{App, ArgGroup, ErrorKind}; +/// # use clap::{App, arg, ArgGroup, ErrorKind}; /// let result = App::new("app") -/// .arg("--set-ver [ver] 'set the version manually'") -/// .arg("--major 'auto increase major'") -/// .arg("--minor 'auto increase minor'") -/// .arg("--patch 'auto increase patch'") +/// .arg(arg!(--"set-ver" "set the version manually").required(false)) +/// .arg(arg!(--major "auto increase major")) +/// .arg(arg!(--minor "auto increase minor")) +/// .arg(arg!(--patch "auto increase patch")) /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor", "patch"]) /// .required(true)) @@ -54,12 +54,12 @@ use yaml_rust::Yaml; /// This next example shows a passing parse of the same scenario /// /// ```rust -/// # use clap::{App, ArgGroup}; +/// # use clap::{App, arg, ArgGroup}; /// let result = App::new("app") -/// .arg("--set-ver [ver] 'set the version manually'") -/// .arg("--major 'auto increase major'") -/// .arg("--minor 'auto increase minor'") -/// .arg("--patch 'auto increase patch'") +/// .arg(arg!(--"set-ver" "set the version manually").required(false)) +/// .arg(arg!(--major "auto increase major")) +/// .arg(arg!(--minor "auto increase minor")) +/// .arg(arg!(--patch "auto increase patch")) /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) @@ -108,15 +108,18 @@ impl<'help> ArgGroup<'help> { ArgGroup::default().name(n) } - /// Deprecated, see [`ArgGroup::new`] + /// Deprecated, replaced with [`ArgGroup::new`] #[deprecated(since = "3.0.0", note = "Replaced with `ArgGroup::new`")] pub fn with_name>(n: S) -> Self { Self::new(n) } - /// Deprecated, see [`ArgGroup::from`] + /// Deprecated in [Issue #9](https://github.com/epage/clapng/issues/9), maybe [`clap::Parser`][crate::Parser] would fit your use case? #[cfg(feature = "yaml")] - #[deprecated(since = "3.0.0", note = "Replaced with `ArgGroup::from`")] + #[deprecated( + since = "3.0.0", + note = "Maybe clap::Parser would fit your use case? (Issue #9)" + )] pub fn from_yaml(yaml: &'help Yaml) -> Self { Self::from(yaml) } @@ -436,17 +439,10 @@ impl<'help> From<&'_ ArgGroup<'help>> for ArgGroup<'help> { } } +/// Deprecated in [Issue #9](https://github.com/epage/clapng/issues/9), maybe [`clap::Parser`][crate::Parser] would fit your use case? #[cfg(feature = "yaml")] impl<'help> From<&'help Yaml> for ArgGroup<'help> { - /// Creates a new instance of `ArgGroup` from a .yaml (YAML) file. - /// - /// # Examples - /// - /// ```ignore - /// # use clap::{ArgGroup, load_yaml}; - /// let yaml = load_yaml!("group.yaml"); - /// let ag = ArgGroup::from(yaml); - /// ``` + /// Deprecated in [Issue #9](https://github.com/epage/clapng/issues/9), maybe [`clap::Parser`][crate::Parser] would fit your use case? fn from(y: &'help Yaml) -> Self { let b = y.as_hash().expect("ArgGroup::from:: expects a table"); // We WANT this to panic on error...so expect() is good. diff --git a/src/build/usage_parser.rs b/src/build/usage_parser.rs index b4e5017e..ec868be3 100644 --- a/src/build/usage_parser.rs +++ b/src/build/usage_parser.rs @@ -44,10 +44,7 @@ impl<'help> UsageParser<'help> { pub(crate) fn parse(mut self) -> Arg<'help> { debug!("UsageParser::parse"); - let mut arg = Arg { - disp_ord: 999, - ..Default::default() - }; + let mut arg = Arg::default(); loop { debug!("UsageParser::parse:iter: pos={}", self.pos); self.stop_at(token); @@ -173,12 +170,11 @@ impl<'help> UsageParser<'help> { self.pos += 1; if dot_counter == 3 { debug!("UsageParser::multiple: setting multiple"); + arg.settings.set(ArgSettings::MultipleOccurrences); if arg.is_set(ArgSettings::TakesValue) { - // This is after `--name=value`, so requesting multiple value arg.settings.set(ArgSettings::MultipleValues); - } else { - // This is after `[name]` (or a flag), so requesting multiple occurrences - arg.settings.set(ArgSettings::MultipleOccurrences); + arg.settings.set(ArgSettings::UseValueDelimiter); + arg.val_delim.get_or_insert(','); } self.prev = UsageToken::Multiple; self.pos += 1; @@ -245,12 +241,14 @@ fn default_value_end(b: u8) -> bool { #[cfg(test)] mod test { + #![allow(deprecated)] + use crate::build::{Arg, ArgSettings}; #[allow(clippy::cognitive_complexity)] #[test] fn create_flag_usage() { - let a = Arg::from("[flag] -f 'some help info'"); + let a = Arg::from_usage("[flag] -f 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); @@ -258,7 +256,7 @@ mod test { assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("[flag] --flag 'some help info'"); + let a = Arg::from_usage("[flag] --flag 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert!(a.short.is_none()); @@ -266,7 +264,7 @@ mod test { assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("--flag 'some help info'"); + let a = Arg::from_usage("--flag 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert!(a.short.is_none()); @@ -274,7 +272,7 @@ mod test { assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("[flag] -f --flag 'some help info'"); + let a = Arg::from_usage("[flag] -f --flag 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.long.unwrap(), "flag"); @@ -282,7 +280,7 @@ mod test { assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("[flag] -f... 'some help info'"); + let a = Arg::from_usage("[flag] -f... 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); @@ -290,7 +288,7 @@ mod test { assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("[flag] -f --flag... 'some help info'"); + let a = Arg::from_usage("[flag] -f --flag... 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.short.unwrap(), 'f'); @@ -298,7 +296,7 @@ mod test { assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("-f --flag... 'some help info'"); + let a = Arg::from_usage("-f --flag... 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.short.unwrap(), 'f'); @@ -306,29 +304,29 @@ mod test { assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("--flags"); + let a = Arg::from_usage("--flags"); assert_eq!(a.name, "flags"); assert_eq!(a.long.unwrap(), "flags"); assert!(a.val_names.is_empty()); - let a = Arg::from("--flags..."); + let a = Arg::from_usage("--flags..."); assert_eq!(a.name, "flags"); assert_eq!(a.long.unwrap(), "flags"); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("[flags] -f"); + let a = Arg::from_usage("[flags] -f"); assert_eq!(a.name, "flags"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.val_names.is_empty()); - let a = Arg::from("[flags] -f..."); + let a = Arg::from_usage("[flags] -f..."); assert_eq!(a.name, "flags"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("-f 'some help info'"); + let a = Arg::from_usage("-f 'some help info'"); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); @@ -336,12 +334,12 @@ mod test { assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_empty()); - let a = Arg::from("-f"); + let a = Arg::from_usage("-f"); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.val_names.is_empty()); - let a = Arg::from("-f..."); + let a = Arg::from_usage("-f..."); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.is_set(ArgSettings::MultipleOccurrences)); @@ -351,7 +349,7 @@ mod test { #[test] fn create_option_usage0() { // Short only - let a = Arg::from("[option] -o [opt] 'some help info'"); + let a = Arg::from_usage("[option] -o [opt] 'some help info'"); assert_eq!(a.name, "option"); assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); @@ -365,7 +363,7 @@ mod test { #[test] fn create_option_usage1() { - let a = Arg::from("-o [opt] 'some help info'"); + let a = Arg::from_usage("-o [opt] 'some help info'"); assert_eq!(a.name, "o"); assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); @@ -379,7 +377,7 @@ mod test { #[test] fn create_option_usage2() { - let a = Arg::from("