feat: added overrides to support conflicts in POSIX compatible manner

This commit is contained in:
Alexander Kuvaev 2015-08-18 22:33:52 +03:00
parent 9c557ed7cb
commit 0b916a00de
5 changed files with 102 additions and 19 deletions

View file

@ -253,7 +253,7 @@ pub struct App<'a, 'v, 'ab, 'u, 'h, 'ar> {
bin_name: Option<String>, bin_name: Option<String>,
usage: Option<String>, usage: Option<String>,
groups: HashMap<&'ar str, ArgGroup<'ar, 'ar>>, groups: HashMap<&'ar str, ArgGroup<'ar, 'ar>>,
global_args: Vec<Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>>, global_args: Vec<Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar, 'ar>>,
help_str: Option<&'u str>, help_str: Option<&'u str>,
no_sc_error: bool, no_sc_error: bool,
wait_on_error: bool, wait_on_error: bool,
@ -266,7 +266,8 @@ pub struct App<'a, 'v, 'ab, 'u, 'h, 'ar> {
global_ver: bool, global_ver: bool,
// None = not set, Some(true) set for all children, Some(false) = disable version // None = not set, Some(true) set for all children, Some(false) = disable version
versionless_scs: Option<bool>, versionless_scs: Option<bool>,
unified_help: bool unified_help: bool,
overrides: HashSet<&'ar str>,
} }
impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ 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, help_on_no_sc: false,
global_ver: false, global_ver: false,
versionless_scs: None, 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.add_arg(a);
self self
} }
// actually adds the arguments // 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) || if self.flags.contains_key(a.name) ||
self.opts.contains_key(a.name) || self.opts.contains_key(a.name) ||
self.positionals_name.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, help: a.help,
global: a.global, global: a.global,
empty_vals: a.empty_vals, empty_vals: a.empty_vals,
validator: None validator: None,
overrides: None
}; };
if pb.min_vals.is_some() && !pb.multiple { if pb.min_vals.is_some() && !pb.multiple {
panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \ 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, requires: None,
required: a.required, required: a.required,
empty_vals: a.empty_vals, empty_vals: a.empty_vals,
validator: None validator: None,
overrides: None
}; };
if let Some(ref vec) = ob.val_names { if let Some(ref vec) = ob.val_names {
ob.num_vals = Some(vec.len() as u8); 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, global: a.global,
multiple: a.multiple, multiple: a.multiple,
requires: None, requires: None,
overrides: None
}; };
// Check if there is anything in the blacklist (mutually excludes list) and add any // Check if there is anything in the blacklist (mutually excludes list) and add any
// values // 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<Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>>) pub fn args(mut self, args: Vec<Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar, 'ar>>)
-> Self { -> Self {
for arg in args.into_iter() { for arg in args.into_iter() {
self = self.arg(arg); self = self.arg(arg);
@ -2360,6 +2365,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
multiple: false, multiple: false,
global: false, global: false,
requires: None, requires: None,
overrides: None
}; };
self.long_list.insert("help"); self.long_list.insert("help");
self.flags.insert("hclap_help", arg); 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, multiple: false,
global: false, global: false,
requires: None, requires: None,
overrides: None
}; };
self.long_list.insert("version"); self.long_list.insert("version");
self.flags.insert("vclap_version", arg); 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, true,
Some(matches.args.keys().map(|k| *k).collect())); 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 matches.args.contains_key(v.name) {
if !v.multiple { 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 { if let Some(ref bl) = v.blacklist {
for name in bl { for name in bl {
self.blacklist.insert(name); self.blacklist.insert(name);
self.overrides.remove(name);
self.required.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, true,
Some(matches.args.keys().map(|k| *k).collect())); 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 // 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 { 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); self.required.remove(v.name);
// Add all of this flags "mutually excludes" list to the master list // 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 { if let Some(ref bl) = v.blacklist {
for name in bl { for name in bl {
self.blacklist.insert(name); self.blacklist.insert(name);
self.overrides.remove(name);
self.required.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, true,
Some(matches.args.keys().map(|k| *k).collect())); 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 matches.args.contains_key(v.name) {
if !v.multiple { 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()) 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 { if let Some(ref bl) = v.blacklist {
for name in bl { for name in bl {
self.blacklist.insert(name); self.blacklist.insert(name);
self.overrides.remove(name);
self.required.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, true,
Some(matches.args.keys().map(|k| *k).collect())); 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 // 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 { 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); self.required.remove(v.name);
// Add all of this flags "mutually excludes" list to the master list // 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 { if let Some(ref bl) = v.blacklist {
for name in bl { for name in bl {
self.blacklist.insert(name); self.blacklist.insert(name);
self.overrides.remove(name);
self.required.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};
}

View file

@ -32,7 +32,7 @@ use usageparser::{UsageParser, UsageToken};
/// // Using a usage string (setting a similar argument to the one above) /// // 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'") /// Arg::from_usage("-i --input=[input] 'Provides an input file to the program'")
/// # ).get_matches(); /// # ).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 /// The unique name of the argument, required
#[doc(hidden)] #[doc(hidden)]
pub name: &'n str, pub name: &'n str,
@ -93,10 +93,13 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> {
#[doc(hidden)] #[doc(hidden)]
pub global: bool, pub global: bool,
#[doc(hidden)] #[doc(hidden)]
pub validator: Option<Rc<Fn(String) -> Result<(), String>>> pub validator: Option<Rc<Fn(String) -> Result<(), String>>>,
/// A list of names for other arguments that *mutually override* this flag
#[doc(hidden)]
pub overrides: Option<Vec<&'o str>>
} }
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. /// Creates a new instace of `Arg` using a unique string name.
/// The name will be used by the library consumer to get information about /// The name will be used by the library consumer to get information about
/// whether or not the argument was used at runtime. /// 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, group: None,
global: false, global: false,
empty_vals: true, 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("<input> 'the input file to use'") /// Arg::from_usage("<input> 'the input file to use'")
/// ]) /// ])
/// # .get_matches(); /// # .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 \ assert!(u.len() > 0, "Arg::from_usage() requires a non-zero-length usage string but none \
was provided"); was provided");
@ -295,6 +299,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
global: false, global: false,
empty_vals: true, empty_vals: true,
validator: None, validator: None,
overrides: None,
} }
} }
@ -441,6 +446,26 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
self 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 /// 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. /// 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> { 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>) -> Self { fn from(a: &'z Arg<'n, 'l, 'h, 'g, 'p, 'r, 'o>) -> Self {
Arg { Arg {
name: a.name, name: a.name,
short: a.short, 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, group: a.group,
global: a.global, global: a.global,
empty_vals: a.empty_vals, empty_vals: a.empty_vals,
validator: a.validator.clone() validator: a.validator.clone(),
overrides: a.overrides.clone()
} }
} }
} }

View file

@ -25,6 +25,8 @@ pub struct FlagBuilder<'n> {
/// of the argument, no preceding `-` /// of the argument, no preceding `-`
pub short: Option<char>, pub short: Option<char>,
pub global: bool, pub global: bool,
/// A list of names for other arguments that *mutually override* this flag
pub overrides: Option<Vec<&'n str>>
} }
impl<'n> Display for FlagBuilder<'n> { impl<'n> Display for FlagBuilder<'n> {

View file

@ -33,7 +33,9 @@ pub struct OptBuilder<'n> {
pub val_names: Option<Vec<&'n str>>, pub val_names: Option<Vec<&'n str>>,
pub empty_vals: bool, pub empty_vals: bool,
pub global: bool, pub global: bool,
pub validator: Option<Rc<Fn(String) -> StdResult<(), String>>> pub validator: Option<Rc<Fn(String) -> StdResult<(), String>>>,
/// A list of names for other arguments that *mutually override* this flag
pub overrides: Option<Vec<&'n str>>
} }
impl<'n> Display for OptBuilder<'n> { impl<'n> Display for OptBuilder<'n> {

View file

@ -30,7 +30,9 @@ pub struct PosBuilder<'n> {
pub min_vals: Option<u8>, pub min_vals: Option<u8>,
pub empty_vals: bool, pub empty_vals: bool,
pub global: bool, pub global: bool,
pub validator: Option<Rc<Fn(String) -> StdResult<(), String>>> pub validator: Option<Rc<Fn(String) -> StdResult<(), String>>>,
/// A list of names for other arguments that *mutually override* this flag
pub overrides: Option<HashSet<&'n str>>,
} }
impl<'n> Display for PosBuilder<'n> { impl<'n> Display for PosBuilder<'n> {