mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 23:04:23 +00:00
feat: added overrides to support conflicts in POSIX compatible manner
This commit is contained in:
parent
9c557ed7cb
commit
0b916a00de
5 changed files with 102 additions and 19 deletions
69
src/app.rs
69
src/app.rs
|
@ -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,
|
||||
|
@ -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>{
|
||||
|
@ -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<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);
|
||||
|
@ -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};
|
||||
}
|
|
@ -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<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.
|
||||
|
@ -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("<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");
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Reference in a new issue