diff --git a/src/app/app.rs b/src/app/app.rs index af0c82dfbfd..62d8848b21f 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -1302,6 +1302,22 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self } + fn groups_for(&self, name: &str) -> Option> { + if self.groups.is_empty() { return None; } + let mut res = vec![]; + for (g_name, grp) in &self.groups { + for a in &grp.args { + if a == &name { + res.push(*g_name); + } + } + } + res.dedup(); + if res.is_empty() { return None } + + Some(res) + } + fn get_group_members(&self, group: &str) -> Vec { @@ -2387,6 +2403,23 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ )); } + if let Some(ref vec) = self.groups_for(opt.name) { + for grp in vec { + if let Some(ref mut o) = matches.args.get_mut(grp) { + o.occurrences = if opt.multiple { + o.occurrences + 1 + } else { + 1 + }; + // Values must be inserted in order...the user may care about that! + if let Some(ref mut vals) = o.values { + let len = vals.len() as u8 + 1; + vals.insert(len, arg_slice.to_owned()); + } + } + + } + } // save the value to matched option if let Some(ref mut o) = matches.args.get_mut(opt.name) { // if it's multiple; the occurrences are increased when originally @@ -2632,6 +2665,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } bm.insert(1, arg_slice.to_owned()); + if let Some(ref vec) = self.groups_for(p.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: Some(bm.clone()), + }); + } + } matches.args.insert(p.name, MatchedArg{ occurrences: 1, values: Some(bm), @@ -2988,6 +3029,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .filter(|&v| v.long.unwrap() == arg).nth(0) { // prevents "--config= value" typo if arg_vec[1].len() == 0 && !v.empty_vals { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: None, + }); + } + } matches.args.insert(v.name, MatchedArg { occurrences: 1, values: None @@ -3020,12 +3069,22 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ debugln!("checking who defined it..."); if let Some(name) = self.overriden_from(v.name, matches) { debugln!("found {}", name); + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.remove(grp); + } + } matches.args.remove(name); remove_override!(self, name); } } if let Some(ref or) = v.overrides { for pa in or { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.remove(grp); + } + } matches.args.remove(pa); remove_override!(self, pa); self.overrides.push(pa); @@ -3041,6 +3100,17 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ App::get_args(matches))); } if let Some(av) = arg_val { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + if let Some(ref mut o) = matches.args.get_mut(grp) { + o.occurrences += 1; + if let Some(ref mut vals) = o.values { + let len = (vals.len() + 1) as u8; + vals.insert(len, av.to_owned()); + } + } + } + } if let Some(ref mut o) = matches.args.get_mut(v.name) { o.occurrences += 1; if let Some(ref mut vals) = o.values { @@ -3058,6 +3128,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut bm = BTreeMap::new(); if let Some(val) = arg_val { bm.insert(1, val.to_owned()); + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: Some(bm.clone()), + }); + } + } matches.args.insert(v.name, MatchedArg{ occurrences: 1, values: Some(bm) @@ -3068,6 +3146,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(e); } } else { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: Some(bm.clone()), + }); + } + } matches.args.insert(v.name, MatchedArg{ occurrences: 0, values: Some(bm) @@ -3161,6 +3247,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ }; } if !done { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: None, + }); + } + } matches.args.insert(v.name, MatchedArg{ // name: v.name.to_owned(), occurrences: 1, @@ -3212,6 +3306,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } }) .next() { + if let Some(ref vec) = self.groups_for(opt) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: None, + }); + } + } matches.args.insert(opt, MatchedArg { occurrences: 0, values: None @@ -3225,6 +3327,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } }) .next() { + if let Some(ref vec) = self.groups_for(flg) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: None, + }); + } + } matches.args.insert(flg, MatchedArg { occurrences: 0, values: None @@ -3360,6 +3470,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ App::get_args(matches))); } } else { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: Some(BTreeMap::new()), + }); + } + } matches.args.insert(v.name, MatchedArg{ // occurrences will be incremented on getting a value occurrences: 0, @@ -3447,6 +3565,17 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ App::get_args(matches))); } + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + if let Some(ref mut f) = matches.args.get_mut(grp) { + f.occurrences = if v.multiple { + f.occurrences + 1 + } else { + 1 + }; + } + } + } let mut done = false; if let Some(ref mut f) = matches.args.get_mut(v.name) { done = true; @@ -3457,6 +3586,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ }; } if !done { + if let Some(ref vec) = self.groups_for(v.name) { + for grp in vec { + matches.args.insert(grp, MatchedArg{ + occurrences: 1, + values: None, + }); + } + } matches.args.insert(v.name, MatchedArg{ // name: v.name.to_owned(), occurrences: 1, @@ -3543,7 +3680,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ matches: &mut ArgMatches<'ar, 'ar>) -> Result<(), ClapError> { for (name, ma) in matches.args.iter() { - if let Some(ref vals) = ma.values { + if self.groups.contains_key(name) { + continue; + } else if let Some(ref vals) = ma.values { if let Some(f) = self.opts.get(name) { if let Some(num) = f.num_vals { let should_err = if f.multiple { diff --git a/src/lib.rs b/src/lib.rs index 6c24c889f3a..8ef224aab05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,3 @@ mod app; mod args; mod usageparser; mod fmt; - -#[cfg(test)] -mod tests; diff --git a/tests/groups.rs b/tests/groups.rs new file mode 100644 index 00000000000..b20d4197a12 --- /dev/null +++ b/tests/groups.rs @@ -0,0 +1,57 @@ +extern crate clap; + +use clap::{App, ArgGroup, ClapErrorType}; + +#[test] +fn required_group_missing_arg() { + let result = App::new("group") + .args_from_usage("-f, --flag 'some flag' + -c, --color 'some other flag'") + .arg_group(ArgGroup::with_name("req") + .add_all(&["flag", "color"]) + .required(true)) + .get_matches_from_safe(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); +} + +#[test] +fn group_single_value() { + let m = App::new("group") + .args_from_usage("-f, --flag 'some flag' + -c, --color [color] 'some option'") + .arg_group(ArgGroup::with_name("grp") + .add_all(&["flag", "color"])) + .get_matches_from(vec!["", "-c", "blue"]); + assert!(m.is_present("grp")); + assert_eq!(m.value_of("grp").unwrap(), "blue"); + let m = App::new("group") + .args_from_usage("-f, --flag 'some flag' + -c, --color [color] 'some option'") + .arg_group(ArgGroup::with_name("grp") + .add_all(&["flag", "color"])) + .get_matches_from(vec!["", "-f"]); + assert!(m.is_present("grp")); + assert!(m.value_of("grp").is_none()); + let m = App::new("group") + .args_from_usage("-f, --flag 'some flag' + -c, --color [color] 'some option'") + .arg_group(ArgGroup::with_name("grp") + .add_all(&["flag", "color"])) + .get_matches_from(vec![""]); + assert!(!m.is_present("grp")); + assert!(m.value_of("grp").is_none()); +} + +#[test] +fn group_multi_value_single_arg() { + let m = App::new("group") + .args_from_usage("-f, --flag 'some flag' + -c, --color [color]... 'some option'") + .arg_group(ArgGroup::with_name("grp") + .add_all(&["flag", "color"])) + .get_matches_from(vec!["", "-c", "blue", "red", "green"]); + assert!(m.is_present("grp")); + assert_eq!(m.values_of("grp").unwrap(), &["blue", "red", "green"]); +} diff --git a/src/tests.rs b/tests/tests.rs similarity index 99% rename from src/tests.rs rename to tests/tests.rs index f8c6105f1f1..e3421a5b051 100644 --- a/src/tests.rs +++ b/tests/tests.rs @@ -1,6 +1,9 @@ +#[macro_use] +extern crate clap; + use std::collections::HashSet; -use super::{App, Arg, ArgGroup, SubCommand}; +use clap::{App, Arg, ArgGroup, SubCommand}; use std::vec::Vec; arg_enum!{