Skip to content

Commit 12aea96

Browse files
committed
fix(RequiredArgs): fixes bug where required-by-default arguments are not listed in usage
Closes #96
1 parent 82b0c5c commit 12aea96

File tree

2 files changed

+161
-133
lines changed

2 files changed

+161
-133
lines changed

src/app.rs

+159-131
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ use std::collections::HashMap;
55
use std::env;
66
use std::path::Path;
77
use std::vec::IntoIter;
8-
use std::borrow::ToOwned;
98
use std::process;
10-
use std::fmt::Write;
119

1210
use args::{ ArgMatches, Arg, SubCommand, MatchedArg};
1311
use args::{ FlagBuilder, OptBuilder, PosBuilder};
@@ -336,7 +334,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
336334
let mut rhs = HashSet::new();
337335
// without derefing n = &&str
338336
for n in r {
339-
rhs.insert(*n); }
337+
rhs.insert(*n);
338+
if pb.required {
339+
self.required.insert(*n);
340+
}
341+
}
340342
pb.requires = Some(rhs);
341343
}
342344
// 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>{
387389
if let Some(ref r) = a.requires {
388390
let mut rhs = HashSet::new();
389391
// without derefing n = &&str
390-
for n in r { rhs.insert(*n); }
392+
for n in r {
393+
rhs.insert(*n);
394+
if ob.required {
395+
self.required.insert(*n);
396+
}
397+
}
391398
ob.requires = Some(rhs);
392399
}
393400
// 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>{
657664
self
658665
}
659666

660-
fn group_string(&self, g: &ArgGroup, r: &str, vec: &Vec<&str>) -> Option<Vec<String>> {
667+
fn group_string(&self, g: &ArgGroup, r: &str, vec: &HashSet<&'ar str>) -> Option<Vec<String>> {
661668
let mut g_vec = vec![];
662669
for a in g.args.iter() {
663-
if vec.contains(&a) { return None }
670+
if vec.contains(a) { return None }
664671
if let Some(f) = self.flags.get(r) {
665672
g_vec.push(format!("{}", f));
666673
} else if let Some(o) = self.opts.get(r) {
@@ -677,141 +684,149 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
677684
Some(g_vec)
678685
}
679686

680-
// Creates a usage string if one was not provided by the user manually. This happens just
681-
// after all arguments were parsed, but before any subcommands have been parsed (so as to
682-
// give subcommands their own usage recursively)
683-
fn create_usage(&self, matches: Option<Vec<&str>>) -> String {
684-
let tab = " ";
685-
let mut usage = String::with_capacity(75);
686-
usage.push_str("USAGE:\n");
687-
usage.push_str(tab);
688-
if let Some(u) = self.usage_str {
689-
usage.push_str(u);
690-
} else if let Some(vec) = matches {
691-
let mut c_flags = vec![];
692-
let mut c_pos = vec![];
693-
let mut c_opt = vec![];
694-
let mut grps = vec![];
695-
vec.iter().map(|name| if let Some(f) = self.flags.get(name) {
696-
c_flags.push(f);
697-
} else if let Some(o) = self.opts.get(name) {
698-
c_opt.push(o);
699-
} else {
700-
c_pos.push(
701-
self.positionals_idx.get(self.positionals_name.get(name)
702-
.unwrap())
703-
.unwrap()
704-
);
705-
}).collect::<Vec<_>>();
706-
let mut tmp_f = vec![];
707-
for f in c_flags.iter_mut() {
708-
if let Some(ref rl) = f.requires {
709-
for r in rl {
710-
if !vec.contains(&&r) {
711-
if let Some(f) = self.flags.get(r) {
712-
tmp_f.push(f);
713-
} else if let Some(o) = self.opts.get(r) {
714-
c_opt.push(o);
715-
} else if let Some(g) = self.groups.get(r) {
716-
if let Some(vec) = self.group_string(g, r, &vec) {
717-
let g_string = vec.iter()
718-
.map(|s| format!("{}|", s))
719-
.fold(String::new(), |acc, i| {
720-
acc + &format!("{}",i)[..]
721-
});
722-
grps.push(format!("[{}]", &g_string[..g_string.len()-1]));
723-
}
724-
} else {
725-
c_pos.push(
726-
self.positionals_idx.get(self.positionals_name.get(r)
727-
.unwrap())
728-
.unwrap());
687+
fn get_required_from(&self, reqs: HashSet<&'ar str>) -> Vec<String> {
688+
let mut c_flags = vec![];
689+
let mut c_pos = vec![];
690+
let mut c_opt = vec![];
691+
let mut grps = vec![];
692+
reqs.iter().map(|name| if let Some(f) = self.flags.get(name) {
693+
c_flags.push(f);
694+
} else if let Some(o) = self.opts.get(name) {
695+
c_opt.push(o);
696+
} else {
697+
c_pos.push(
698+
self.positionals_idx.get(self.positionals_name.get(name)
699+
.unwrap())
700+
.unwrap()
701+
);
702+
}).collect::<Vec<_>>();
703+
let mut tmp_f = vec![];
704+
for f in c_flags.iter_mut() {
705+
if let Some(ref rl) = f.requires {
706+
for r in rl {
707+
if !reqs.contains(r) {
708+
if let Some(f) = self.flags.get(r) {
709+
tmp_f.push(f);
710+
} else if let Some(o) = self.opts.get(r) {
711+
c_opt.push(o);
712+
} else if let Some(g) = self.groups.get(r) {
713+
if let Some(vec) = self.group_string(g, r, &reqs) {
714+
let g_string = vec.iter()
715+
.map(|s| format!("{}|", s))
716+
.fold(String::new(), |acc, i| {
717+
acc + &format!("{}",i)[..]
718+
});
719+
grps.push(format!("[{}]", &g_string[..g_string.len()-1]));
729720
}
721+
} else {
722+
c_pos.push(
723+
self.positionals_idx.get(self.positionals_name.get(r)
724+
.unwrap())
725+
.unwrap());
730726
}
731727
}
732728
}
733729
}
734-
for f in tmp_f {
735-
c_flags.push(f);
736-
}
737-
let mut tmp_o = vec![];
738-
for o in c_opt.iter_mut() {
739-
if let Some(ref rl) = o.requires {
740-
for r in rl {
741-
if !vec.contains(&&r) {
742-
if let Some(f) = self.flags.get(r) {
743-
c_flags.push(f);
744-
} else if let Some(o) = self.opts.get(r) {
745-
tmp_o.push(o);
746-
} else if let Some(g) = self.groups.get(r) {
747-
if let Some(vec) = self.group_string(g, r, &vec) {
748-
let g_string = vec.iter()
749-
.map(|s| format!("{}|", s))
750-
.fold(String::new(), |acc, i| {
751-
acc + &format!("{}",i)[..]
752-
});
753-
grps.push(format!("[{}]", &g_string[..g_string.len()-1]));
754-
}
755-
} else {
756-
c_pos.push(self.positionals_idx.get(self.positionals_name.get(r)
757-
.unwrap())
758-
.unwrap());
730+
}
731+
for f in tmp_f {
732+
c_flags.push(f);
733+
}
734+
let mut tmp_o = vec![];
735+
for o in c_opt.iter_mut() {
736+
if let Some(ref rl) = o.requires {
737+
for r in rl {
738+
if !reqs.contains(r) {
739+
if let Some(f) = self.flags.get(r) {
740+
c_flags.push(f);
741+
} else if let Some(o) = self.opts.get(r) {
742+
tmp_o.push(o);
743+
} else if let Some(g) = self.groups.get(r) {
744+
if let Some(vec) = self.group_string(g, r, &reqs) {
745+
let g_string = vec.iter()
746+
.map(|s| format!("{}|", s))
747+
.fold(String::new(), |acc, i| {
748+
acc + &format!("{}",i)[..]
749+
});
750+
grps.push(format!("[{}]", &g_string[..g_string.len()-1]));
759751
}
752+
} else {
753+
c_pos.push(self.positionals_idx.get(self.positionals_name.get(r)
754+
.unwrap())
755+
.unwrap());
760756
}
761757
}
762758
}
763759
}
764-
for o in tmp_o {
765-
c_opt.push(o);
766-
}
767-
let mut tmp_p = vec![];
768-
for p in c_pos.iter_mut() {
769-
if let Some(ref rl) = p.requires {
770-
for r in rl {
771-
if !vec.contains(&&r) {
772-
if let Some(f) = self.flags.get(r) {
773-
c_flags.push(f);
774-
} else if let Some(o) = self.opts.get(r) {
775-
c_opt.push(o);
776-
} else if let Some(g) = self.groups.get(r) {
777-
if let Some(vec) = self.group_string(g, r, &vec) {
778-
let g_string = vec.iter()
779-
.map(|s| format!("{}|", s))
780-
.fold(String::new(), |acc, i| {
781-
acc + &format!("{}",i)[..]
782-
});
783-
grps.push(format!("[{}]", &g_string[..g_string.len()-1]));
784-
}
785-
} else {
786-
tmp_p.push(self.positionals_idx.get(self.positionals_name.get(r)
787-
.unwrap())
788-
.unwrap());
760+
}
761+
for o in tmp_o {
762+
c_opt.push(o);
763+
}
764+
let mut tmp_p = vec![];
765+
for p in c_pos.iter_mut() {
766+
if let Some(ref rl) = p.requires {
767+
for r in rl {
768+
if !reqs.contains(r) {
769+
if let Some(f) = self.flags.get(r) {
770+
c_flags.push(f);
771+
} else if let Some(o) = self.opts.get(r) {
772+
c_opt.push(o);
773+
} else if let Some(g) = self.groups.get(r) {
774+
if let Some(vec) = self.group_string(g, r, &reqs) {
775+
let g_string = vec.iter()
776+
.map(|s| format!("{}|", s))
777+
.fold(String::new(), |acc, i| {
778+
acc + &format!("{}",i)[..]
779+
});
780+
grps.push(format!("[{}]", &g_string[..g_string.len()-1]));
789781
}
782+
} else {
783+
tmp_p.push(self.positionals_idx.get(self.positionals_name.get(r)
784+
.unwrap())
785+
.unwrap());
790786
}
791787
}
792788
}
793789
}
794-
for p in tmp_p {
795-
c_pos.push(p);
796-
}
797-
let f_string = c_flags.iter()
798-
.map(|f| format!("{}", f))
799-
.fold(String::new(),|acc, i| acc + &format!(" {}", i));
800-
let o_string = c_opt.iter()
801-
.map(|f| format!("{}", f))
802-
.fold(String::new(),|acc, i| acc + &format!(" {}", i));
803-
let p_string = c_pos.iter()
804-
.map(|f| format!("{}", f))
805-
.fold(String::new(),|acc, i| acc + &format!(" {}", i));
806-
let g_string = grps.iter()
807-
.map(|f| format!(" {}", f))
808-
.fold(String::new(), |acc, i| acc + &format!("{}", i));
809-
usage.push_str(&format!("{} {} {} {} {}",
790+
}
791+
for p in tmp_p {
792+
c_pos.push(p);
793+
}
794+
let mut ret_val = vec![];
795+
796+
c_pos.iter()
797+
.map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
798+
c_flags.iter()
799+
.map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
800+
c_opt.iter()
801+
.map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
802+
grps.iter()
803+
.map(|f| ret_val.push(format!("{}", f))).collect::<Vec<_>>();
804+
805+
ret_val
806+
807+
}
808+
809+
// Creates a usage string if one was not provided by the user manually. This happens just
810+
// after all arguments were parsed, but before any subcommands have been parsed (so as to
811+
// give subcommands their own usage recursively)
812+
fn create_usage(&self, matches: Option<Vec<&'ar str>>) -> String {
813+
let tab = " ";
814+
let mut usage = String::with_capacity(75);
815+
usage.push_str("USAGE:\n");
816+
usage.push_str(tab);
817+
if let Some(u) = self.usage_str {
818+
usage.push_str(u);
819+
} else if let Some(tmp_vec) = matches {
820+
let mut hs = HashSet::new();
821+
self.required.iter().map(|n| hs.insert(*n)).collect::<Vec<_>>();
822+
tmp_vec.iter().map(|n| hs.insert(*n)).collect::<Vec<_>>();
823+
let reqs = self.get_required_from(hs);
824+
825+
let r_string = reqs.iter().fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
826+
827+
usage.push_str(&format!("{}{}",
810828
self.bin_name.clone().unwrap_or(self.name.clone()),
811-
p_string,
812-
f_string,
813-
o_string,
814-
g_string)[..]);
829+
r_string)[..]);
815830
} else {
816831
let flags = !self.flags.is_empty();
817832
let pos = !self.positionals_idx.is_empty();
@@ -1448,10 +1463,15 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
14481463
if let Some(o) = self.opts.get(a) {
14491464
if o.multiple && self.required.is_empty() { () }
14501465
else {
1451-
self.report_error("One or more required arguments were not \
1452-
supplied".to_owned(),
1453-
true,
1454-
true,
1466+
self.report_error(format!("The following required arguments were not \
1467+
supplied:\n{}",
1468+
self.get_required_from(self.required.iter()
1469+
.map(|s| *s)
1470+
.collect::<HashSet<_>>())
1471+
.iter()
1472+
.fold(String::new(), |acc, s| acc + &format!("\t{}\n",s)[..])),
1473+
true,
1474+
true,
14551475
Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>()));
14561476
}
14571477
} else {
@@ -1478,8 +1498,16 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
14781498

14791499
if !self.required.is_empty() {
14801500
if self.validate_required(&matches) {
1481-
self.report_error("One or more required arguments were not supplied".to_owned(),
1482-
true, true, Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>()));
1501+
self.report_error(format!("The following required arguments were not \
1502+
supplied:\n{}",
1503+
self.get_required_from(self.required.iter()
1504+
.map(|s| *s)
1505+
.collect::<HashSet<_>>())
1506+
.iter()
1507+
.fold(String::new(), |acc, s| acc + &format!("\t{}\n",s)[..])),
1508+
true,
1509+
true,
1510+
Some(matches.args.keys().map(|k| *k).collect::<Vec<_>>()));
14831511
}
14841512
}
14851513

src/args/argbuilder/option.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ impl<'n> Display for OptBuilder<'n> {
3535
fn fmt(&self, f: &mut Formatter) -> Result {
3636
write!(f, "{}",
3737
if let Some(ref vec) = self.val_names {
38-
format!("[ {} {}]",
38+
format!("{}{}",
3939
if self.long.is_some() {
4040
format!("--{}", self.long.unwrap())
4141
} else {
4242
format!("-{}", self.short.unwrap())
4343
},
44-
vec.iter().fold(String::new(),|acc, i| acc + &format!("<{}> ",i)[..]) )
44+
vec.iter().fold(String::new(),|acc, i| acc + &format!(" <{}>",i)[..]) )
4545
} else {
4646
format!("{} <{}>{}",
4747
if self.long.is_some() {

0 commit comments

Comments
 (0)