From 0b916a00de26f6941538f6bc5f3365fa302083c1 Mon Sep 17 00:00:00 2001 From: Alexander Kuvaev Date: Tue, 18 Aug 2015 22:33:52 +0300 Subject: [PATCH] feat: added overrides to support conflicts in POSIX compatible manner --- src/app.rs | 69 +++++++++++++++++++++++++++---- src/args/arg.rs | 42 +++++++++++++++---- src/args/argbuilder/flag.rs | 2 + src/args/argbuilder/option.rs | 4 +- src/args/argbuilder/positional.rs | 4 +- 5 files changed, 102 insertions(+), 19 deletions(-) diff --git a/src/app.rs b/src/app.rs index b5a538410dd..8a11dcfb1f9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -253,7 +253,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>, + global_args: Vec>, help_str: Option<&'u str>, no_sc_error: bool, wait_on_error: bool, @@ -266,7 +266,8 @@ pub struct App<'a, 'v, 'ab, 'u, 'h, 'ar> { global_ver: bool, // None = not set, Some(true) set for all children, Some(false) = disable version versionless_scs: Option, - unified_help: bool + unified_help: bool, + overrides: HashSet<&'ar str>, } impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ @@ -317,7 +318,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ help_on_no_sc: false, global_ver: false, versionless_scs: None, - unified_help: false + unified_help: false, + overrides: HashSet::new() } } @@ -784,13 +786,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ /// ) /// # ; /// ``` - pub fn arg(mut self, a: Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>) -> Self { + pub fn arg(mut self, a: Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar, 'ar>) -> Self { self.add_arg(a); self } // actually adds the arguments - fn add_arg(&mut self, a: Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>) { + fn add_arg(&mut self, a: Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar, 'ar>) { if self.flags.contains_key(a.name) || self.opts.contains_key(a.name) || self.positionals_name.contains_key(a.name) { @@ -872,7 +874,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ help: a.help, global: a.global, empty_vals: a.empty_vals, - validator: None + validator: None, + overrides: None }; if pb.min_vals.is_some() && !pb.multiple { panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \ @@ -935,7 +938,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ requires: None, required: a.required, empty_vals: a.empty_vals, - validator: None + validator: None, + overrides: None }; if let Some(ref vec) = ob.val_names { ob.num_vals = Some(vec.len() as u8); @@ -1008,6 +1012,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ global: a.global, multiple: a.multiple, requires: None, + overrides: None }; // Check if there is anything in the blacklist (mutually excludes list) and add any // values @@ -1048,7 +1053,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ /// ) /// # ; /// ``` - pub fn args(mut self, args: Vec>) + pub fn args(mut self, args: Vec>) -> Self { for arg in args.into_iter() { self = self.arg(arg); @@ -2360,6 +2365,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ multiple: false, global: false, requires: None, + overrides: None }; self.long_list.insert("help"); self.flags.insert("hclap_help", arg); @@ -2380,6 +2386,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ multiple: false, global: false, requires: None, + overrides: None }; self.long_list.insert("version"); self.flags.insert("vclap_version", arg); @@ -2443,6 +2450,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ true, Some(matches.args.keys().map(|k| *k).collect())); } + if self.overrides.contains(v.name) { + matches.args.remove(v.name); + } if matches.args.contains_key(v.name) { if !v.multiple { @@ -2512,10 +2522,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } }); } - + if let Some(ref ov) = v.overrides { + for name in ov { + self.overrides.insert(name); + self.required.remove(name); + } + } if let Some(ref bl) = v.blacklist { for name in bl { self.blacklist.insert(name); + self.overrides.remove(name); self.required.remove(name); } } @@ -2555,6 +2571,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ true, Some(matches.args.keys().map(|k| *k).collect())); } + if self.overrides.contains(v.name) { + matches.args.remove(v.name); + } // 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 { @@ -2583,9 +2602,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.required.remove(v.name); // Add all of this flags "mutually excludes" list to the master list + if let Some(ref ov) = v.overrides { + for name in ov { + self.overrides.insert(name); + self.required.remove(name); + } + } if let Some(ref bl) = v.blacklist { for name in bl { self.blacklist.insert(name); + self.overrides.remove(name); self.required.remove(name); } } @@ -2688,6 +2714,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ true, Some(matches.args.keys().map(|k| *k).collect())); } + if self.overrides.contains(v.name) { + matches.args.remove(v.name); + } if matches.args.contains_key(v.name) { if !v.multiple { @@ -2704,9 +2733,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ values: Some(BTreeMap::new()) }); } + if let Some(ref ov) = v.overrides { + for name in ov { + self.overrides.insert(name); + self.required.remove(name); + } + } if let Some(ref bl) = v.blacklist { for name in bl { self.blacklist.insert(name); + self.overrides.remove(name); self.required.remove(name); } } @@ -2754,6 +2790,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ true, Some(matches.args.keys().map(|k| *k).collect())); } + if self.overrides.contains(v.name) { + matches.args.remove(v.name); + } // 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 { @@ -2782,9 +2821,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.required.remove(v.name); // Add all of this flags "mutually excludes" list to the master list + if let Some(ref ov) = v.overrides { + for name in ov { + self.overrides.insert(name); + self.required.remove(name); + } + } if let Some(ref bl) = v.blacklist { for name in bl { self.blacklist.insert(name); + self.overrides.remove(name); self.required.remove(name); } } @@ -2999,3 +3045,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } } + +#[cfg(tests)] +mod tests { + use clap::{App, Arg, SubCommand}; +} \ No newline at end of file diff --git a/src/args/arg.rs b/src/args/arg.rs index e76072a6673..71d0e407870 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -32,7 +32,7 @@ use usageparser::{UsageParser, UsageToken}; /// // Using a usage string (setting a similar argument to the one above) /// Arg::from_usage("-i --input=[input] 'Provides an input file to the program'") /// # ).get_matches(); -pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> { +pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r, 'o> { /// The unique name of the argument, required #[doc(hidden)] pub name: &'n str, @@ -93,10 +93,13 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> { #[doc(hidden)] pub global: bool, #[doc(hidden)] - pub validator: Option Result<(), String>>> + pub validator: Option Result<(), String>>>, + /// A list of names for other arguments that *mutually override* this flag + #[doc(hidden)] + pub overrides: Option> } -impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { +impl<'n, 'l, 'h, 'g, 'p, 'r, 'o> Arg<'n, 'l, 'h, 'g, 'p, 'r, 'o> { /// 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. @@ -135,7 +138,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { group: None, global: false, empty_vals: true, - validator: None + validator: None, + overrides: None } } @@ -191,7 +195,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { /// Arg::from_usage(" 'the input file to use'") /// ]) /// # .get_matches(); - pub fn from_usage(u: &'n str) -> Arg<'n, 'n, 'n, 'g, 'p, 'r> { + pub fn from_usage(u: &'n str) -> Arg<'n, 'n, 'n, 'g, 'p, 'r, 'o> { assert!(u.len() > 0, "Arg::from_usage() requires a non-zero-length usage string but none \ was provided"); @@ -295,6 +299,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { global: false, empty_vals: true, validator: None, + overrides: None, } } @@ -441,6 +446,26 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self } + /// Sets a mutually overridable argument by name. I.e. this argument and + /// the following argument will override each other in POSIX style + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let myprog = App::new("myprog").arg(Arg::with_name("conifg") + /// .mutually_overrides_with("debug") + /// # ).get_matches(); + pub fn mutually_overrides_with(mut self, name: &'o str) -> Self { + if let Some(ref mut vec) = self.overrides { + vec.push(name); + } else { + self.overrides = Some(vec![name]); + } + self + } + /// Sets an argument by name that is required when this one is presnet I.e. when /// using this argument, the following argument *must* be present. /// @@ -796,8 +821,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { } } -impl<'n, 'l, 'h, 'g, 'p, 'r, 'z> From<&'z Arg<'n, 'l, 'h, 'g, 'p, 'r>> for Arg<'n, 'l, 'h, 'g, 'p, 'r> { - fn from(a: &'z Arg<'n, 'l, 'h, 'g, 'p, 'r>) -> Self { +impl<'n, 'l, 'h, 'g, 'p, 'r, 'z, 'o> From<&'z Arg<'n, 'l, 'h, 'g, 'p, 'r, 'o>> for Arg<'n, 'l, 'h, 'g, 'p, 'r, 'o> { + fn from(a: &'z Arg<'n, 'l, 'h, 'g, 'p, 'r, 'o>) -> Self { Arg { name: a.name, short: a.short, @@ -817,7 +842,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r, 'z> From<&'z Arg<'n, 'l, 'h, 'g, 'p, 'r>> for Arg<' group: a.group, global: a.global, empty_vals: a.empty_vals, - validator: a.validator.clone() + validator: a.validator.clone(), + overrides: a.overrides.clone() } } } \ No newline at end of file diff --git a/src/args/argbuilder/flag.rs b/src/args/argbuilder/flag.rs index 64161dac9e9..8510c998286 100644 --- a/src/args/argbuilder/flag.rs +++ b/src/args/argbuilder/flag.rs @@ -25,6 +25,8 @@ pub struct FlagBuilder<'n> { /// of the argument, no preceding `-` pub short: Option, pub global: bool, + /// A list of names for other arguments that *mutually override* this flag + pub overrides: Option> } impl<'n> Display for FlagBuilder<'n> { diff --git a/src/args/argbuilder/option.rs b/src/args/argbuilder/option.rs index 420b676a490..880918e74ba 100644 --- a/src/args/argbuilder/option.rs +++ b/src/args/argbuilder/option.rs @@ -33,7 +33,9 @@ pub struct OptBuilder<'n> { pub val_names: Option>, pub empty_vals: bool, pub global: bool, - pub validator: Option StdResult<(), String>>> + pub validator: Option StdResult<(), String>>>, + /// A list of names for other arguments that *mutually override* this flag + pub overrides: Option> } impl<'n> Display for OptBuilder<'n> { diff --git a/src/args/argbuilder/positional.rs b/src/args/argbuilder/positional.rs index 8f9b62c118b..34369861fdf 100644 --- a/src/args/argbuilder/positional.rs +++ b/src/args/argbuilder/positional.rs @@ -30,7 +30,9 @@ pub struct PosBuilder<'n> { pub min_vals: Option, pub empty_vals: bool, pub global: bool, - pub validator: Option StdResult<(), String>>> + pub validator: Option StdResult<(), String>>>, + /// A list of names for other arguments that *mutually override* this flag + pub overrides: Option>, } impl<'n> Display for PosBuilder<'n> {