Skip to content

Commit 343d47d

Browse files
committed
fix(positional args): all previous positional args become required when
a latter one is required Closes #50
1 parent 1a1ce2a commit 343d47d

File tree

1 file changed

+65
-17
lines changed

1 file changed

+65
-17
lines changed

src/app.rs

+65-17
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub struct App<'a, 'v, 'ab, 'u, 'ar> {
5555
needs_short_version: bool,
5656
needs_subcmd_help: bool,
5757
required: HashSet<&'ar str>,
58+
matched_reqs: HashSet<&'ar str>,
5859
arg_list: HashSet<&'ar str>,
5960
short_list: HashSet<char>,
6061
long_list: HashSet<&'ar str>,
@@ -91,6 +92,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
9192
needs_subcmd_help: true,
9293
needs_short_version: true,
9394
required: HashSet::new(),
95+
matched_reqs: HashSet::new(),
9496
arg_list: HashSet::new(),
9597
short_list: HashSet::new(),
9698
long_list: HashSet::new(),
@@ -216,6 +218,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
216218
if a.takes_value {
217219
panic!("Argument \"{}\" has conflicting requirements, both index() and takes_value(true) were supplied", a.name);
218220
}
221+
219222
// Create the Positional Arguemnt Builder with each HashSet = None to only allocate those that require it
220223
let mut pb = PosBuilder {
221224
name: a.name,
@@ -238,7 +241,8 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
238241
if let Some(ref r) = a.requires {
239242
let mut rhs = HashSet::new();
240243
// without derefing n = &&str
241-
for n in r { rhs.insert(*n); }
244+
for n in r {
245+
rhs.insert(*n); }
242246
pb.requires = Some(rhs);
243247
}
244248
// Check if there is anything in the possible values and add those as well
@@ -409,16 +413,31 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
409413
}
410414

411415
fn print_usage(&self, more_info: bool) {
416+
let tab = " ";
412417
println!("USAGE:");
413418
if let Some(u) = self.usage_str {
414-
println!(" {}",u);
419+
println!("{}{}",tab,u);
415420
} else {
416421
let flags = !self.flags.is_empty();
417422
let pos = !self.positionals_idx.is_empty();
418423
let opts = !self.opts.is_empty();
419424
let subcmds = !self.subcommands.is_empty();
420425
let mut num_req_pos = 0;
421-
let req_pos = self.positionals_idx.values().filter_map(|ref x| if x.required || self.required.contains(x.name) {
426+
let mut matched_pos_reqs = HashSet::new();
427+
// If it's required we also need to ensure all previous positionals are required too
428+
let mut found = false;
429+
for p in self.positionals_idx.values().rev() {
430+
if found {
431+
matched_pos_reqs.insert(p.name);
432+
continue;
433+
}
434+
if self.matched_reqs.contains(p.name) {
435+
matched_pos_reqs.insert(p.name);
436+
found = true;
437+
}
438+
}
439+
440+
let req_pos = self.positionals_idx.values().filter_map(|ref x| if x.required || matched_pos_reqs.contains(x.name) {
422441
num_req_pos += 1;
423442
if x.multiple {
424443
Some(format!("<{}>...", x.name))
@@ -430,7 +449,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
430449
})
431450
.fold(String::new(), |acc, ref name| acc + &format!("{} ", name)[..]);
432451
let mut num_req_opts = 0;
433-
let req_opts = self.opts.values().filter_map(|x| if x.required || self.required.contains(x.name) {
452+
let req_opts = self.opts.values().filter_map(|x| if x.required || self.matched_reqs.contains(x.name) {
434453
num_req_opts += 1;
435454
Some(x)
436455
}else {
@@ -442,7 +461,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
442461
format!("{} ",o.short.unwrap())
443462
},o.name));
444463

445-
print!(" {} {} {} {} {}", self.bin_name.clone().unwrap_or(self.name.clone()),
464+
print!("{}{} {} {} {} {}",tab, self.bin_name.clone().unwrap_or(self.name.clone()),
446465
if flags {"[FLAGS]"} else {""},
447466
if opts {
448467
if num_req_opts != self.opts.len() && !req_opts.is_empty() {
@@ -653,7 +672,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
653672

654673
pub fn get_matches(mut self) -> ArgMatches<'ar> {
655674
self.verify_positionals();
656-
for sc in self.subcommands.values() {
675+
for (_,sc) in self.subcommands.iter_mut() {
657676
sc.verify_positionals();
658677
}
659678

@@ -674,7 +693,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
674693
matches
675694
}
676695

677-
fn verify_positionals(&self) {
696+
fn verify_positionals(&mut self) {
678697
// Because you must wait until all arguments have been supplied, this is the first chance
679698
// to make assertions on positional argument indexes
680699
//
@@ -695,6 +714,19 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
695714
panic!("Found positional argument \"{}\" which accepts multiple values but it's not the last positional argument (i.e. others have a higher index)",
696715
p.name);
697716
}
717+
718+
// If it's required we also need to ensure all previous positionals are required too
719+
let mut found = false;
720+
for (_, p) in self.positionals_idx.iter_mut().rev() {
721+
if found {
722+
p.required = true;
723+
self.required.insert(p.name);
724+
continue;
725+
}
726+
if p.required {
727+
found = true;
728+
}
729+
}
698730
}
699731

700732
fn get_matches_from(&mut self, matches: &mut ArgMatches<'ar>, it: &mut IntoIter<String>) {
@@ -738,6 +770,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
738770
needs_val_of = None;
739771
continue;
740772
}
773+
741774
if arg_slice.starts_with("--") && !pos_only {
742775
if arg_slice.len() == 2 {
743776
pos_only = true;
@@ -762,6 +795,9 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
762795
format!("Found positional argument {}, but {} doesn't accept any", arg, self.name),
763796
true, true);
764797
}
798+
// If we find that an argument requires a positiona, we need to update all the
799+
// previous positionals too. This will denote where to start
800+
// let mut req_pos_from_name = None;
765801
if let Some(p) = self.positionals_idx.get(&pos_counter) {
766802
if self.blacklist.contains(p.name) {
767803
self.report_error(format!("The argument \"{}\" is mutually exclusive with one or more other arguments", arg),
@@ -806,13 +842,16 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
806842
self.required.remove(name);
807843
}
808844
}
809-
if self.required.contains(p.name) {
845+
846+
// No need to check for existance, returns None if not found
847+
// if self.required.contains(p.name) {
810848
self.required.remove(p.name);
811-
}
849+
// }
812850
if let Some(ref reqs) = p.requires {
813851
// Add all required args which aren't already found in matches to the
814852
// final required list
815853
for n in reqs {
854+
self.matched_reqs.insert(n);
816855
if matches.positionals.contains_key(n) {continue;}
817856
if matches.opts.contains_key(n) {continue;}
818857
if matches.flags.contains_key(n) {continue;}
@@ -955,13 +994,15 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
955994
self.required.remove(name);
956995
}
957996
}
958-
if self.required.contains(v.name) {
997+
// No need to check for existance, returns None if not found
998+
// if self.required.contains(v.name) {
959999
self.required.remove(v.name);
960-
}
1000+
// }
9611001
if let Some(ref reqs) = v.requires {
9621002
// Add all required args which aren't already found in matches to the
9631003
// final required list
9641004
for n in reqs {
1005+
self.matched_reqs.insert(n);
9651006
if matches.opts.contains_key(n) { continue; }
9661007
if matches.flags.contains_key(n) { continue; }
9671008
if matches.positionals.contains_key(n) { continue; }
@@ -1002,9 +1043,10 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
10021043

10031044
// If this flag was requierd, remove it
10041045
// .. even though Flags shouldn't be required
1005-
if self.required.contains(v.name) {
1046+
// No need to check for existance, returns None if not found
1047+
// if self.required.contains(v.name) {
10061048
self.required.remove(v.name);
1007-
}
1049+
// }
10081050

10091051
// Add all of this flags "mutually excludes" list to the master list
10101052
if let Some(ref bl) = v.blacklist {
@@ -1017,6 +1059,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
10171059
// Add all required args which aren't already found in matches to the master list
10181060
if let Some(ref reqs) = v.requires {
10191061
for n in reqs {
1062+
self.matched_reqs.insert(n);
10201063
if matches.flags.contains_key(n) { continue; }
10211064
if matches.opts.contains_key(n) { continue; }
10221065
if matches.positionals.contains_key(n) { continue; }
@@ -1080,13 +1123,15 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
10801123
self.required.remove(name);
10811124
}
10821125
}
1083-
if self.required.contains(v.name) {
1126+
// No need to check for existance, returns None if not found
1127+
// if self.required.contains(v.name) {
10841128
self.required.remove(v.name);
1085-
}
1129+
// }
10861130
if let Some(ref reqs) = v.requires {
10871131
// Add all required args which aren't already found in matches to the
10881132
// final required list
10891133
for n in reqs {
1134+
self.matched_reqs.insert(n);
10901135
if matches.opts.contains_key(n) { continue; }
10911136
if matches.flags.contains_key(n) { continue; }
10921137
if matches.positionals.contains_key(n) { continue; }
@@ -1130,9 +1175,11 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
11301175

11311176
// If this flag was requierd, remove it
11321177
// .. even though Flags shouldn't be required
1133-
if self.required.contains(v.name) {
1178+
1179+
// No need to check for existance, returns None if it didn't exist
1180+
// if self.required.contains(v.name) {
11341181
self.required.remove(v.name);
1135-
}
1182+
// }
11361183

11371184
// Add all of this flags "mutually excludes" list to the master list
11381185
if let Some(ref bl) = v.blacklist {
@@ -1145,6 +1192,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
11451192
// Add all required args which aren't already found in matches to the master list
11461193
if let Some(ref reqs) = v.requires {
11471194
for n in reqs {
1195+
self.matched_reqs.insert(n);
11481196
if matches.flags.contains_key(n) { continue; }
11491197
if matches.opts.contains_key(n) { continue; }
11501198
if matches.positionals.contains_key(n) { continue; }

0 commit comments

Comments
 (0)