From 88fff13e715507391e836e124f66ce78fd9be2f3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 18 Nov 2021 14:17:31 -0600 Subject: [PATCH 01/13] Revert rename of from_yaml / from_usage Since usage parser and yaml are on the way to being deprecated (#8, #9), doing a rename also seems excessive, so rolling it back. Past relevant PRs: - clap-rs/clap#1157 - clap-rs/clap#1257 --- README.md | 10 +- benches/02_simple.rs | 14 +- benches/03_complex.rs | 30 ++- benches/04_new_help.rs | 18 +- examples/01a_quick_example.rs | 14 +- examples/03_args.rs | 8 +- examples/05_flag_args.rs | 6 +- examples/07_option_args.rs | 6 +- examples/12_typed_values.rs | 10 +- examples/13_enum_values.rs | 2 +- examples/14_groups.rs | 12 +- examples/16_app_settings.rs | 4 +- examples/17_yaml.rs | 2 +- src/build/app/mod.rs | 289 +++++++++++--------- src/build/app/settings.rs | 12 +- src/build/arg/mod.rs | 441 +++++++++++++++++++------------ src/build/arg/tests.rs | 4 +- src/build/arg_group.rs | 30 +-- src/build/usage_parser.rs | 170 ++++++------ src/lib.rs | 5 +- src/parse/errors.rs | 2 +- src/parse/matches/arg_matches.rs | 30 +-- tests/app_settings.rs | 104 ++++---- tests/arg_aliases.rs | 2 +- tests/arg_aliases_short.rs | 2 +- tests/conflicts.rs | 36 +-- tests/default_missing_vals.rs | 4 +- tests/default_vals.rs | 130 ++++----- tests/env.rs | 32 +-- tests/flag_subcommands.rs | 8 +- tests/flags.rs | 26 +- tests/global_args.rs | 8 +- tests/groups.rs | 56 ++-- tests/help.rs | 46 ++-- tests/hidden_args.rs | 8 +- tests/ignore_errors.rs | 28 +- tests/multiple_occurrences.rs | 16 +- tests/multiple_values.rs | 2 +- tests/opts.rs | 72 ++--- tests/positionals.rs | 60 ++--- tests/posix_compatible.rs | 94 +++---- tests/require.rs | 74 +++--- tests/subcommands.rs | 8 +- tests/template_help.rs | 16 +- tests/utf16.rs | 24 +- tests/utils.rs | 36 +-- tests/yaml.rs | 58 ++-- 47 files changed, 1137 insertions(+), 932 deletions(-) diff --git a/README.md b/README.md index 350996fb..275d7134 100644 --- a/README.md +++ b/README.md @@ -283,21 +283,21 @@ 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::from_usage("-c, --config=[FILE] 'Sets a custom config file'")) + .arg(Arg::from_usage(" 'Sets the input file to use'")) + .arg(Arg::from_usage("-v... '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::from_usage("-d, --debug 'Print debug information'"))) .get_matches(); // Same as previous example... diff --git a/benches/02_simple.rs b/benches/02_simple.rs index 77e83b2b..1a691754 100644 --- a/benches/02_simple.rs +++ b/benches/02_simple.rs @@ -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::from_usage("-f --flag 'tests flags'")) + .arg(Arg::from_usage("-o --option=[opt] 'tests options'")) + .arg(Arg::from_usage("[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::from_usage("-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::from_usage("-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::from_usage("-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::from_usage("-s, --some 'something'"); App::new("claptests").arg(&arg) }) }); diff --git a/benches/03_complex.rs b/benches/03_complex.rs index b149c2ad..0dc1043e 100644 --- a/benches/03_complex.rs +++ b/benches/03_complex.rs @@ -10,35 +10,37 @@ 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::from_usage("-o --option=[opt]... 'tests options'")) + .arg(Arg::from_usage("[positional] 'tests positionals'")) + .arg(Arg::from_usage("-f --flag... 'tests flags'").global(true)) .args(&[ - Arg::from("[flag2] -F 'tests flags with exclusions'") + Arg::from_usage("[flag2] -F 'tests flags with exclusions'") .conflicts_with("flag") .requires("option2"), - Arg::from( + Arg::from_usage( "[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::from_usage("[positional2] 'tests positionals with exclusions'"), + Arg::from_usage("-O --Option [option3] 'tests options with specific value sets'") .possible_values(OPT3_VALS), - Arg::from("[positional3]... 'tests positionals with specific values'") + Arg::from_usage("[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::from_usage("--multvals [one] [two] 'Tests multiple values, not mult occs'"), + Arg::from_usage( + "--multvalsmo... [one] [two] 'Tests multiple values, not mult occs'", + ), + Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), + Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3), ]) .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::from_usage("-o --option [scoption]... 'tests options'")) + .arg(Arg::from_usage("[scpositional] 'tests positionals'")), ) }}; } diff --git a/benches/04_new_help.rs b/benches/04_new_help.rs index f2f6e249..a0e65d06 100644 --- a/benches/04_new_help.rs +++ b/benches/04_new_help.rs @@ -15,13 +15,15 @@ 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::from_usage( + "-c, --config=[FILE] 'Sets a custom config file'", + )) + .arg(Arg::from_usage(" 'Sets an optional output file'")) + .arg(Arg::from_usage("-d... 'Turn debugging information on'")) .subcommand( App::new("test") .about("does testing things") - .arg("-l, --list 'lists test values'"), + .arg(Arg::from_usage("-l, --list 'lists test values'")), ) } @@ -49,9 +51,11 @@ 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::from_usage("--license 'display the license file'")) + .arg(Arg::from_usage("[output] 'Supply an output file to use'")) + .arg(Arg::from_usage( + "-i, --int=[IFACE] 'Set an interface to use'", + )) } fn app_example4<'c>() -> App<'c> { diff --git a/examples/01a_quick_example.rs b/examples/01a_quick_example.rs index b9e7888b..61171025 100644 --- a/examples/01a_quick_example.rs +++ b/examples/01a_quick_example.rs @@ -1,4 +1,4 @@ -use clap::App; +use clap::{App, Arg}; fn main() { // This example shows how to create an application with several arguments using usage strings, which can be @@ -33,13 +33,17 @@ 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::from_usage( + "-c, --config=[FILE] 'Sets a custom config file'", + )) + .arg(Arg::from_usage("[output] 'Sets an optional output file'")) + .arg(Arg::from_usage( + "-d..., --debug... 'Turn debugging information on'", + )) .subcommand( App::new("test") .about("does testing things") - .arg("-l, --list 'lists test values'"), + .arg(Arg::from_usage("-l, --list 'lists test values'")), ) .get_matches(); diff --git a/examples/03_args.rs b/examples/03_args.rs index 4d7e35b1..f1196e30 100644 --- a/examples/03_args.rs +++ b/examples/03_args.rs @@ -50,10 +50,12 @@ fn main() { // // // One "Flag" using a usage string - .arg("--license 'display the license file'") + .arg(Arg::from_usage("--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::from_usage("[output] 'Supply an output file to use'")) + .arg(Arg::from_usage( + "-i, --int=[IFACE] 'Set an interface to use'", + )) .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..d0de5fdd 100644 --- a/examples/05_flag_args.rs +++ b/examples/05_flag_args.rs @@ -29,8 +29,10 @@ 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::from_usage( + "-c, --config=[FILE] 'sets a custom config file'", + )) + .arg(Arg::from_usage("[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..e7ae9bcc 100644 --- a/examples/07_option_args.rs +++ b/examples/07_option_args.rs @@ -33,8 +33,10 @@ 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::from_usage( + "-c, --config=[FILE] 'the config file to use'", + )) + .arg(Arg::from_usage("[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..83b09357 100644 --- a/examples/12_typed_values.rs +++ b/examples/12_typed_values.rs @@ -1,4 +1,4 @@ -use clap::App; +use clap::{App, Arg}; fn main() { // You can use some convenience methods provided by clap to get typed values, so long as the @@ -22,8 +22,12 @@ 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::from_usage( + "... 'A sequence of whole positive numbers, i.e. 20 25 30'", + )) + .arg(Arg::from_usage( + "-l [len] 'A length to use, defaults to 10 when omitted'", + )) .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..8b8f7b4c 100644 --- a/examples/13_enum_values.rs +++ b/examples/13_enum_values.rs @@ -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::from_usage(" '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..2f20584a 100644 --- a/examples/14_groups.rs +++ b/examples/14_groups.rs @@ -26,10 +26,10 @@ 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::from_usage("--set-ver [ver] 'set version manually'")) + .arg(Arg::from_usage("--major 'auto inc major'")) + .arg(Arg::from_usage("--minor 'auto inc minor'")) + .arg(Arg::from_usage("--patch 'auto inc patch'")) // Create a group, make it required, and add the above arguments .group( ArgGroup::new("vers") @@ -38,8 +38,8 @@ 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::from_usage("[INPUT_FILE] 'some regular input'").group("input")) + .arg(Arg::from_usage("--spec-in [SPEC_IN] 'some special input argument'").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..986b4f9d 100644 --- a/examples/16_app_settings.rs +++ b/examples/16_app_settings.rs @@ -1,4 +1,4 @@ -use clap::{App, AppSettings}; +use clap::{App, AppSettings, Arg}; 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::from_usage(" '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 index ac0f92b4..bc1a6822 100644 --- a/examples/17_yaml.rs +++ b/examples/17_yaml.rs @@ -27,7 +27,7 @@ fn main() { // 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(); + let m = App::from_yaml(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... diff --git a/src/build/app/mod.rs b/src/build/app/mod.rs index 4503ea5b..ac703250 100644 --- a/src/build/app/mod.rs +++ b/src/build/app/mod.rs @@ -398,11 +398,104 @@ impl<'help> App<'help> { ) } - /// Deprecated, see [`App::from`] + /// TODO #[cfg(feature = "yaml")] - #[deprecated(since = "3.0.0", note = "Replaced with `App::from`")] - pub fn from_yaml(yaml: &'help Yaml) -> Self { - Self::from(yaml) + pub fn from_yaml(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_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 @@ -1113,7 +1206,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::from_usage("-c --config=[CONFIG] 'Optionally sets a config file to use'") /// ) /// # ; /// ``` @@ -1153,7 +1246,7 @@ impl<'help> App<'help> { /// # use clap::{App, Arg}; /// App::new("myprog") /// .args(&[ - /// Arg::from("[debug] -d 'turns on debugging info'"), + /// Arg::from_usage("[debug] -d 'turns on debugging info'"), /// Arg::new("input").index(1).help("the input file to use") /// ]) /// # ; @@ -1174,10 +1267,58 @@ impl<'help> App<'help> { self } - /// Deprecated, see [`App::arg`] - #[deprecated(since = "3.0.0", note = "Replaced with `App::arg`")] + /// A convenience method for adding a single [argument] from a usage type string. The string + /// used follows the same rules and syntax as [`Arg::from_usage`] + /// + /// **NOTE:** The downside to using this method is that you can not set any additional + /// properties of the [`Arg`] other than what [`Arg::from_usage`] supports. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// App::new("myprog") + /// .arg_from_usage("-c --config= 'Sets a configuration file to use'") + /// # ; + /// ``` + /// [argument]: ./struct.Arg.html + /// [`Arg`]: ./struct.Arg.html + /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage pub fn arg_from_usage(self, usage: &'help str) -> Self { - self.arg(usage) + self.arg(Arg::from_usage(usage)) + } + + /// Adds multiple [arguments] at once from a usage string, one per line. See + /// [`Arg::from_usage`] for details on the syntax and rules supported. + /// + /// **NOTE:** Like [`App::arg_from_usage`] the downside is you only set properties for the + /// [`Arg`]s which [`Arg::from_usage`] supports. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// App::new("myprog") + /// .args_from_usage( + /// "-c --config=[FILE] 'Sets a configuration file to use' + /// [debug]... -d 'Sets the debugging level' + /// 'The input file to use'" + /// ) + /// # ; + /// ``` + /// [arguments]: ./struct.Arg.html + /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage + /// [`App::arg_from_usage`]: ./struct.App.html#method.arg_from_usage + /// [`Arg`]: ./struct.Arg.html + pub fn args_from_usage(mut self, usage: &'help str) -> Self { + 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 +1756,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::from_usage("--set-ver [ver] 'set the version manually'")) + /// .arg(Arg::from_usage("--major 'auto increase major'")) + /// .arg(Arg::from_usage("--minor 'auto increase minor'")) + /// .arg(Arg::from_usage("--patch 'auto increase patch'")) /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) @@ -1637,14 +1778,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::from_usage("--set-ver [ver] 'set the version manually'")) + /// .arg(Arg::from_usage("--major 'auto increase major'")) + /// .arg(Arg::from_usage("--minor 'auto increase minor'")) + /// .arg(Arg::from_usage("--patch 'auto increase patch'")) + /// .arg(Arg::from_usage("-c [FILE] 'a config file'")) + /// .arg(Arg::from_usage("-i [IFACE] 'an interface'")) /// .groups(&[ /// ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) @@ -1673,11 +1814,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::from_usage(" 'Required configuration file to use'"))) /// # ; /// ``` #[inline] @@ -2965,108 +3106,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..5ee7a643 100644 --- a/src/build/app/settings.rs +++ b/src/build/app/settings.rs @@ -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::from_usage("-c, --config=[FILE] 'Sets a custom config file'")) + /// .arg(Arg::from_usage("-x, --stuff=[FILE] 'Sets a custom stuff file'")) + /// .arg(Arg::from_usage("-f 'Flag'")); /// /// let r = app.try_get_matches_from(vec!["app", "-c", "file", "-f", "-x"]); /// @@ -886,7 +886,7 @@ pub enum AppSettings { /// # use clap::{App, Arg, AppSettings}; /// let m = App::new("myprog") /// .setting(AppSettings::NoBinaryName) - /// .arg(Arg::from("... 'commands to run'")) + /// .arg(Arg::from_usage("... 'commands to run'")) /// .get_matches_from(vec!["command", "set"]); /// /// let cmds: Vec<&str> = m.values_of("cmd").unwrap().collect(); @@ -1031,7 +1031,7 @@ pub enum AppSettings { /// # use clap::{App, Arg, AppSettings}; /// let m = App::new("myprog") /// .setting(AppSettings::TrailingVarArg) - /// .arg(Arg::from("... 'commands to run'")) + /// .arg(Arg::from_usage("... '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..3a6e638b 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg/mod.rs @@ -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::from_usage("-i, --input=[FILE] 'Provides an input file to the program'"); /// ``` #[allow(missing_debug_implementations)] #[derive(Default, Clone)] @@ -349,17 +349,277 @@ impl<'help> Arg<'help> { Self::new(n) } - /// Deprecated, see [`Arg::from`] + /// 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); + /// ``` #[cfg(feature = "yaml")] - #[deprecated(since = "3.0.0", note = "Replaced with `Arg::from`")] 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`")] + /// Creates a new instance of [`Arg`] from a usage string. Allows creation of basic settings + /// for the [`Arg`]. The syntax is flexible, but there are some rules to follow. + /// + /// **NOTE**: Not all settings may be set using the usage string method. Some properties are + /// only available via the builder pattern. + /// + /// **NOTE**: Only ASCII values are officially supported in [`Arg::from_usage`] strings. Some + /// UTF-8 codepoints may work just fine, but this is not guaranteed. + /// + /// # Syntax + /// + /// Usage strings typically following the form: + /// + /// ```notrust + /// [explicit name] [short] [long] [value names] [help string] + /// ``` + /// + /// This is not a hard rule as the attributes can appear in other orders. There are also + /// several additional sigils which denote additional settings. Below are the details of each + /// portion of the string. + /// + /// ### Explicit Name + /// + /// This is an optional field, if it's omitted the argument will use one of the additional + /// fields as the name using the following priority order: + /// + /// * Explicit Name (This always takes precedence when present) + /// * Long + /// * Short + /// * Value Name + /// + /// `clap` determines explicit names as the first string of characters between either `[]` or + /// `<>` where `[]` has the dual notation of meaning the argument is optional, and `<>` meaning + /// the argument is required. + /// + /// Explicit names may be followed by: + /// * The multiple denotation `...` + /// + /// Example explicit names as follows (`ename` for an optional argument, and `rname` for a + /// required argument): + /// + /// ```notrust + /// [ename] -s, --long 'some flag' + /// -r, --longer 'some other flag' + /// ``` + /// + /// ### Short + /// + /// This is set by placing a single character after a leading `-`. + /// + /// Shorts may be followed by + /// * The multiple denotation `...` + /// * An optional comma `,` which is cosmetic only + /// * Value notation + /// + /// Example shorts are as follows (`-s`, and `-r`): + /// + /// ```notrust + /// -s, --long 'some flag' + /// -r [val], --longer 'some option' + /// ``` + /// + /// ### Long + /// + /// This is set by placing a word (no spaces) after a leading `--`. + /// + /// Shorts may be followed by + /// * The multiple denotation `...` + /// * Value notation + /// + /// Example longs are as follows (`--some`, and `--rapid`): + /// + /// ```notrust + /// -s, --some 'some flag' + /// --rapid=[FILE] 'some option' + /// ``` + /// + /// ### Values (Value Notation) + /// + /// This is set by placing a word(s) between `[]` or `<>` optionally after `=` (although this + /// is cosmetic only and does not affect functionality). If an explicit name has **not** been + /// set, using `<>` will denote a required argument, and `[]` will denote an optional argument + /// + /// Values may be followed by + /// * The multiple denotation `...` + /// * More Value notation + /// + /// More than one value will also implicitly set the arguments number of values, i.e. having + /// two values, `--option [val1] [val2]` specifies that in order for option to be satisified it + /// must receive exactly two values + /// + /// Example values are as follows (`FILE`, and `SPEED`): + /// + /// ```notrust + /// -s, --some [FILE] 'some option' + /// --rapid=... 'some required multiple option' + /// ``` + /// + /// ### Help String + /// + /// The help string is denoted between a pair of single quotes `''` and may contain any + /// characters. + /// + /// Example help strings are as follows: + /// + /// ```notrust + /// -s, --some [FILE] 'some option' + /// --rapid=... 'some required multiple option' + /// ``` + /// + /// ### Additional Sigils + /// + /// Multiple notation `...` (three consecutive dots/periods) specifies that this argument may + /// be used multiple times. Do not confuse multiple occurrences (`...`) with multiple values. + /// `--option val1 val2` is a single occurrence with multiple values. `--flag --flag` is + /// multiple occurrences (and then you can obviously have instances of both as well) + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg}; + /// App::new("prog") + /// .args(&[ + /// Arg::from_usage("--config 'a required file for the configuration and no short'"), + /// Arg::from_usage("-d, --debug... 'turns on debugging information and allows multiples'"), + /// Arg::from_usage("[input] 'an optional input file to use'") + /// ]) + /// # ; + /// ``` + /// [`Arg`]: ./struct.Arg.html + /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage pub fn from_usage(u: &'help str) -> Self { - Self::from(u) + UsageParser::from_usage(u).parse() } pub(crate) fn generated(mut self) -> Self { @@ -1182,10 +1442,10 @@ impl<'help> Arg<'help> { /// ```rust # use clap::{App, Arg}; /// # use clap::{App, Arg}; /// let m = App::new("prog") - /// .arg(Arg::from("-f, --flag 'some flag'") + /// .arg(Arg::from_usage("-f, --flag 'some flag'") /// .conflicts_with("debug")) - /// .arg(Arg::from("-d, --debug 'other flag'")) - /// .arg(Arg::from("-c, --color 'third flag'") + /// .arg(Arg::from_usage("-d, --debug 'other flag'")) + /// .arg(Arg::from_usage("-c, --color 'third flag'") /// .overrides_with("flag")) /// .get_matches_from(vec![ /// "prog", "-f", "-d", "-c"]); @@ -1207,7 +1467,7 @@ impl<'help> Arg<'help> { /// ```rust /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from("--flag 'some flag'").overrides_with("flag")) + /// .arg(Arg::from_usage("--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); @@ -1220,7 +1480,7 @@ impl<'help> Arg<'help> { /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from("--flag... 'some flag'").overrides_with("flag")) + /// .arg(Arg::from_usage("--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); @@ -1233,7 +1493,7 @@ impl<'help> Arg<'help> { /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from("--opt [val] 'some option'").overrides_with("opt")) + /// .arg(Arg::from_usage("--opt [val] '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); @@ -1264,7 +1524,7 @@ impl<'help> Arg<'help> { /// ``` /// # use clap::{App, Arg}; /// let m = App::new("posix") - /// .arg(Arg::from("[opt]... --opt [val]... 'some option'") + /// .arg(Arg::from_usage("[opt]... --opt [val]... 'some option'") /// .overrides_with("opt")) /// .get_matches_from(vec!["", "--opt", "first", "over", "--opt", "other", "val"]); /// assert!(m.is_present("opt")); @@ -1288,10 +1548,10 @@ impl<'help> Arg<'help> { /// ```rust /// # use clap::{App, Arg}; /// let m = App::new("prog") - /// .arg(Arg::from("-f, --flag 'some flag'") + /// .arg(Arg::from_usage("-f, --flag 'some flag'") /// .conflicts_with("color")) - /// .arg(Arg::from("-d, --debug 'other flag'")) - /// .arg(Arg::from("-c, --color 'third flag'") + /// .arg(Arg::from_usage("-d, --debug 'other flag'")) + /// .arg(Arg::from_usage("-c, --color 'third flag'") /// .overrides_with_all(&["flag", "debug"])) /// .get_matches_from(vec![ /// "prog", "-f", "-d", "-c"]); @@ -4905,159 +5165,12 @@ impl<'help> Arg<'help> { } } -#[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 - } -} - impl<'help> From<&'_ Arg<'help>> for Arg<'help> { fn from(a: &Arg<'help>) -> Self { a.clone() } } -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/tests.rs b/src/build/arg/tests.rs index 520572bf..0840da93 100644 --- a/src/build/arg/tests.rs +++ b/src/build/arg/tests.rs @@ -2,7 +2,7 @@ use super::{settings::ArgSettings, Arg}; #[test] fn short_flag_misspell() { - let a = Arg::from("-f1, --flag 'some flag'"); + let a = Arg::from_usage("-f1, --flag 'some flag'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.long.unwrap(), "flag"); @@ -14,7 +14,7 @@ fn short_flag_misspell() { #[test] fn short_flag_name_missing() { - let a = Arg::from("-f 'some flag'"); + let a = Arg::from_usage("-f 'some flag'"); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); diff --git a/src/build/arg_group.rs b/src/build/arg_group.rs index 809441c5..cb509667 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::from_usage("--set-ver [ver] 'set the version manually'")) +/// .arg(Arg::from_usage("--major 'auto increase major'")) +/// .arg(Arg::from_usage("--minor 'auto increase minor'")) +/// .arg(Arg::from_usage("--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::from_usage("--set-ver [ver] 'set the version manually'")) +/// .arg(Arg::from_usage("--major 'auto increase major'")) +/// .arg(Arg::from_usage("--minor 'auto increase minor'")) +/// .arg(Arg::from_usage("--patch 'auto increase patch'")) /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) @@ -438,15 +438,7 @@ impl<'help> From<&'_ ArgGroup<'help>> for ArgGroup<'help> { #[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); - /// ``` + /// TODO 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..42bce912 100644 --- a/src/build/usage_parser.rs +++ b/src/build/usage_parser.rs @@ -250,7 +250,7 @@ mod test { #[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 +258,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 +266,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 +274,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 +282,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 +290,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 +298,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 +306,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 +336,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 +351,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 +365,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 +379,7 @@ mod test { #[test] fn create_option_usage2() { - let a = Arg::from("