From 3d58197674ed7886ca315efb76e411608a327501 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Mon, 4 May 2015 23:36:45 -0400 Subject: [PATCH] feat(from_usage): adds ability to add value names or num of vals in usage string Allows new usage strings with value names or number of values. If the names are consecutive, they are counted to represent the number of values (if they all have the same name), or if their names are different they are used as value names. Closes #98 --- src/args/arg.rs | 65 ++++++++++++---- src/lib.rs | 200 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 16 deletions(-) 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("