diff --git a/src/args/arg.rs b/src/args/arg.rs index 3063850c824..2d18b7789fa 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -1,4 +1,5 @@ use std::iter::IntoIterator; +use std::collections::HashSet; use usageparser::{UsageParser, UsageToken}; @@ -79,7 +80,7 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> { #[doc(hidden)] pub group: Option<&'g str>, #[doc(hidden)] - pub val_names: Option>, + pub val_names: Option>, #[doc(hidden)] pub num_vals: Option, #[doc(hidden)] @@ -229,34 +230,57 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { let mut required = false; let mut takes_value = false; let mut multiple = false; + let mut num_names = 1; + let mut name_first = false; + let mut consec_names = false; + let mut val_names = HashSet::new(); let parser = UsageParser::with_usage(u); for_match!{ parser, UsageToken::Name(n, req) => { - if name.is_none() { + if consec_names { + num_names += 1; + } + let mut use_req = false; + let mut use_name = false; + if name.is_none() && long.is_none() && short.is_none() { + name_first = true; + use_name = true; + use_req = true; + } else if let Some(l) = long { + if l == name.unwrap_or("") { + if !name_first { + use_name = true; + use_req = true; + } + } + } else { + // starting with short + if !name_first { + use_name = true; + use_req = true; + } + } + if use_name && !consec_names { name = Some(n); + } + if use_req && !consec_names { if let Some(r) = req { required = r; } - } else if let Some(l) = long { - if l == name.unwrap() { - if let Some(r) = req { - required = r; - } - name = Some(n); - } else if n != l { - name = Some(n); - } - } if short.is_some() || long.is_some() { + val_names.insert(n); takes_value = true; } + consec_names = true; }, UsageToken::Short(s) => { + consec_names = false; short = Some(s); }, UsageToken::Long(l) => { + consec_names = false; long = Some(l); if name.is_none() { name = Some(l); @@ -270,6 +294,15 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { } } + if let Some(l) = long { + val_names.remove(l); + if val_names.len() > 1 { + if name.unwrap() != l && !name_first { + name = Some(l); + } + } + } + Arg { name: name.unwrap(), short: short, @@ -282,8 +315,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, + num_vals: if num_names > 1 { Some(num_names) } else { None }, + val_names: if val_names.len() > 1 {Some(val_names.iter().map(|s| *s).collect::>())}else{None}, max_vals: None, min_vals: None, group: None, @@ -765,8 +798,8 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> { /// # ).get_matches(); pub fn value_names(mut self, names: I) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> - where T: AsRef + 'p, - I: IntoIterator { + where T: AsRef + 'n, + I: IntoIterator { if let Some(ref mut vec) = self.val_names { names.into_iter().map(|s| vec.push(s.as_ref())).collect::>(); } else { diff --git a/src/lib.rs b/src/lib.rs index 3d7bccc4daf..59b1461fcc8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -361,6 +361,7 @@ mod usageparser; #[cfg(test)] mod tests { use super::{App, Arg, SubCommand}; + use std::collections::HashSet; #[test] fn create_app() { @@ -394,6 +395,8 @@ mod tests { assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.multiple); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); let b = Arg::from_usage("[flag] --flag 'some help info'"); assert_eq!(b.name, "flag"); @@ -401,6 +404,8 @@ mod tests { assert!(b.short.is_none()); assert_eq!(b.help.unwrap(), "some help info"); assert!(!b.multiple); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); let b = Arg::from_usage("--flag 'some help info'"); assert_eq!(b.name, "flag"); @@ -408,6 +413,8 @@ mod tests { assert!(b.short.is_none()); assert_eq!(b.help.unwrap(), "some help info"); assert!(!b.multiple); + assert!(b.val_names.is_none()); + assert!(b.num_vals.is_none()); let c = Arg::from_usage("[flag] -f --flag 'some help info'"); assert_eq!(c.name, "flag"); @@ -415,6 +422,8 @@ mod tests { assert_eq!(c.long.unwrap(), "flag"); assert_eq!(c.help.unwrap(), "some help info"); assert!(!c.multiple); + assert!(c.val_names.is_none()); + assert!(c.num_vals.is_none()); let d = Arg::from_usage("[flag] -f... 'some help info'"); assert_eq!(d.name, "flag"); @@ -422,6 +431,8 @@ mod tests { assert!(d.long.is_none()); assert_eq!(d.help.unwrap(), "some help info"); assert!(d.multiple); + assert!(d.val_names.is_none()); + assert!(d.num_vals.is_none()); let e = Arg::from_usage("[flag] -f --flag... 'some help info'"); assert_eq!(e.name, "flag"); @@ -429,6 +440,8 @@ mod tests { assert_eq!(e.short.unwrap(), 'f'); assert_eq!(e.help.unwrap(), "some help info"); assert!(e.multiple); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); let e = Arg::from_usage("-f --flag... 'some help info'"); assert_eq!(e.name, "flag"); @@ -436,24 +449,34 @@ mod tests { assert_eq!(e.short.unwrap(), 'f'); assert_eq!(e.help.unwrap(), "some help info"); assert!(e.multiple); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); let e = Arg::from_usage("--flags"); assert_eq!(e.name, "flags"); assert_eq!(e.long.unwrap(), "flags"); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); let e = Arg::from_usage("--flags..."); assert_eq!(e.name, "flags"); assert_eq!(e.long.unwrap(), "flags"); assert!(e.multiple); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); let e = Arg::from_usage("[flags] -f"); assert_eq!(e.name, "flags"); assert_eq!(e.short.unwrap(), 'f'); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); let e = Arg::from_usage("[flags] -f..."); assert_eq!(e.name, "flags"); assert_eq!(e.short.unwrap(), 'f'); assert!(e.multiple); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); } #[test] @@ -472,34 +495,46 @@ mod tests { assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.multiple); assert!(!a.required); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); let b = Arg::from_usage(" 'some help info'"); assert_eq!(b.name, "pos"); assert_eq!(b.help.unwrap(), "some help info"); assert!(!b.multiple); assert!(b.required); + assert!(b.val_names.is_none()); + assert!(b.num_vals.is_none()); let c = Arg::from_usage("[pos]... 'some help info'"); assert_eq!(c.name, "pos"); assert_eq!(c.help.unwrap(), "some help info"); assert!(c.multiple); assert!(!c.required); + assert!(c.val_names.is_none()); + assert!(c.num_vals.is_none()); let d = Arg::from_usage("... 'some help info'"); assert_eq!(d.name, "pos"); assert_eq!(d.help.unwrap(), "some help info"); assert!(d.multiple); assert!(d.required); + assert!(d.val_names.is_none()); + assert!(d.num_vals.is_none()); let b = Arg::from_usage(""); assert_eq!(b.name, "pos"); assert!(!b.multiple); assert!(b.required); + assert!(b.val_names.is_none()); + assert!(b.num_vals.is_none()); let c = Arg::from_usage("[pos]..."); assert_eq!(c.name, "pos"); assert!(c.multiple); assert!(!c.required); + assert!(c.val_names.is_none()); + assert!(c.num_vals.is_none()); } #[test] @@ -509,24 +544,32 @@ mod tests { assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.multiple); assert!(!a.required); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); let b = Arg::from_usage("\t'some help info'"); assert_eq!(b.name, "pos"); assert_eq!(b.help.unwrap(), "some help info"); assert!(!b.multiple); assert!(b.required); + assert!(b.val_names.is_none()); + assert!(b.num_vals.is_none()); let c = Arg::from_usage("[pos]...\t'some help info'"); assert_eq!(c.name, "pos"); assert_eq!(c.help.unwrap(), "some help info"); assert!(c.multiple); assert!(!c.required); + assert!(c.val_names.is_none()); + assert!(c.num_vals.is_none()); let d = Arg::from_usage("...\t'some help info'"); assert_eq!(d.name, "pos"); assert_eq!(d.help.unwrap(), "some help info"); assert!(d.multiple); assert!(d.required); + assert!(d.val_names.is_none()); + assert!(d.num_vals.is_none()); } #[test] @@ -551,6 +594,8 @@ mod tests { assert!(!a.multiple); assert!(a.takes_value); assert!(!a.required); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); let b = Arg::from_usage("-o [opt] 'some help info'"); assert_eq!(b.name, "opt"); @@ -560,6 +605,8 @@ mod tests { assert!(!b.multiple); assert!(b.takes_value); assert!(!b.required); + assert!(b.val_names.is_none()); + assert!(b.num_vals.is_none()); let c = Arg::from_usage("