From 28b73855523ad170544afdb20665db98702fbe70 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 22 May 2015 11:15:26 -0400 Subject: [PATCH 1/6] depr(SubCommand/App): several methods and functions for stable release SubCommand::new() -> SubCommand::with_name() App::error_on_no_subcommand() -> App::subcommand_required() --- src/app.rs | 19 +++++++++++++++++++ src/args/arg.rs | 37 ++++++++++++++++++++++++++++--------- src/args/subcommand.rs | 17 +++++++++++++++++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/app.rs b/src/app.rs index c1fa3a7a3fe..deda76a1483 100644 --- a/src/app.rs +++ b/src/app.rs @@ -234,6 +234,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self } + /// **WARNING:** This method is deprecated. Use `.subcommand_required(true)` instead. + /// /// Allows specifying that if no subcommand is present at runtime, error and exit gracefully /// /// **NOTE:** This defaults to false (subcommands do *not* need to be present) @@ -251,6 +253,23 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self } + /// Allows specifying that if no subcommand is present at runtime, error and exit gracefully + /// + /// **NOTE:** This defaults to false (subcommands do *not* need to be present) + /// + /// # Example + /// + /// ```no_run + /// # use clap::App; + /// # let app = App::new("myprog") + /// .subcommands_negate_reqs(true) + /// # .get_matches(); + /// ``` + pub fn subcommand_required(mut self, n: bool) -> App<'a, 'v, 'ab, 'u, 'h, 'ar> { + self.no_sc_error = n; + self + } + /// Sets a string of the version number to be displayed when displaying version or help /// information. /// diff --git a/src/args/arg.rs b/src/args/arg.rs index cc64e3d334b..0ecfd44c2be 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -88,10 +88,14 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> { #[doc(hidden)] pub min_vals: Option, #[doc(hidden)] - pub empty_vals: bool + pub empty_vals: bool, + #[doc(hidden)] + pub global: bool } impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { + /// **WARNING:** This function is deprecated. Use `Arg::with_name()` instead. + /// /// Creates a new instace of `Arg` using a unique string name. /// The name will be used by the library consumer to get information about /// whether or not the argument was used at runtime. @@ -100,8 +104,6 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { /// and positional arguments (i.e. those without a `-` or `--`) the name will also /// be displayed when the user prints the usage/help information of the program. /// - /// **NOTE:** this function is deprecated in favor of Arg::with_name() to stay consistent with - /// Rust APIs /// /// /// # Example @@ -423,6 +425,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self } + /// **WARNING:** This method is deprecated. Use `.conflicts_with()` instead. + /// /// Sets a mutually exclusive argument by name. I.e. when using this argument, /// the following argument can't be present. /// @@ -430,9 +434,6 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { /// by default. Mutually exclusive rules only need to be set for one of the two /// arguments, they do not need to be set for each. /// - /// **NOTE:** This method is deprecated in favor of `conflicts_with()` - /// - /// /// # Example /// /// ```no_run @@ -450,6 +451,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self } + /// **WARNING:** This method is deprecated. Use `conflicts_with_all()` instead. + /// /// Sets a mutually exclusive arguments by names. I.e. when using this argument, /// the following argument can't be present. /// @@ -457,9 +460,6 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { /// by default. Mutually exclusive rules only need to be set for one of the two /// arguments, they do not need to be set for each. /// - /// **NOTE:** This method is deprecated in favor of `conflicts_with_all()` - /// - /// /// # Example /// /// ```no_run @@ -646,6 +646,25 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self } + /// Specifies that an argument applies to and will be available to all subcommands, both + /// children, and parent commands. + /// + /// **NOTE:** Global arguments *cannot* be required. + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let matches = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug") + /// .global(true) + /// # ).get_matches(); + pub fn global(mut self, g: bool) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> { + self.global = g; + self + } + /// Allows an argument to accept explicit empty values. An empty value must be specified at the /// command line with an explicit `""`, or `''` /// diff --git a/src/args/subcommand.rs b/src/args/subcommand.rs index acfec961260..5a6ff5fff1d 100644 --- a/src/args/subcommand.rs +++ b/src/args/subcommand.rs @@ -28,6 +28,23 @@ pub struct SubCommand<'n, 'a> { } impl<'n, 'a> SubCommand<'n, 'a> { + /// Creates a new instance of a subcommand requiring a name. Will be displayed + /// to the user when they print version or help and usage information. + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, SubCommand}; + /// # let prog = App::new("myprog").subcommand( + /// SubCommand::new("config") + /// # ).get_matches(); + /// ``` + pub fn with_name<'au, 'v, 'ab, 'u, 'h, 'ar>(name: &'ar str) -> App<'au, 'v, 'ab, 'u, 'h, 'ar> { + App::new(name) + } + + /// **WARNING:** This function is deprecated. Use `SubCommand::with_name()` instead. + /// /// Creates a new instance of a subcommand requiring a name. Will be displayed /// to the user when they print version or help and usage information. /// From 2bcc6137a83cb07757771a0afea953e68e692f0b Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 22 May 2015 13:02:11 -0400 Subject: [PATCH 2/6] feat(Global Args): allows args that propagate down to child commands Closes #131 --- src/app.rs | 22 ++++++++++++++++++++-- src/args/arg.rs | 8 ++++++-- src/args/argbuilder/flag.rs | 1 + src/args/argbuilder/option.rs | 3 ++- src/args/argbuilder/positional.rs | 3 ++- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/app.rs b/src/app.rs index deda76a1483..685261b83e1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -118,6 +118,7 @@ pub struct App<'a, 'v, 'ab, 'u, 'h, 'ar> { bin_name: Option, usage: Option, groups: HashMap<&'ar str, ArgGroup<'ar, 'ar>>, + global_args: Vec>, no_sc_error: bool } @@ -161,6 +162,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ bin_name: None, groups: HashMap::new(), subcmds_neg_reqs: false, + global_args: vec![], no_sc_error: false } } @@ -419,6 +421,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ min_vals: a.min_vals, max_vals: a.max_vals, help: a.help, + global: a.global, empty_vals: a.empty_vals }; if pb.min_vals.is_some() && !pb.multiple { @@ -470,11 +473,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ multiple: a.multiple, blacklist: None, help: a.help, + global: a.global, possible_vals: None, num_vals: a.num_vals, min_vals: a.min_vals, max_vals: a.max_vals, - val_names: a.val_names, + val_names: a.val_names.clone(), requires: None, required: a.required, empty_vals: a.empty_vals @@ -540,6 +544,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ long: a.long, help: a.help, blacklist: None, + global: a.global, multiple: a.multiple, requires: None, }; @@ -560,6 +565,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } self.flags.insert(a.name, fb); } + if a.global { + if a.required { + panic!("Global arguments cannot be required.\n\n\t'{}' is marked as global and required", a.name); + } + self.global_args.push(a); + } self } @@ -749,9 +760,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ /// // Additional subcommand configuration goes here, such as other arguments... /// # .get_matches(); /// ``` - pub fn subcommand(mut self, subcmd: App<'a, 'v, 'ab, 'u, 'h, 'ar>) + pub fn subcommand(mut self, mut subcmd: App<'a, 'v, 'ab, 'u, 'h, 'ar>) -> App<'a, 'v, 'ab, 'u, 'h, 'ar> { if subcmd.name == "help" { self.needs_subcmd_help = false; } + { + while let Some(a) = self.global_args.pop() { + subcmd = subcmd.arg(a); + } + } self.subcommands.insert(subcmd.name.clone(), subcmd); self } @@ -1763,6 +1779,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ help: Some("Prints help information"), blacklist: None, multiple: false, + global: false, requires: None, }; if self.needs_short_help { @@ -1780,6 +1797,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ help: Some("Prints version information"), blacklist: None, multiple: false, + global: false, requires: None, }; if self.needs_short_version { diff --git a/src/args/arg.rs b/src/args/arg.rs index 0ecfd44c2be..5f0f14c9d58 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -133,6 +133,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { val_names: None, max_vals: None, min_vals: None, + global: false, empty_vals: true, } } @@ -173,6 +174,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { max_vals: None, val_names: None, group: None, + global: false, empty_vals: true } } @@ -330,6 +332,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { max_vals: None, min_vals: None, group: None, + global: false, empty_vals: true } } @@ -646,8 +649,9 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self } - /// Specifies that an argument applies to and will be available to all subcommands, both - /// children, and parent commands. + /// Specifies that an argument applies to and will be available to all child subcommands. + /// + /// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands) /// /// **NOTE:** Global arguments *cannot* be required. /// diff --git a/src/args/argbuilder/flag.rs b/src/args/argbuilder/flag.rs index cf40bc3e80a..28673f6a567 100644 --- a/src/args/argbuilder/flag.rs +++ b/src/args/argbuilder/flag.rs @@ -24,6 +24,7 @@ pub struct FlagBuilder<'n> { /// The short version (i.e. single character) /// of the argument, no preceding `-` pub short: Option, + pub global: bool, } impl<'n> Display for FlagBuilder<'n> { diff --git a/src/args/argbuilder/option.rs b/src/args/argbuilder/option.rs index 217f90627e6..6d4eeff5ae0 100644 --- a/src/args/argbuilder/option.rs +++ b/src/args/argbuilder/option.rs @@ -29,7 +29,8 @@ pub struct OptBuilder<'n> { pub min_vals: Option, pub max_vals: Option, pub val_names: Option>, - pub empty_vals: bool + pub empty_vals: bool, + pub global: bool, } impl<'n> Display for OptBuilder<'n> { diff --git a/src/args/argbuilder/positional.rs b/src/args/argbuilder/positional.rs index f11b40921da..167b470b91a 100644 --- a/src/args/argbuilder/positional.rs +++ b/src/args/argbuilder/positional.rs @@ -26,7 +26,8 @@ pub struct PosBuilder<'n> { pub num_vals: Option, pub max_vals: Option, pub min_vals: Option, - pub empty_vals: bool + pub empty_vals: bool, + pub global: bool } impl<'n> Display for PosBuilder<'n> { From c60e9183cc3de007b2caf7923f41483ecb1a2a52 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 22 May 2015 13:10:34 -0400 Subject: [PATCH 3/6] tests(Global Args): adds tests for global args --- clap-tests/run_tests.py | 6 +++--- clap-tests/src/main.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py index 212fd030234..10a48fd4287 100755 --- a/clap-tests/run_tests.py +++ b/clap-tests/run_tests.py @@ -140,8 +140,8 @@ claptests subcmd [POSITIONAL] [FLAGS] [OPTIONS] FLAGS: - -h, --help Prints help information -f, --flag tests flags + -h, --help Prints help information -v, --version Prints version information OPTIONS: @@ -161,7 +161,7 @@ option NOT present positional NOT present subcmd present -scflag present 1 times +flag present 1 times scoption present with value: some An scoption: some scpositional present with value: value''' @@ -177,7 +177,7 @@ option NOT present positional NOT present subcmd present -scflag present 2 times +flag present 2 times scoption present with value: some An scoption: some scpositional present with value: value''' diff --git a/clap-tests/src/main.rs b/clap-tests/src/main.rs index 99db0e856ba..5cc096cefb4 100644 --- a/clap-tests/src/main.rs +++ b/clap-tests/src/main.rs @@ -6,8 +6,7 @@ use clap::{App, Arg, SubCommand}; fn main() { let m_val_names = ["one", "two"]; - let args = "-f --flag... 'tests flags' - -o --option=[opt]... 'tests options' + let args = "-o --option=[opt]... 'tests options' [positional] 'tests positionals'"; let opt3_vals = ["fast", "slow"]; let pos3_vals = ["vi", "emacs"]; @@ -17,6 +16,8 @@ fn main() { .about("tests clap library") .author("Kevin K. ") .args_from_usage(args) + .arg(Arg::from_usage("-f --flag... 'tests flags'") + .global(true)) .args(vec![ Arg::from_usage("[flag2] -F 'tests flags with exclusions'").mutually_excludes("flag").requires("option2"), Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").mutually_excludes("option").requires("positional2"), @@ -32,7 +33,6 @@ fn main() { .about("tests subcommands") .version("0.1") .author("Kevin K. ") - .arg_from_usage("[scflag] -f --flag... 'tests flags'") .arg_from_usage("-o --option [scoption]... 'tests options'") .arg_from_usage("[scpositional] 'tests positionals'")) .get_matches(); @@ -105,10 +105,10 @@ fn main() { if matches.is_present("subcmd") { println!("subcmd present"); if let Some(matches) = matches.subcommand_matches("subcmd") { - if matches.is_present("scflag") { - println!("scflag present {} times", matches.occurrences_of("scflag")); + if matches.is_present("flag") { + println!("flag present {} times", matches.occurrences_of("flag")); } else { - println!("scflag NOT present"); + println!("flag NOT present"); } if matches.is_present("scoption") { From 92fcec8fb7042066729cdcb3637f0d4522b3a42a Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 22 May 2015 13:19:06 -0400 Subject: [PATCH 4/6] chore: adds deprecations sections --- .clog.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.clog.toml b/.clog.toml index d0c53a92e9d..808253fc26f 100644 --- a/.clog.toml +++ b/.clog.toml @@ -7,3 +7,4 @@ from-latest-tag = true Performance = ["perf"] Improvements = ["impr", "im", "imp"] Documentation = ["docs"] +Deprecations = ["depr"] From d6c3ed54d21cf7b40d9f130d4280ff5448522fc5 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 22 May 2015 18:17:57 -0400 Subject: [PATCH 5/6] imp(Colors): implements more structured colored output Closes #129 --- clap-tests/run_tests.py | 25 ++--- src/app.rs | 208 +++++++++++++++++++++------------------- src/fmt.rs | 50 ++++++++++ src/lib.rs | 2 + src/macros.rs | 50 ++++++---- src/usageparser.rs | 2 +- 6 files changed, 203 insertions(+), 134 deletions(-) create mode 100644 src/fmt.rs diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py index 10a48fd4287..6b6d642291e 100755 --- a/clap-tests/run_tests.py +++ b/clap-tests/run_tests.py @@ -27,18 +27,18 @@ --multvalsmo Tests mutliple values, not mult occs -o, --option ... tests options --long-option-2 tests long options with exclusions - -O, --Option tests options with specific value sets [values: fast slow] + -O, --Option tests options with specific value sets [values: fast, slow] POSITIONAL ARGUMENTS: positional tests positionals positional2 tests positionals with exclusions - positional3... tests positionals with specific values [values: emacs vi] + positional3... tests positionals with specific values [values: emacs, vi] SUBCOMMANDS: help Prints this message subcmd tests subcommands''' -_sc_dym_usage = '''The subcommand 'subcm' isn't valid +_sc_dym_usage = '''error: The subcommand 'subcm' isn't valid Did you mean 'subcmd' ? If you received this message in error, try re-running with 'claptests -- subcm' @@ -48,7 +48,7 @@ For more information try --help''' -_arg_dym_usage = '''The argument --optio isn't valid +_arg_dym_usage = '''error: The argument '--optio' isn't valid Did you mean --option ? USAGE: @@ -56,8 +56,9 @@ For more information try --help''' -_pv_dym_usage = '''"slo" isn't a valid value for '--Option ' +_pv_dym_usage = '''error: 'slo' isn't a valid value for '--Option ' [valid values: fast slow] + Did you mean 'slow' ? USAGE: @@ -65,21 +66,21 @@ For more information try --help''' -_excluded = '''The argument '--flag' cannot be used with '-F' +_excluded = '''error: The argument '--flag' cannot be used with '-F' USAGE: \tclaptests [positional2] -F --long-option-2 For more information try --help''' -_excluded_l = '''The argument -f cannot be used '-F' +_excluded_l = '''error: The argument '-f' cannot be used '-F' USAGE: claptests [positional2] -F --long-option-2 For more information try --help''' -_required = '''The following required arguments were not supplied: +_required = '''error: The following required arguments were not supplied: \t'[positional2]' \t'--long-option-2 ' @@ -182,7 +183,7 @@ An scoption: some scpositional present with value: value''' -_min_vals_few = '''The argument '--minvals2 ...' requires at least 2 values, but 1 was provided +_min_vals_few = '''error: The argument '--minvals2 ...' requires at least 2 values, but 1 was provided USAGE: \tclaptests --minvals2 ... @@ -213,21 +214,21 @@ positional present with value: too subcmd NOT present''' -_mult_vals_more = '''The argument --multvals was supplied more than once, but does not support multiple values +_mult_vals_more = '''error: The argument '--multvals' was supplied more than once, but does not support multiple values USAGE: \tclaptests --multvals For more information try --help''' -_mult_vals_few = '''The argument '--multvals ' requires a value but none was supplied +_mult_vals_few = '''error: The argument '--multvals ' requires a value but none was supplied USAGE: \tclaptests --multvals For more information try --help''' -_mult_vals_2m1 = '''The argument '--multvalsmo ' requires 2 values, but 1 was provided +_mult_vals_2m1 = '''error: The argument '--multvalsmo ' requires 2 values, but 1 was provided USAGE: claptests --multvalsmo diff --git a/src/app.rs b/src/app.rs index 685261b83e1..4a7f166897f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,11 +11,10 @@ use std::process; use args::{ ArgMatches, Arg, SubCommand, MatchedArg}; use args::{ FlagBuilder, OptBuilder, PosBuilder}; use args::ArgGroup; +use fmt::Format; #[cfg(feature = "suggestions")] use strsim; -#[cfg(feature = "color")] -use ansi_term::Colour::Red; /// Produces a string from a given list of possible values which is similar to /// the passed in value `v` with a certain confidence. @@ -1037,7 +1036,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ fn print_usage(&self, more_info: bool, matches: Option>) { print!("{}",self.create_usage(matches)); if more_info { - println!("\n\nFor more information try --help"); + println!("\n\nFor more information try {}", Format::Good("--help")); } } @@ -1243,16 +1242,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } // Reports and error to the users screen along with an optional usage statement and quits - #[cfg(not(feature = "color"))] fn report_error(&self, msg: String, usage: bool, quit: bool, matches: Option>) { - println!("{}\n", msg); - if usage { self.print_usage(true, matches); } - if quit { self.exit(1); } - } - - #[cfg(feature = "color")] - fn report_error(&self, msg: String, usage: bool, quit: bool, matches: Option>) { - println!("{}\n", Red.paint(&msg[..])); + println!("{} {}\n", Format::Error("error:"), msg); if usage { self.print_usage(true, matches); } if quit { self.exit(1); } } @@ -1330,10 +1321,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Some(candidate) => { let mut suffix = "\n\tDid you mean ".to_string(); match style { - DidYouMeanMessageStyle::LongFlag => suffix.push_str("--"), + DidYouMeanMessageStyle::LongFlag => suffix.push_str(&Format::Good("--").to_string()[..]), DidYouMeanMessageStyle::EnumValue => suffix.push('\''), } - suffix.push_str(candidate); + suffix.push_str(&Format::Good(candidate).to_string()[..]); if let DidYouMeanMessageStyle::EnumValue = style { suffix.push('\''); } @@ -1349,10 +1340,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let suffix = App::did_you_mean_suffix(arg, p_vals.iter(), DidYouMeanMessageStyle::EnumValue); - self.report_error(format!("\"{}\" isn't a valid value for '{}'{}{}", - arg, - opt, - format!("\n\t[valid values:{}]", + self.report_error(format!("'{}' isn't a valid value for '{}'{}{}", + Format::Warning(arg), + Format::Warning(opt), + format!("\n\t[valid values:{}]\n", p_vals.iter() .fold(String::new(), |acc, name| { acc + &format!(" {}",name)[..] @@ -1395,9 +1386,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if num == vals.len() as u8 && !opt.multiple { self.report_error(format!("The argument '{}' was found, \ but '{}' only expects {} values", - arg, - opt, - vals.len()), + Format::Warning(&arg), + Format::Warning(opt.to_string()), + Format::Good(vals.len().to_string())), true, true, Some( @@ -1413,7 +1404,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ matches.args.contains_key(opt.name) && arg.is_empty() { self.report_error(format!("The argument '{}' does not allow empty \ - values, but one was found.", opt), + values, but one was found.", Format::Warning(opt.to_string())), true, true, Some(matches.args.keys() @@ -1455,7 +1446,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(ref o) = self.opts.get(name) { if !o.multiple { self.report_error( - format!("The argument '{}' requires a value but none was supplied", o), + format!("The argument '{}' requires a value but none was supplied", + Format::Warning(o.to_string())), true, true, Some(matches.args.keys().map(|k| *k).collect() ) ); @@ -1490,10 +1482,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error( format!("The subcommand '{}' isn't valid\n\tDid you mean '{}' ?\n\n\ If you received this message in error, try \ - re-running with '{} -- {}'", - arg, - candidate_subcommand, + re-running with '{} {} {}'", + Format::Warning(&arg), + Format::Good(candidate_subcommand), self.bin_name.clone().unwrap_or(self.name.clone()), + Format::Good("--"), arg), true, true, @@ -1503,8 +1496,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if self.positionals_idx.is_empty() { self.report_error( - format!("Found argument \"{}\", but {} wasn't expecting any", - arg, + format!("Found argument '{}', but {} wasn't expecting any", + Format::Warning(&arg), self.bin_name.clone().unwrap_or(self.name.clone())), true, true, @@ -1517,9 +1510,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if self.blacklist.contains(p.name) { matches.args.remove(p.name); self.report_error(format!("The argument '{}' cannot be used with {}", - p, + Format::Warning(p.to_string()), match self.blacklisted_from(p.name, &matches) { - Some(name) => format!("'{}'", name), + Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the other specified \ arguments".to_owned() }), @@ -1544,7 +1537,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(ref vals) = ma.values { if vals.len() as u8 == num { self.report_error(format!("The argument '{}' was found, \ - but '{}' wasn't expecting any more values", arg, p), + but '{}' wasn't expecting any more values", + Format::Warning(&arg), + Format::Warning(p.to_string())), true, true, Some(matches.args.keys() @@ -1555,7 +1550,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } if !p.empty_vals && matches.args.contains_key(p.name) && arg.is_empty() { self.report_error(format!("The argument '{}' does not allow empty \ - values, but one was found.", p), + values, but one was found.", Format::Warning(p.to_string())), true, true, Some(matches.args.keys() @@ -1579,7 +1574,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut bm = BTreeMap::new(); if !p.empty_vals && arg.is_empty() { self.report_error(format!("The argument '{}' does not allow empty \ - values, but one was found.", p), + values, but one was found.", Format::Warning(p.to_string())), true, true, Some(matches.args.keys() @@ -1614,7 +1609,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else { self.report_error(format!("The argument '{}' was found, but '{}' wasn't \ - expecting any", arg, + expecting any", Format::Warning(&arg), self.bin_name.clone().unwrap_or(self.name.clone())), true, true, @@ -1632,8 +1627,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ }; if should_err { self.report_error( - format!("The argument '{}' requires a value but none was \ - supplied", o), + format!("The argument '{}' requires a value but there wasn't any \ + supplied", Format::Warning(o.to_string())), true, true, Some(matches.args.keys().map(|k| *k).collect() ) ); @@ -1641,7 +1636,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else if !o.multiple { self.report_error( - format!("The argument '{}' requires a value but none was supplied", o), + format!("The argument '{}' requires a value but none was supplied", + Format::Warning(o.to_string())), true, true, Some(matches.args.keys().map(|k| *k).collect() ) ); @@ -1653,7 +1649,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .map(|s| *s) .collect::>()) .iter() - .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'",s)[..])), + .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'", + Format::Error(s.to_string()))[..])), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1661,8 +1658,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else { self.report_error( format!("The argument '{}' requires a value but none was supplied", - format!("{}", self.positionals_idx.get( - self.positionals_name.get(a).unwrap()).unwrap())), + Format::Warning(format!("{}", self.positionals_idx.get( + self.positionals_name.get(a).unwrap()).unwrap()))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1715,13 +1712,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } else if self.no_sc_error { let bn = self.bin_name.clone().unwrap_or(self.name.clone()); - self.report_error(format!("'{}' requires a subcommand but none was provided", &bn[..]), + self.report_error(format!("'{}' requires a subcommand but none was provided", + Format::Warning(&bn[..])), if self.usage_str.is_some() { true } else { false }, if self.usage_str.is_some() { true } else { false }, Some(matches.args.keys().map(|k| *k).collect())); - println!("USAGE:\n\t{} [SUBCOMMAND]\n\nFor more information re-run with '--help' or \ - 'help'", &bn[..]); + println!("USAGE:\n\t{} [SUBCOMMAND]\n\nFor more information re-run with {} or \ + '{}'", &bn[..], Format::Good("--help"), Format::Good("help")); self.exit(1); } if !self.required.is_empty() && !self.subcmds_neg_reqs { @@ -1732,7 +1730,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .map(|s| *s) .collect::>()) .iter() - .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'",s)[..])), + .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'", + Format::Error(s))[..])), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1837,8 +1836,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ arg = arg_vec[0]; // prevents "--config= value" typo if arg_vec[1].len() == 0 { - self.report_error(format!("The argument --{} requires a value, but none was \ - supplied", arg), + self.report_error(format!("The argument '{}' requires a value, but none was \ + supplied", Format::Warning(format!("--{}", arg))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1852,15 +1851,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Ensure this option isn't on the master mutually excludes list if self.blacklist.contains(v.name) { matches.args.remove(v.name); - self.report_error(format!("The argument --{} cannot be used with one or more of \ - the other specified arguments", arg), + self.report_error(format!("The argument '{}' cannot be used with one or more of \ + the other specified arguments", Format::Warning(format!("--{}", arg))), true, true, Some(matches.args.keys().map(|k| *k).collect())); } if matches.args.contains_key(v.name) { if !v.multiple { - self.report_error(format!("The argument --{} was supplied more than once, but \ - does not support multiple values", arg), + self.report_error(format!("The argument '{}' was supplied more than once, but \ + does not support multiple values", + Format::Warning(format!("--{}", arg))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1877,7 +1877,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if arg_val.is_some() { if !v.empty_vals && arg.is_empty() && matches.args.contains_key(v.name) { self.report_error(format!("The argument '{}' does not allow empty \ - values, but one was found.", v), + values, but one was found.", Format::Warning(v.to_string())), true, true, Some(matches.args.keys() @@ -1894,7 +1894,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else { if !v.empty_vals && arg_val.is_some() && arg_val.clone().unwrap().is_empty() { self.report_error(format!("The argument '{}' does not allow empty \ - values, but one was found.", v), + values, but one was found.", Format::Warning(v.to_string())), true, true, Some(matches.args.keys() @@ -1946,11 +1946,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if self.blacklist.contains(v.name) { matches.args.remove(v.name); self.report_error(format!("The argument '{}' cannot be used with {}", - v, - match self.blacklisted_from(v.name, matches) { - Some(name) => format!("'{}'", name), - None => "one or more of the specified arguments".to_owned() - }), + Format::Warning(v.to_string()), + match self.blacklisted_from(v.name, matches) { + Some(name) => format!("'{}'", Format::Warning(name)), + None => "one or more of the specified arguments".to_owned() + }), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -1959,7 +1959,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Make sure this isn't one being added multiple times if it doesn't suppor it if matches.args.contains_key(v.name) && !v.multiple { self.report_error(format!("The argument '{}' was supplied more than once, but does \ - not support multiple values", v), + not support multiple values", Format::Warning(v.to_string())), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -2038,7 +2038,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } - self.report_error(format!("The argument --{} isn't valid{}", arg, suffix.0), + self.report_error(format!("The argument '{}' isn't valid{}", + Format::Warning(format!("--{}", arg)), + suffix.0), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -2054,7 +2056,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ for c in arg.chars() { self.check_for_help_and_version(c); if !self.parse_single_short_flag(matches, c) { - self.report_error(format!("The argument -{} isn't valid",arg), + self.report_error(format!("The argument '{}' isn't valid", + Format::Warning(format!("-{}", c))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -2079,10 +2082,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Ensure this option isn't on the master mutually excludes list if self.blacklist.contains(v.name) { matches.args.remove(v.name); - self.report_error(format!("The argument -{} cannot be used with {}", - arg, + self.report_error(format!("The argument '{}' cannot be used with {}", + Format::Warning(format!("-{}", arg)), match self.blacklisted_from(v.name, matches) { - Some(name) => format!("'{}'", name), + Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the other specified arguments".to_owned() }), true, @@ -2092,8 +2095,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if matches.args.contains_key(v.name) { if !v.multiple { - self.report_error(format!("The argument -{} was supplied more than once, but \ - does not support multiple values", arg), + self.report_error(format!("The argument '{}' was supplied more than once, but \ + does not support multiple values", + Format::Warning(format!("-{}", arg))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -2130,7 +2134,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } // Didn't match a flag or option, must be invalid - self.report_error(format!("The argument -{} isn't valid",arg_c), + self.report_error(format!("The argument '{}' isn't valid", + Format::Warning(format!("-{}", arg_c))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -2145,10 +2150,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Ensure this flag isn't on the mutually excludes list if self.blacklist.contains(v.name) { matches.args.remove(v.name); - self.report_error(format!("The argument -{} cannot be used {}", - arg, + self.report_error(format!("The argument '{}' cannot be used {}", + Format::Warning(format!("-{}", arg)), match self.blacklisted_from(v.name, matches) { - Some(name) => format!("'{}'", name), + Some(name) => format!("'{}'", Format::Warning(name)), None => "with one or more of the other specified \ arguments".to_owned() }), @@ -2159,8 +2164,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Make sure this isn't one being added multiple times if it doesn't suppor it if matches.args.contains_key(v.name) && !v.multiple { - self.report_error(format!("The argument -{} was supplied more than once, but does \ - not support multiple values", arg), + self.report_error(format!("The argument '{}' was supplied more than once, but does \ + not support multiple values", + Format::Warning(format!("-{}", arg))), true, true, Some(matches.args.keys().map(|k| *k).collect())); @@ -2213,16 +2219,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ matches.args.remove(name); self.report_error(format!("The argument '{}' cannot be used with {}", if let Some(ref flag) = self.flags.get(name) { - format!("{}", flag) + format!("{}", Format::Warning(flag.to_string())) } else if let Some(ref opt) = self.opts.get(name) { - format!("{}", opt) + format!("{}", Format::Warning(opt.to_string())) } else { match self.positionals_idx.values().filter(|p| p.name == *name).next() { - Some(pos) => format!("{}", pos), - None => format!("\"{}\"", name) + Some(pos) => format!("{}", Format::Warning(pos.to_string())), + None => format!("\"{}\"", Format::Warning(name)) } }, match self.blacklisted_from(name, matches) { - Some(name) => format!("'{}'", name), + Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the other specified arguments".to_owned() }), true, true, Some(matches.args.keys().map(|k| *k).collect())); } else if self.groups.contains_key(name) { @@ -2232,15 +2238,15 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' cannot be used with one or \ more of the other specified arguments", if let Some(ref flag) = self.flags.get(n) { - format!("{}", flag) + format!("{}", Format::Warning(flag.to_string())) } else if let Some(ref opt) = self.opts.get(n) { - format!("{}", opt) + format!("{}", Format::Warning(opt.to_string())) } else { match self.positionals_idx.values() .filter(|p| p.name == *name) .next() { - Some(pos) => format!("{}", pos), - None => format!("\"{}\"", n) + Some(pos) => format!("{}", Format::Warning(pos.to_string())), + None => format!("\"{}\"", Format::Warning(n)) } }), true, @@ -2265,13 +2271,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if should_err { self.report_error(format!("The argument '{}' requires {} values, \ but {} w{} provided", - f, - num, - if f.multiple { - vals.len() % num as usize + Format::Warning(f.to_string()), + Format::Good(num.to_string()), + Format::Error(if f.multiple { + (vals.len() % num as usize).to_string() } else { - vals.len() - }, + vals.len().to_string() + }), if vals.len() == 1 || ( f.multiple && ( vals.len() % num as usize) == 1) {"as"}else{"ere"}), @@ -2284,9 +2290,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if (vals.len() as u8) > num { self.report_error(format!("The argument '{}' requires no more than {} \ values, but {} w{} provided", - f, - num, - vals.len(), + Format::Warning(f.to_string()), + Format::Good(num.to_string()), + Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, true, @@ -2297,9 +2303,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if (vals.len() as u8) < num { self.report_error(format!("The argument '{}' requires at least {} \ values, but {} w{} provided", - f, - num, - vals.len(), + Format::Warning(f.to_string()), + Format::Good(num.to_string()), + Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, true, @@ -2312,9 +2318,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if num != vals.len() as u8 { self.report_error(format!("The argument '{}' requires {} values, \ but {} w{} provided", - f, - num, - vals.len(), + Format::Warning(f.to_string()), + Format::Good(num.to_string()), + Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, true, @@ -2325,9 +2331,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if num > vals.len() as u8 { self.report_error(format!("The argument '{}' requires no more than {} \ values, but {} w{} provided", - f, - num, - vals.len(), + Format::Warning(f.to_string()), + Format::Good(num.to_string()), + Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, true, @@ -2338,9 +2344,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if num < vals.len() as u8 { self.report_error(format!("The argument '{}' requires at least {} \ values, but {} w{} provided", - f, - num, - vals.len(), + Format::Warning(f.to_string()), + Format::Good(num.to_string()), + Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, true, diff --git a/src/fmt.rs b/src/fmt.rs new file mode 100644 index 00000000000..1d46509604b --- /dev/null +++ b/src/fmt.rs @@ -0,0 +1,50 @@ +use std::fmt; + +#[cfg(feature = "color")] +use ansi_term::Colour::{Red, Green, Yellow}; +#[cfg(feature = "color")] +use ansi_term::ANSIString; + + +pub enum Format { + Error(T), + Warning(T), + Good(T), +} + +#[cfg(feature = "color")] +impl> Format { + fn format(&self) -> ANSIString { + match *self { + Format::Error(ref e) => Red.bold().paint(e.as_ref()), + Format::Warning(ref e) => Yellow.paint(e.as_ref()), + Format::Good(ref e) => Green.paint(e.as_ref()), + } + } + +} + +#[cfg(feature = "color")] +impl> fmt::Display for Format { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", &self.format()) + } +} + +#[cfg(not(feature = "color"))] +impl Format { + fn format(&self) -> &T { + match *self { + Format::Error(ref e) => e, + Format::Warning(ref e) => e, + Format::Good(ref e) => e, + } + } +} + +#[cfg(not(feature = "color"))] +impl fmt::Display for Format { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", &self.format()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 2663f964668..e4c658f458e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -383,12 +383,14 @@ extern crate ansi_term; pub use args::{Arg, SubCommand, ArgMatches, ArgGroup}; pub use app::App; +pub use fmt::Format; #[macro_use] mod macros; mod app; mod args; mod usageparser; +mod fmt; #[cfg(test)] mod tests { diff --git a/src/macros.rs b/src/macros.rs index 8a26d97979e..fde4f7f9169 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,10 +5,12 @@ macro_rules! get_help { format!("{}{}", h, if let Some(ref pv) = $opt.possible_vals { let mut pv_s = pv.iter().fold(String::with_capacity(50), |acc, name| { - acc + &format!(" {}",name)[..] + acc + &format!(" {},",name)[..] }); pv_s.shrink_to_fit(); - format!(" [values:{}]", &pv_s[..]) + // pv_s = one, two, three, four, + // Needs to remove trailing comma (',') + format!(" [values:{}]", &pv_s[..pv_s.len()-1]) }else{"".to_owned()}) } else { " ".to_owned() @@ -144,10 +146,10 @@ macro_rules! value_t { Some(v) => { match v.parse::<$t>() { Ok(val) => Ok(val), - Err(_) => Err(format!("'{}' isn't a valid value",v)), + Err(_) => Err(format!("'{}' isn't a valid value", ::clap::Format::Warning(v))), } }, - None => Err(format!("The argument '{}' not found", $v)) + None => Err(format!("The argument '{}' not found", ::clap::Format::Warning($v))) } }; ($m:ident.values_of($v:expr), $t:ty) => { @@ -159,7 +161,7 @@ macro_rules! value_t { match pv.parse::<$t>() { Ok(rv) => tmp.push(rv), Err(e) => { - err = Some(format!("'{}' isn't a valid value\n\t{}",pv,e)); + err = Some(format!("'{}' isn't a valid value\n\t{}", ::clap::Format::Warning(pv),e)); break } } @@ -169,7 +171,7 @@ macro_rules! value_t { None => Ok(tmp) } }, - None => Err(format!("The argument '{}' was not found", $v)) + None => Err(format!("The argument '{}' was not found", ::clap::Format::Warning($v))) } }; } @@ -227,20 +229,24 @@ macro_rules! value_t_or_exit { match v.parse::<$t>() { Ok(val) => val, Err(e) => { - println!("'{}' isn't a valid value\n\t{}\n\n{}\n\nPlease re-run with --help for \ + println!("{} '{}' isn't a valid value\n\t{}\n\n{}\n\nPlease re-run with {} for \ more information", - v, + ::clap::Format::Error("error:"), + ::clap::Format::Warning(v.to_string()), e, - $m.usage()); + $m.usage(), + ::clap::Format::Good("--help")); ::std::process::exit(1); } } }, None => { - println!("The argument '{}' was not found or is not valid\n\n{}\n\nPlease re-run with \ - --help for more information", - $v, - $m.usage()); + println!("{} The argument '{}' was not found or is not valid\n\n{}\n\nPlease re-run with \ + {} for more information", + ::clap::Format::Error("error:"), + ::clap::Format::Warning($v.to_string()), + $m.usage(), + ::clap::Format::Good("--help")); ::std::process::exit(1); } } @@ -253,10 +259,12 @@ macro_rules! value_t_or_exit { match pv.parse::<$t>() { Ok(rv) => tmp.push(rv), Err(_) => { - println!("'{}' isn't a valid value\n\t{}\n\nPlease re-run with --help for more \ + println!("{} '{}' isn't a valid value\n\t{}\n\nPlease re-run with {} for more \ information", - pv, - $m.usage()); + ::clap::Format::Error("error:"), + ::clap::Format::Warning(pv), + $m.usage(), + ::clap::Format::Good("--help")); ::std::process::exit(1); } } @@ -264,10 +272,12 @@ macro_rules! value_t_or_exit { tmp }, None => { - println!("The argument '{}' not found or is not valid\n\n{}\n\nPlease re-run with \ - --help for more information", - $v, - $m.usage()); + println!("{} The argument '{}' not found or is not valid\n\n{}\n\nPlease re-run with \ + {} for more information", + ::clap::Format::Error("error:"), + ::clap::Format::Warning($v.to_string()), + $m.usage(), + ::clap::Format::Good("--help")); ::std::process::exit(1); } } diff --git a/src/usageparser.rs b/src/usageparser.rs index ec7fcda9fbc..3c3ba5d34ac 100644 --- a/src/usageparser.rs +++ b/src/usageparser.rs @@ -123,7 +123,7 @@ impl<'u> Iterator for UsageParser<'u> { } if mult { return Some(UsageToken::Multiple) } }, - Some(' ') | Some('=') | Some(']') | Some('>') | Some('\t') => { + Some(' ') | Some('=') | Some(']') | Some('>') | Some('\t') | Some(',') => { self.e += 1; continue }, From 743eefe8dd40c1260065ce086d572e9e9358bc4c Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 22 May 2015 23:27:11 -0400 Subject: [PATCH 6/6] docs: updates for deprecations and new features --- README.md | 10 +++++++--- src/args/arg.rs | 7 ++++++- src/lib.rs | 4 ++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d99bff9ba59..e4861a86207 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Below are a few of the features which `clap` supports, full descriptions and usa * **Typed Values**: You can use several convenience macros provided by `clap` to get typed values (i.e. `i32`, `u8`, etc.) from positional or option arguments so long as the type you request implements `std::str::FromStr` See the `examples/12_TypedValues.rs`. You can also use `clap`s `simple_enum!` or `arg_enum!` macro to create an enum with variants that automatically implements `std::str::FromStr`. See `examples/13a_EnumValuesAutomatic.rs` for details and performs an ascii case insensitive parse from a `string`->`enum`. * **Suggestions**: Suggests corrections when the user enter's a typo. For example, if you defined a `--myoption ` argument, and the user mistakenly typed `--moyption value` (notice `y` and `o` switched), they would receive a `Did you mean '--myoption' ?` error and exit gracefully. This also works for subcommands and flags. (Thanks to [Byron](https://github.com/Byron) for the implementation) (This feature can optionally be disabled, see 'Optional Dependencies / Features') * **Colorized (Red) Errors**: Error message are printed in red text (this feature can optionally be disabled, see 'Optional Dependencies / Features'). +* **Global Arguments**: Arguments can optionally be defined once, and be available to all child subcommands. ## Quick Example @@ -369,7 +370,10 @@ Although I do my best to keep breaking changes to a minimum, being that this a s Old method names will be left around for some time. +* As of 0.10.0 + - `SubCommand::new()` -> `SubCommand::with_name()` + - `App::error_on_no_subcommand()` -> `App::subcommand_required()` * As of 0.6.8 - - `Arg::new()` -> `Arg::with_name()` - - `Arg::mutually_excludes()` -> `Arg::conflicts_with()` - - `Arg::mutually_excludes_all()` -> `Arg::conflicts_with_all()` + - `Arg::new()` -> `Arg::with_name()` + - `Arg::mutually_excludes()` -> `Arg::conflicts_with()` + - `Arg::mutually_excludes_all()` -> `Arg::conflicts_with_all()` diff --git a/src/args/arg.rs b/src/args/arg.rs index 5f0f14c9d58..a83bb823875 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -649,12 +649,17 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self } - /// Specifies that an argument applies to and will be available to all child subcommands. + /// Specifies that an argument can be matched to all child subcommands. /// /// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands) /// /// **NOTE:** Global arguments *cannot* be required. /// + /// **NOTE:** Global arguments, when matched, *only* exist in the command's matches that they + /// were matched to. For example, if you defined a `--flag` global argument in the top most + /// parent command, but the user supplied the arguments `top cmd1 cmd2 --flag` *only* `cmd2`'s + /// `ArgMatches` would return `true` if tested for `.is_present("flag")`. + /// /// # Example /// /// ```no_run diff --git a/src/lib.rs b/src/lib.rs index e4c658f458e..56924441f18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ //! * **Typed Values**: You can use several convenience macros provided by `clap` to get typed values (i.e. `i32`, `u8`, etc.) from positional or option arguments so long as the type you request implements `std::str::FromStr` See the `examples/12_TypedValues.rs`. You can also use `clap`s `simple_enum!` or `arg_enum!` macro to create an enum with variants that automatically implements `std::str::FromStr`. See `examples/13a_EnumValuesAutomatic.rs` for details and performs an ascii case insensitive parse from a `string`->`enum`. //! * **Suggestions**: Suggests corrections when the user enter's a typo. For example, if you defined a `--myoption ` argument, and the user mistakenly typed `--moyption value` (notice `y` and `o` switched), they would receive a `Did you mean '--myoption' ?` error and exit gracefully. This also works for subcommands and flags. (Thanks to [Byron](https://github.com/Byron) for the implementation) (This feature can optionally be disabled, see 'Optional Dependencies / Features') //! * **Colorized (Red) Errors**: Error message are printed in red text (this feature can optionally be disabled, see 'Optional Dependencies / Features'). +//! * **Global Arguments**: Arguments can optionally be defined once, and be available to all child subcommands. //! //! ## Quick Example //! @@ -372,6 +373,9 @@ //! //! Old method names will be left around for some time. //! +//! * As of 0.10.0 +//! - `SubCommand::new()` -> `SubCommand::with_name()` +//! - `App::error_on_no_subcommand()` -> `App::subcommand_required()` //! * As of 0.6.8 //! - `Arg::new()` -> `Arg::with_name()` //! - `Arg::mutually_excludes()` -> `Arg::conflicts_with()`