fix(RequiredArgs): fixes bug where required-by-default arguments are not listed in usage

Closes #96
This commit is contained in:
Kevin K 2015-05-02 12:50:31 -04:00
parent 82b0c5c3d7
commit 12aea9612d
2 changed files with 167 additions and 139 deletions

View file

@ -5,9 +5,7 @@ use std::collections::HashMap;
use std::env; use std::env;
use std::path::Path; use std::path::Path;
use std::vec::IntoIter; use std::vec::IntoIter;
use std::borrow::ToOwned;
use std::process; use std::process;
use std::fmt::Write;
use args::{ ArgMatches, Arg, SubCommand, MatchedArg}; use args::{ ArgMatches, Arg, SubCommand, MatchedArg};
use args::{ FlagBuilder, OptBuilder, PosBuilder}; use args::{ FlagBuilder, OptBuilder, PosBuilder};
@ -336,7 +334,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
let mut rhs = HashSet::new(); let mut rhs = HashSet::new();
// without derefing n = &&str // without derefing n = &&str
for n in r { for n in r {
rhs.insert(*n); } rhs.insert(*n);
if pb.required {
self.required.insert(*n);
}
}
pb.requires = Some(rhs); pb.requires = Some(rhs);
} }
// Check if there is anything in the possible values and add those as well // Check if there is anything in the possible values and add those as well
@ -387,7 +389,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
if let Some(ref r) = a.requires { if let Some(ref r) = a.requires {
let mut rhs = HashSet::new(); let mut rhs = HashSet::new();
// without derefing n = &&str // without derefing n = &&str
for n in r { rhs.insert(*n); } for n in r {
rhs.insert(*n);
if ob.required {
self.required.insert(*n);
}
}
ob.requires = Some(rhs); ob.requires = Some(rhs);
} }
// Check if there is anything in the possible values and add those as well // Check if there is anything in the possible values and add those as well
@ -657,10 +664,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
self self
} }
fn group_string(&self, g: &ArgGroup, r: &str, vec: &Vec<&str>) -> Option<Vec<String>> { fn group_string(&self, g: &ArgGroup, r: &str, vec: &HashSet<&'ar str>) -> Option<Vec<String>> {
let mut g_vec = vec![]; let mut g_vec = vec![];
for a in g.args.iter() { for a in g.args.iter() {
if vec.contains(&a) { return None } if vec.contains(a) { return None }
if let Some(f) = self.flags.get(r) { if let Some(f) = self.flags.get(r) {
g_vec.push(format!("{}", f)); g_vec.push(format!("{}", f));
} else if let Some(o) = self.opts.get(r) { } else if let Some(o) = self.opts.get(r) {
@ -677,22 +684,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
Some(g_vec) Some(g_vec)
} }
// Creates a usage string if one was not provided by the user manually. This happens just fn get_required_from(&self, reqs: HashSet<&'ar str>) -> Vec<String> {
// after all arguments were parsed, but before any subcommands have been parsed (so as to
// give subcommands their own usage recursively)
fn create_usage(&self, matches: Option<Vec<&str>>) -> String {
let tab = " ";
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n");
usage.push_str(tab);
if let Some(u) = self.usage_str {
usage.push_str(u);
} else if let Some(vec) = matches {
let mut c_flags = vec![]; let mut c_flags = vec![];
let mut c_pos = vec![]; let mut c_pos = vec![];
let mut c_opt = vec![]; let mut c_opt = vec![];
let mut grps = vec![]; let mut grps = vec![];
vec.iter().map(|name| if let Some(f) = self.flags.get(name) { reqs.iter().map(|name| if let Some(f) = self.flags.get(name) {
c_flags.push(f); c_flags.push(f);
} else if let Some(o) = self.opts.get(name) { } else if let Some(o) = self.opts.get(name) {
c_opt.push(o); c_opt.push(o);
@ -707,13 +704,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
for f in c_flags.iter_mut() { for f in c_flags.iter_mut() {
if let Some(ref rl) = f.requires { if let Some(ref rl) = f.requires {
for r in rl { for r in rl {
if !vec.contains(&&r) { if !reqs.contains(r) {
if let Some(f) = self.flags.get(r) { if let Some(f) = self.flags.get(r) {
tmp_f.push(f); tmp_f.push(f);
} else if let Some(o) = self.opts.get(r) { } else if let Some(o) = self.opts.get(r) {
c_opt.push(o); c_opt.push(o);
} else if let Some(g) = self.groups.get(r) { } else if let Some(g) = self.groups.get(r) {
if let Some(vec) = self.group_string(g, r, &vec) { if let Some(vec) = self.group_string(g, r, &reqs) {
let g_string = vec.iter() let g_string = vec.iter()
.map(|s| format!("{}|", s)) .map(|s| format!("{}|", s))
.fold(String::new(), |acc, i| { .fold(String::new(), |acc, i| {
@ -738,13 +735,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
for o in c_opt.iter_mut() { for o in c_opt.iter_mut() {
if let Some(ref rl) = o.requires { if let Some(ref rl) = o.requires {
for r in rl { for r in rl {
if !vec.contains(&&r) { if !reqs.contains(r) {
if let Some(f) = self.flags.get(r) { if let Some(f) = self.flags.get(r) {
c_flags.push(f); c_flags.push(f);
} else if let Some(o) = self.opts.get(r) { } else if let Some(o) = self.opts.get(r) {
tmp_o.push(o); tmp_o.push(o);
} else if let Some(g) = self.groups.get(r) { } else if let Some(g) = self.groups.get(r) {
if let Some(vec) = self.group_string(g, r, &vec) { if let Some(vec) = self.group_string(g, r, &reqs) {
let g_string = vec.iter() let g_string = vec.iter()
.map(|s| format!("{}|", s)) .map(|s| format!("{}|", s))
.fold(String::new(), |acc, i| { .fold(String::new(), |acc, i| {
@ -768,13 +765,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
for p in c_pos.iter_mut() { for p in c_pos.iter_mut() {
if let Some(ref rl) = p.requires { if let Some(ref rl) = p.requires {
for r in rl { for r in rl {
if !vec.contains(&&r) { if !reqs.contains(r) {
if let Some(f) = self.flags.get(r) { if let Some(f) = self.flags.get(r) {
c_flags.push(f); c_flags.push(f);
} else if let Some(o) = self.opts.get(r) { } else if let Some(o) = self.opts.get(r) {
c_opt.push(o); c_opt.push(o);
} else if let Some(g) = self.groups.get(r) { } else if let Some(g) = self.groups.get(r) {
if let Some(vec) = self.group_string(g, r, &vec) { if let Some(vec) = self.group_string(g, r, &reqs) {
let g_string = vec.iter() let g_string = vec.iter()
.map(|s| format!("{}|", s)) .map(|s| format!("{}|", s))
.fold(String::new(), |acc, i| { .fold(String::new(), |acc, i| {
@ -794,24 +791,42 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
for p in tmp_p { for p in tmp_p {
c_pos.push(p); c_pos.push(p);
} }
let f_string = c_flags.iter() let mut ret_val = vec![];
.map(|f| format!("{}", f))
.fold(String::new(),|acc, i| acc + &format!(" {}", i)); c_pos.iter()
let o_string = c_opt.iter() .map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
.map(|f| format!("{}", f)) c_flags.iter()
.fold(String::new(),|acc, i| acc + &format!(" {}", i)); .map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
let p_string = c_pos.iter() c_opt.iter()
.map(|f| format!("{}", f)) .map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
.fold(String::new(),|acc, i| acc + &format!(" {}", i)); grps.iter()
let g_string = grps.iter() .map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
.map(|f| format!(" {}", f))
.fold(String::new(), |acc, i| acc + &format!("{}", i)); ret_val
usage.push_str(&format!("{} {} {} {} {}",
}
// Creates a usage string if one was not provided by the user manually. This happens just
// after all arguments were parsed, but before any subcommands have been parsed (so as to
// give subcommands their own usage recursively)
fn create_usage(&self, matches: Option<Vec<&'ar str>>) -> String {
let tab = " ";
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n");
usage.push_str(tab);
if let Some(u) = self.usage_str {
usage.push_str(u);
} else if let Some(tmp_vec) = matches {
let mut hs = HashSet::new();
self.required.iter().map(|n| hs.insert(*n)).collect::<Vec<_>>();
tmp_vec.iter().map(|n| hs.insert(*n)).collect::<Vec<_>>();
let reqs = self.get_required_from(hs);
let r_string = reqs.iter().fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
usage.push_str(&format!("{}{}",
self.bin_name.clone().unwrap_or(self.name.clone()), self.bin_name.clone().unwrap_or(self.name.clone()),
p_string, r_string)[..]);
f_string,
o_string,
g_string)[..]);
} else { } else {
let flags = !self.flags.is_empty(); let flags = !self.flags.is_empty();
let pos = !self.positionals_idx.is_empty(); let pos = !self.positionals_idx.is_empty();
@ -1448,8 +1463,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
if let Some(o) = self.opts.get(a) { if let Some(o) = self.opts.get(a) {
if o.multiple && self.required.is_empty() { () } if o.multiple && self.required.is_empty() { () }
else { else {
self.report_error("One or more required arguments were not \ self.report_error(format!("The following required arguments were not \
supplied".to_owned(), supplied:\n{}",
self.get_required_from(self.required.iter()
.map(|s| *s)
.collect::<HashSet<_>>())
.iter()
.fold(String::new(), |acc, s| acc + &format!("\t{}\n",s)[..])),
true, true,
true, true,
Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>())); Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>()));
@ -1478,8 +1498,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
if !self.required.is_empty() { if !self.required.is_empty() {
if self.validate_required(&matches) { if self.validate_required(&matches) {
self.report_error("One or more required arguments were not supplied".to_owned(), self.report_error(format!("The following required arguments were not \
true, true, Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>())); supplied:\n{}",
self.get_required_from(self.required.iter()
.map(|s| *s)
.collect::<HashSet<_>>())
.iter()
.fold(String::new(), |acc, s| acc + &format!("\t{}\n",s)[..])),
true,
true,
Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>()));
} }
} }

View file

@ -35,13 +35,13 @@ impl<'n> Display for OptBuilder<'n> {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", write!(f, "{}",
if let Some(ref vec) = self.val_names { if let Some(ref vec) = self.val_names {
format!("[ {} {}]", format!("{}{}",
if self.long.is_some() { if self.long.is_some() {
format!("--{}", self.long.unwrap()) format!("--{}", self.long.unwrap())
} else { } else {
format!("-{}", self.short.unwrap()) format!("-{}", self.short.unwrap())
}, },
vec.iter().fold(String::new(),|acc, i| acc + &format!("<{}> ",i)[..]) ) vec.iter().fold(String::new(),|acc, i| acc + &format!(" <{}>",i)[..]) )
} else { } else {
format!("{} <{}>{}", format!("{} <{}>{}",
if self.long.is_some() { if self.long.is_some() {