From 807840094109fbf90b348039ae22669ef27889ba Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 29 Mar 2015 16:44:30 -0400 Subject: [PATCH] feat(positionals): add support for multiple values Add support for #44 (multiple values for positional arguments) when the positional argument is the last one (i.e. highest index) --- src/app.rs | 33 +++++++++++++++++++++++++-------- src/args/argmatches.rs | 34 ++++++++++++++++++++-------------- src/args/posarg.rs | 6 +++++- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/app.rs b/src/app.rs index 87865550..c2a991e4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -207,9 +207,9 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{ if a.short.is_some() || a.long.is_some() { panic!("Argument \"{}\" has conflicting requirements, both index() and short(), or long(), were supplied", a.name); } - if a.multiple { - panic!("Argument \"{}\" has conflicting requirements, both index() and multiple(true) were supplied",a.name); - } + // if a.multiple { + // panic!("Argument \"{}\" has conflicting requirements, both index() and multiple(true) were supplied",a.name); + // } if a.takes_value { panic!("Argument \"{}\" has conflicting requirements, both index() and takes_value(true) were supplied", a.name); } @@ -218,6 +218,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{ name: a.name, index: i, required: a.required, + multiple: a.multiple, blacklist: None, requires: None, possible_vals: None, @@ -652,10 +653,27 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{ } } } - matches.positionals.insert(p.name, PosArg{ - name: p.name.to_owned(), - value: arg.clone(), - }); + // Have we made the update yet? + let mut done = false; + if p.multiple { + // Check if it's already existing and update if so... + if let Some(ref mut pa) = matches.positionals.get_mut(p.name) { + done = true; + pa.occurrences += 1; + pa.values.push(arg.clone()); + } + } else { + // Only increment the positional counter if it doesn't allow multiples + pos_counter += 1; + } + // Was an update made, or is this the first occurrence? + if !done { + matches.positionals.insert(p.name, PosArg{ + name: p.name.to_owned(), + occurrences: 1, + values: vec![arg.clone()], + }); + } if let Some(ref bl) = p.blacklist { for name in bl { @@ -676,7 +694,6 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{ self.required.insert(n); } } - pos_counter += 1; } else { self.report_error(format!("Positional argument \"{}\" was found, but {} wasn't expecting any", arg, self.name), true, true); } diff --git a/src/args/argmatches.rs b/src/args/argmatches.rs index 06462e3a..c737968c 100644 --- a/src/args/argmatches.rs +++ b/src/args/argmatches.rs @@ -91,8 +91,8 @@ impl<'a> ArgMatches<'a> { /// an additional value at runtime). If the option wasn't present at runtime /// it returns `None`. /// - /// *NOTE:* If getting a value for an option argument that allows multiples, prefer `values_of()` - /// as `value_of()` will only return the _*first*_ value. + /// *NOTE:* If getting a value for an option or positional argument that allows multiples, + /// prefer `values_of()` as `value_of()` will only return the _*first*_ value. /// /// # Example /// @@ -105,21 +105,21 @@ impl<'a> ArgMatches<'a> { /// ``` pub fn value_of<'n>(&self, name: &'n str) -> Option<&str> { if let Some(ref opt) = self.opts.get(name) { - if !opt.values.is_empty() { - if let Some(ref s) = opt.values.iter().nth(0) { - return Some(&s[..]); - } - } + if let Some(ref s) = opt.values.iter().nth(0) { + return Some(&s[..]); + } } if let Some(ref pos) = self.positionals.get(name) { - return Some(&pos.value[..]); + if let Some(ref s) = pos.values.iter().nth(0) { + return Some(&s[..]); + } } None } - /// Gets the values of a specific option in a vector (i.e. an argument that takes - /// an additional value at runtime). If the option wasn't present at runtime - /// it returns `None` + /// Gets the values of a specific option or positional argument in a vector (i.e. an argument + /// that takes an additional value at runtime). If the option wasn't present at runtime it + /// returns `None` /// /// # Example /// @@ -141,6 +141,11 @@ impl<'a> ArgMatches<'a> { return Some(opt.values.iter().map(|s| &s[..]).collect::>()); } + if let Some(ref pos) = self.positionals.get(name) { + if pos.values.is_empty() { return None; } + + return Some(pos.values.iter().map(|s| &s[..]).collect::>()); + } None } @@ -167,13 +172,11 @@ impl<'a> ArgMatches<'a> { false } - /// Checks the number of occurrences of an option or flag at runtime. + /// Checks the number of occurrences of an option, flag, or positional argument at runtime. /// If an option or flag isn't present it will return `0`, if the option or flag doesn't /// allow multiple occurrences, it will return `1` no matter how many times it occurred /// (unless it wasn't prsent) at all. /// - /// *NOTE:* This _*DOES NOT*_ work for positional arguments (use `.value_of()` instead). - /// /// /// # Example /// @@ -193,6 +196,9 @@ impl<'a> ArgMatches<'a> { if let Some(ref o) = self.opts.get(name) { return o.occurrences; } + if let Some(ref p) = self.positionals.get(name) { + return p.occurrences; + } 0 } diff --git a/src/args/posarg.rs b/src/args/posarg.rs index cac2d95a..5eb1ce35 100644 --- a/src/args/posarg.rs +++ b/src/args/posarg.rs @@ -16,8 +16,10 @@ use std::collections::HashSet; pub struct PosArg { /// The unique name of the argument, required pub name: String, + /// How many occurences of this option have been found when parsing + pub occurrences: u8, /// The value provided to the argument by the user - pub value: String, + pub values: Vec } pub struct PosBuilder<'n> { @@ -30,6 +32,8 @@ pub struct PosBuilder<'n> { /// **NOTE:** required by default means, it is required *until* mutually /// exclusive arguments are evaluated. pub required: bool, + /// Allow multiple occurrences of an option argument such as "-c some -c other" + pub multiple: bool, /// A list of names of other arguments that are *required* to be used when /// this flag is used pub requires: Option>,