Skip to content

Commit

Permalink
feat(MultipleValues): adds support limited number and named values
Browse files Browse the repository at this point in the history
  • Loading branch information
kbknapp committed Apr 30, 2015
1 parent 42d9b14 commit ae09f05
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 8 deletions.
72 changes: 68 additions & 4 deletions src/app.rs
Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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![];
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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::<Vec<_>>()));
}
}
}
}
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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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::<Vec<_>>()));
}
}
}
}
// Check if it's already existing and update if so...
if let Some(ref mut pos) = matches.args.get_mut(p.name) {
done = true;
Expand Down Expand Up @@ -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::<Vec<_>>()));
Expand All @@ -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) {
Expand Down Expand Up @@ -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::<Vec<_>>()));
}
}
} 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::<Vec<_>>()));
}
}
}
}
}
}

fn validate_required(&self, matches: &ArgMatches<'ar, 'ar>) -> bool{
for name in self.required.iter() {
validate_reqs!(self, flags, matches, name);
Expand Down
29 changes: 28 additions & 1 deletion src/args/arg.rs
Expand Up @@ -77,7 +77,11 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> {
pub requires: Option<Vec<&'r str>>,
/// 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<Vec<&'p str>>,
#[doc(hidden)]
pub num_vals: Option<u8>,
}

impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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,
}
}
Expand Down Expand Up @@ -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,
}
}
Expand Down Expand Up @@ -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<T, I>(mut self, names: I)
-> Arg<'n, 'l, 'h, 'g, 'p, 'r>
where T: AsRef<str> + 'p,
I: IntoIterator<Item=&'p T> {
if let Some(ref mut vec) = self.val_names {
names.into_iter().map(|s| vec.push(s.as_ref())).collect::<Vec<_>>();
} else {
self.val_names = Some(names.into_iter().map(|s| s.as_ref()).collect::<Vec<_>>());
}
self
}
}
22 changes: 21 additions & 1 deletion src/args/argbuilder/option.rs
Expand Up @@ -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<HashSet<&'n str>>,
pub num_vals: Option<u8>,
pub val_names: Option<Vec<&'n str>>
}

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{""})
})
}
}
9 changes: 7 additions & 2 deletions src/args/argbuilder/positional.rs
Expand Up @@ -22,11 +22,16 @@ pub struct PosBuilder<'n> {
/// A list of possible values for this argument
pub possible_vals: Option<BTreeSet<&'n str>>,
/// The index of the argument
pub index: u8
pub index: u8,
pub num_vals: Option<u8>,
}

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{""})
}
}

0 comments on commit ae09f05

Please sign in to comment.