diff --git a/src/app.rs b/src/app.rs index cdee8161064..559bfa53daa 100644 --- a/src/app.rs +++ b/src/app.rs @@ -285,6 +285,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if a.takes_value { panic!("Argument \"{}\" has conflicting requirements, both index() and takes_value(true) were supplied\n\n\tArguments with an index automatically take a value, you do not need to specify it manually", a.name); } + if a.val_names.is_some() { + panic!("Positional arguments (\"{}\") do not support named values, instead consider multiple positional arguments", a.name); + } self.positionals_name.insert(a.name, i); // Create the Positional Arguemnt Builder with each HashSet = None to only allocate those that require it @@ -296,8 +299,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ blacklist: None, requires: None, possible_vals: None, + num_vals: a.num_vals, help: a.help, }; + if pb.num_vals.unwrap_or(0) > 1 && !pb.multiple { + panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} values", pb.name, pb.num_vals.unwrap()); + } // Check if there is anything in the blacklist (mutually excludes list) and add any values if let Some(ref bl) = a.blacklist { let mut bhs = HashSet::new(); @@ -334,9 +341,17 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ blacklist: None, help: a.help, possible_vals: None, + num_vals: a.num_vals, + val_names: a.val_names, requires: None, required: a.required, }; + if let Some(ref vec) = ob.val_names { + ob.num_vals = Some(vec.len() as u8); + } + if ob.num_vals.unwrap_or(0) > 1 && !ob.multiple { + panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} values", ob.name, ob.num_vals.unwrap()); + } // Check if there is anything in the blacklist (mutually excludes list) and add any values if let Some(ref bl) = a.blacklist { let mut bhs = HashSet::new(); @@ -638,7 +653,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(u) = self.usage_str { usage.push_str(u); } else if let Some(vec) = matches { - // FIXME: Add groups let mut c_flags = vec![]; let mut c_pos = vec![]; let mut c_opt = vec![]; @@ -726,7 +740,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let o_string = c_opt.iter().map(|f| format!("{}", f)).fold(String::new(),|acc, i| acc + &format!(" {}", i)); let p_string = c_pos.iter().map(|f| format!("{}", f)).fold(String::new(),|acc, i| acc + &format!(" {}", i)); let g_string = grps.iter().map(|f| format!(" {}", f)).fold(String::new(), |acc, i| acc + &format!("{}", i)); - return format!("\tUSAGE: {} {} {} {} {}", self.bin_name.clone().unwrap_or(self.name.clone()), p_string, f_string, o_string, g_string) + usage.push_str(&format!("{} {} {} {} {}", self.bin_name.clone().unwrap_or(self.name.clone()), p_string, f_string, o_string, g_string)[..]); } else { let flags = !self.flags.is_empty(); let pos = !self.positionals_idx.is_empty(); @@ -925,7 +939,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else { " ".to_owned() }, - format!("<{}>{}", v.name, if v.multiple{"..."} else {""}), + format!("{}", + if let Some(ref vec) = v.val_names { + vec.iter().fold(String::new(), |acc, s| acc + &format!("<{}> ", s)[..]) + } else { + format!("<{}>{}", v.name, if v.multiple{"..."} else {""}) + }), if v.long.is_some() { self.get_spaces((longest_opt) - (v.long.unwrap().len() + v.name.len() + mult + 1)) } else { @@ -1106,6 +1125,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } } + if let Some(num) = opt.num_vals { + if let Some(ref ma) = matches.args.get(opt.name) { + if let Some(ref vals) = ma.values { + if num == vals.len() as u8 { + self.report_error(format!("The argument \"{}\" was found, but {} only expects {} values", arg, opt, vals.len()), + true, true, Some(matches.args.keys().map(|k| *k).collect::>())); + } + } + } + } if let Some(ref mut o) = matches.args.get_mut(opt.name) { // Options have values, so we can unwrap() if let Some(ref mut vals) = o.values { @@ -1160,6 +1189,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // previous positionals too. This will denote where to start // let mut req_pos_from_name = None; if let Some(p) = self.positionals_idx.get(&pos_counter) { + + if self.blacklist.contains(p.name) { matches.args.remove(p.name); self.report_error(format!("The argument \"{}\" cannot be used with {}", p, match self.blacklisted_from(p.name, &matches) { @@ -1181,6 +1212,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Have we made the update yet? let mut done = false; if p.multiple { + if let Some(num) = p.num_vals { + if let Some(ref ma) = matches.args.get(p.name) { + if let Some(ref vals) = ma.values { + if vals.len() as u8 == num { + self.report_error(format!("The argument \"{}\" was found, but {} doesn't expect any more values", arg, p), + true, true, Some(matches.args.keys().map(|k| *k).collect::>())); + } + } + } + } // Check if it's already existing and update if so... if let Some(ref mut pos) = matches.args.get_mut(p.name) { done = true; @@ -1230,7 +1271,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ match needs_val_of { Some(ref a) => { if let Some(o) = self.opts.get(a) { - if o.multiple && self.required.is_empty() { return } + if o.multiple && self.required.is_empty() { () } else { self.report_error("One or more required arguments were not supplied".to_owned(), true, true, Some(matches.args.keys().map(|k| *k).collect::>())); @@ -1251,6 +1292,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } self.validate_blacklist(matches); + self.validate_num_args(matches); if !self.required.is_empty() { if self.validate_required(&matches) { @@ -1675,6 +1717,28 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } + fn validate_num_args(&self, matches: &mut ArgMatches<'ar, 'ar>) { + for (name, ma) in matches.args.iter() { + if let Some(ref vals) = ma.values { + if let Some(f) = self.opts.get(name) { + if let Some(num) = f.num_vals { + if num != vals.len() as u8 { + self.report_error(format!("The argument {} requires {} values, but {} w{} provided", f, num, vals.len(), if vals.len() == 1 {"as"}else{"ere"}), + true, true, Some(matches.args.keys().map(|k| *k).collect::>())); + } + } + } else if let Some(f) = self.positionals_idx.get(self.positionals_name.get(name).unwrap()) { + if let Some(num) = f.num_vals { + if num != vals.len() as u8 { + self.report_error(format!("The argument {} requires {} values, but {} w{} provided", f, num, vals.len(), if vals.len() == 1 {"as"}else{"ere"}), + true, true, Some(matches.args.keys().map(|k| *k).collect::>())); + } + } + } + } + } + } + fn validate_required(&self, matches: &ArgMatches<'ar, 'ar>) -> bool{ for name in self.required.iter() { validate_reqs!(self, flags, matches, name); diff --git a/src/args/arg.rs b/src/args/arg.rs index aca3c09b77e..d69b287dd48 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -77,7 +77,11 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> { pub requires: Option>, /// A name of the group the argument belongs to #[doc(hidden)] - pub group: Option<&'g str> + pub group: Option<&'g str>, + #[doc(hidden)] + pub val_names: Option>, + #[doc(hidden)] + pub num_vals: Option, } impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { @@ -116,6 +120,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { blacklist: None, requires: None, group: None, + num_vals: None, + val_names: None } } @@ -150,6 +156,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { possible_vals: None, blacklist: None, requires: None, + num_vals: None, + val_names: None, group: None, } } @@ -265,6 +273,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { possible_vals: None, blacklist: None, requires: None, + num_vals: None, + val_names: None, group: None, } } @@ -627,4 +637,21 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { self.group = Some(name); self } + + pub fn number_of_values(mut self, qty: u8) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> { + self.num_vals = Some(qty); + self + } + + pub fn value_names(mut self, names: I) + -> Arg<'n, 'l, 'h, 'g, 'p, 'r> + where T: AsRef + 'p, + I: IntoIterator { + if let Some(ref mut vec) = self.val_names { + names.into_iter().map(|s| vec.push(s.as_ref())).collect::>(); + } else { + self.val_names = Some(names.into_iter().map(|s| s.as_ref()).collect::>()); + } + self + } } diff --git a/src/args/argbuilder/option.rs b/src/args/argbuilder/option.rs index f1319289710..970fa7e65d3 100644 --- a/src/args/argbuilder/option.rs +++ b/src/args/argbuilder/option.rs @@ -25,10 +25,30 @@ pub struct OptBuilder<'n> { /// A list of names of other arguments that are *required* to be used when /// this flag is used pub requires: Option>, + pub num_vals: Option, + pub val_names: Option> } impl<'n> Display for OptBuilder<'n> { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{} <{}>{}", if self.long.is_some() { format!("--{}", self.long.unwrap())} else {format!("-{}", self.short.unwrap())}, self.name, if self.multiple{"..."}else{""}) + write!(f, "{}", + if let Some(ref vec) = self.val_names { + format!("[ {} {}]", + if self.long.is_some() { + format!("--{}", self.long.unwrap()) + } else { + format!("-{}", self.short.unwrap()) + }, + vec.iter().fold(String::new(),|acc, i| acc + &format!("<{}> ",i)[..]) ) + } else { + format!("{} <{}>{}", + if self.long.is_some() { + format!("--{}", self.long.unwrap()) + } else { + format!("-{}", self.short.unwrap()) + }, + self.name, + if self.multiple{"..."}else{""}) + }) } } \ No newline at end of file diff --git a/src/args/argbuilder/positional.rs b/src/args/argbuilder/positional.rs index 366d0a70722..60e7849ea45 100644 --- a/src/args/argbuilder/positional.rs +++ b/src/args/argbuilder/positional.rs @@ -22,11 +22,16 @@ pub struct PosBuilder<'n> { /// A list of possible values for this argument pub possible_vals: Option>, /// The index of the argument - pub index: u8 + pub index: u8, + pub num_vals: Option, } impl<'n> Display for PosBuilder<'n> { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}{}{}{}", if self.required { "<" } else {"["}, self.name,if self.required { ">" } else {"]"}, if self.multiple {"..."}else{""}) + write!(f, "{}{}{}{}", + if self.required { "<" } else {"["}, + self.name, + if self.required { ">" } else {"]"}, + if self.multiple {"..."}else{""}) } } \ No newline at end of file