Skip to content

Commit

Permalink
feat: added overrides to support conflicts in POSIX compatible manner
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinatorul committed Aug 18, 2015
1 parent 9c557ed commit 0b916a0
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 19 deletions.
69 changes: 60 additions & 9 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ pub struct App<'a, 'v, 'ab, 'u, 'h, 'ar> {
bin_name: Option<String>,
usage: Option<String>,
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>,
no_sc_error: bool,
wait_on_error: bool,
Expand All @@ -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<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>{
Expand Down Expand Up @@ -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()
}
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {} \
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
for arg in args.into_iter() {
self = self.arg(arg);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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};
}
42 changes: 34 additions & 8 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -93,10 +93,13 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> {
#[doc(hidden)]
pub global: bool,
#[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.
/// The name will be used by the library consumer to get information about
/// whether or not the argument was used at runtime.
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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'")
/// ])
/// # .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");

Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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,
Expand All @@ -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()
}
}
}
2 changes: 2 additions & 0 deletions src/args/argbuilder/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub struct FlagBuilder<'n> {
/// of the argument, no preceding `-`
pub short: Option<char>,
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> {
Expand Down
4 changes: 3 additions & 1 deletion src/args/argbuilder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ pub struct OptBuilder<'n> {
pub val_names: Option<Vec<&'n str>>,
pub empty_vals: 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> {
Expand Down
4 changes: 3 additions & 1 deletion src/args/argbuilder/positional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ pub struct PosBuilder<'n> {
pub min_vals: Option<u8>,
pub empty_vals: 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> {
Expand Down

0 comments on commit 0b916a0

Please sign in to comment.