mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
fix(positional args): all previous positional args become required when
a latter one is required Closes #50
This commit is contained in:
parent
1a1ce2a7da
commit
343d47dcbf
1 changed files with 65 additions and 17 deletions
82
src/app.rs
82
src/app.rs
|
@ -55,6 +55,7 @@ pub struct App<'a, 'v, 'ab, 'u, 'ar> {
|
||||||
needs_short_version: bool,
|
needs_short_version: bool,
|
||||||
needs_subcmd_help: bool,
|
needs_subcmd_help: bool,
|
||||||
required: HashSet<&'ar str>,
|
required: HashSet<&'ar str>,
|
||||||
|
matched_reqs: HashSet<&'ar str>,
|
||||||
arg_list: HashSet<&'ar str>,
|
arg_list: HashSet<&'ar str>,
|
||||||
short_list: HashSet<char>,
|
short_list: HashSet<char>,
|
||||||
long_list: HashSet<&'ar str>,
|
long_list: HashSet<&'ar str>,
|
||||||
|
@ -91,6 +92,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
needs_subcmd_help: true,
|
needs_subcmd_help: true,
|
||||||
needs_short_version: true,
|
needs_short_version: true,
|
||||||
required: HashSet::new(),
|
required: HashSet::new(),
|
||||||
|
matched_reqs: HashSet::new(),
|
||||||
arg_list: HashSet::new(),
|
arg_list: HashSet::new(),
|
||||||
short_list: HashSet::new(),
|
short_list: HashSet::new(),
|
||||||
long_list: HashSet::new(),
|
long_list: HashSet::new(),
|
||||||
|
@ -216,6 +218,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
if a.takes_value {
|
if a.takes_value {
|
||||||
panic!("Argument \"{}\" has conflicting requirements, both index() and takes_value(true) were supplied", a.name);
|
panic!("Argument \"{}\" has conflicting requirements, both index() and takes_value(true) were supplied", a.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the Positional Arguemnt Builder with each HashSet = None to only allocate those that require it
|
// Create the Positional Arguemnt Builder with each HashSet = None to only allocate those that require it
|
||||||
let mut pb = PosBuilder {
|
let mut pb = PosBuilder {
|
||||||
name: a.name,
|
name: a.name,
|
||||||
|
@ -238,7 +241,8 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, '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); }
|
||||||
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
|
||||||
|
@ -409,16 +413,31 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_usage(&self, more_info: bool) {
|
fn print_usage(&self, more_info: bool) {
|
||||||
|
let tab = " ";
|
||||||
println!("USAGE:");
|
println!("USAGE:");
|
||||||
if let Some(u) = self.usage_str {
|
if let Some(u) = self.usage_str {
|
||||||
println!(" {}",u);
|
println!("{}{}",tab,u);
|
||||||
} 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();
|
||||||
let opts = !self.opts.is_empty();
|
let opts = !self.opts.is_empty();
|
||||||
let subcmds = !self.subcommands.is_empty();
|
let subcmds = !self.subcommands.is_empty();
|
||||||
let mut num_req_pos = 0;
|
let mut num_req_pos = 0;
|
||||||
let req_pos = self.positionals_idx.values().filter_map(|ref x| if x.required || self.required.contains(x.name) {
|
let mut matched_pos_reqs = HashSet::new();
|
||||||
|
// If it's required we also need to ensure all previous positionals are required too
|
||||||
|
let mut found = false;
|
||||||
|
for p in self.positionals_idx.values().rev() {
|
||||||
|
if found {
|
||||||
|
matched_pos_reqs.insert(p.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self.matched_reqs.contains(p.name) {
|
||||||
|
matched_pos_reqs.insert(p.name);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let req_pos = self.positionals_idx.values().filter_map(|ref x| if x.required || matched_pos_reqs.contains(x.name) {
|
||||||
num_req_pos += 1;
|
num_req_pos += 1;
|
||||||
if x.multiple {
|
if x.multiple {
|
||||||
Some(format!("<{}>...", x.name))
|
Some(format!("<{}>...", x.name))
|
||||||
|
@ -430,7 +449,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
})
|
})
|
||||||
.fold(String::new(), |acc, ref name| acc + &format!("{} ", name)[..]);
|
.fold(String::new(), |acc, ref name| acc + &format!("{} ", name)[..]);
|
||||||
let mut num_req_opts = 0;
|
let mut num_req_opts = 0;
|
||||||
let req_opts = self.opts.values().filter_map(|x| if x.required || self.required.contains(x.name) {
|
let req_opts = self.opts.values().filter_map(|x| if x.required || self.matched_reqs.contains(x.name) {
|
||||||
num_req_opts += 1;
|
num_req_opts += 1;
|
||||||
Some(x)
|
Some(x)
|
||||||
}else {
|
}else {
|
||||||
|
@ -442,7 +461,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
format!("{} ",o.short.unwrap())
|
format!("{} ",o.short.unwrap())
|
||||||
},o.name));
|
},o.name));
|
||||||
|
|
||||||
print!(" {} {} {} {} {}", self.bin_name.clone().unwrap_or(self.name.clone()),
|
print!("{}{} {} {} {} {}",tab, self.bin_name.clone().unwrap_or(self.name.clone()),
|
||||||
if flags {"[FLAGS]"} else {""},
|
if flags {"[FLAGS]"} else {""},
|
||||||
if opts {
|
if opts {
|
||||||
if num_req_opts != self.opts.len() && !req_opts.is_empty() {
|
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>{
|
||||||
|
|
||||||
pub fn get_matches(mut self) -> ArgMatches<'ar> {
|
pub fn get_matches(mut self) -> ArgMatches<'ar> {
|
||||||
self.verify_positionals();
|
self.verify_positionals();
|
||||||
for sc in self.subcommands.values() {
|
for (_,sc) in self.subcommands.iter_mut() {
|
||||||
sc.verify_positionals();
|
sc.verify_positionals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +693,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
matches
|
matches
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_positionals(&self) {
|
fn verify_positionals(&mut self) {
|
||||||
// Because you must wait until all arguments have been supplied, this is the first chance
|
// Because you must wait until all arguments have been supplied, this is the first chance
|
||||||
// to make assertions on positional argument indexes
|
// to make assertions on positional argument indexes
|
||||||
//
|
//
|
||||||
|
@ -695,6 +714,19 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
panic!("Found positional argument \"{}\" which accepts multiple values but it's not the last positional argument (i.e. others have a higher index)",
|
panic!("Found positional argument \"{}\" which accepts multiple values but it's not the last positional argument (i.e. others have a higher index)",
|
||||||
p.name);
|
p.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's required we also need to ensure all previous positionals are required too
|
||||||
|
let mut found = false;
|
||||||
|
for (_, p) in self.positionals_idx.iter_mut().rev() {
|
||||||
|
if found {
|
||||||
|
p.required = true;
|
||||||
|
self.required.insert(p.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if p.required {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matches_from(&mut self, matches: &mut ArgMatches<'ar>, it: &mut IntoIter<String>) {
|
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>{
|
||||||
needs_val_of = None;
|
needs_val_of = None;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg_slice.starts_with("--") && !pos_only {
|
if arg_slice.starts_with("--") && !pos_only {
|
||||||
if arg_slice.len() == 2 {
|
if arg_slice.len() == 2 {
|
||||||
pos_only = true;
|
pos_only = true;
|
||||||
|
@ -762,6 +795,9 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
format!("Found positional argument {}, but {} doesn't accept any", arg, self.name),
|
format!("Found positional argument {}, but {} doesn't accept any", arg, self.name),
|
||||||
true, true);
|
true, true);
|
||||||
}
|
}
|
||||||
|
// If we find that an argument requires a positiona, we need to update all the
|
||||||
|
// 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 let Some(p) = self.positionals_idx.get(&pos_counter) {
|
||||||
if self.blacklist.contains(p.name) {
|
if self.blacklist.contains(p.name) {
|
||||||
self.report_error(format!("The argument \"{}\" is mutually exclusive with one or more other arguments", arg),
|
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>{
|
||||||
self.required.remove(name);
|
self.required.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.required.contains(p.name) {
|
|
||||||
|
// No need to check for existance, returns None if not found
|
||||||
|
// if self.required.contains(p.name) {
|
||||||
self.required.remove(p.name);
|
self.required.remove(p.name);
|
||||||
}
|
// }
|
||||||
if let Some(ref reqs) = p.requires {
|
if let Some(ref reqs) = p.requires {
|
||||||
// Add all required args which aren't already found in matches to the
|
// Add all required args which aren't already found in matches to the
|
||||||
// final required list
|
// final required list
|
||||||
for n in reqs {
|
for n in reqs {
|
||||||
|
self.matched_reqs.insert(n);
|
||||||
if matches.positionals.contains_key(n) {continue;}
|
if matches.positionals.contains_key(n) {continue;}
|
||||||
if matches.opts.contains_key(n) {continue;}
|
if matches.opts.contains_key(n) {continue;}
|
||||||
if matches.flags.contains_key(n) {continue;}
|
if matches.flags.contains_key(n) {continue;}
|
||||||
|
@ -955,13 +994,15 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
self.required.remove(name);
|
self.required.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.required.contains(v.name) {
|
// No need to check for existance, returns None if not found
|
||||||
|
// if self.required.contains(v.name) {
|
||||||
self.required.remove(v.name);
|
self.required.remove(v.name);
|
||||||
}
|
// }
|
||||||
if let Some(ref reqs) = v.requires {
|
if let Some(ref reqs) = v.requires {
|
||||||
// Add all required args which aren't already found in matches to the
|
// Add all required args which aren't already found in matches to the
|
||||||
// final required list
|
// final required list
|
||||||
for n in reqs {
|
for n in reqs {
|
||||||
|
self.matched_reqs.insert(n);
|
||||||
if matches.opts.contains_key(n) { continue; }
|
if matches.opts.contains_key(n) { continue; }
|
||||||
if matches.flags.contains_key(n) { continue; }
|
if matches.flags.contains_key(n) { continue; }
|
||||||
if matches.positionals.contains_key(n) { continue; }
|
if matches.positionals.contains_key(n) { continue; }
|
||||||
|
@ -1002,9 +1043,10 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
|
|
||||||
// If this flag was requierd, remove it
|
// If this flag was requierd, remove it
|
||||||
// .. even though Flags shouldn't be required
|
// .. even though Flags shouldn't be required
|
||||||
if self.required.contains(v.name) {
|
// No need to check for existance, returns None if not found
|
||||||
|
// if self.required.contains(v.name) {
|
||||||
self.required.remove(v.name);
|
self.required.remove(v.name);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Add all of this flags "mutually excludes" list to the master list
|
// Add all of this flags "mutually excludes" list to the master list
|
||||||
if let Some(ref bl) = v.blacklist {
|
if let Some(ref bl) = v.blacklist {
|
||||||
|
@ -1017,6 +1059,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
// Add all required args which aren't already found in matches to the master list
|
// Add all required args which aren't already found in matches to the master list
|
||||||
if let Some(ref reqs) = v.requires {
|
if let Some(ref reqs) = v.requires {
|
||||||
for n in reqs {
|
for n in reqs {
|
||||||
|
self.matched_reqs.insert(n);
|
||||||
if matches.flags.contains_key(n) { continue; }
|
if matches.flags.contains_key(n) { continue; }
|
||||||
if matches.opts.contains_key(n) { continue; }
|
if matches.opts.contains_key(n) { continue; }
|
||||||
if matches.positionals.contains_key(n) { continue; }
|
if matches.positionals.contains_key(n) { continue; }
|
||||||
|
@ -1080,13 +1123,15 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
self.required.remove(name);
|
self.required.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.required.contains(v.name) {
|
// No need to check for existance, returns None if not found
|
||||||
|
// if self.required.contains(v.name) {
|
||||||
self.required.remove(v.name);
|
self.required.remove(v.name);
|
||||||
}
|
// }
|
||||||
if let Some(ref reqs) = v.requires {
|
if let Some(ref reqs) = v.requires {
|
||||||
// Add all required args which aren't already found in matches to the
|
// Add all required args which aren't already found in matches to the
|
||||||
// final required list
|
// final required list
|
||||||
for n in reqs {
|
for n in reqs {
|
||||||
|
self.matched_reqs.insert(n);
|
||||||
if matches.opts.contains_key(n) { continue; }
|
if matches.opts.contains_key(n) { continue; }
|
||||||
if matches.flags.contains_key(n) { continue; }
|
if matches.flags.contains_key(n) { continue; }
|
||||||
if matches.positionals.contains_key(n) { continue; }
|
if matches.positionals.contains_key(n) { continue; }
|
||||||
|
@ -1130,9 +1175,11 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
|
|
||||||
// If this flag was requierd, remove it
|
// If this flag was requierd, remove it
|
||||||
// .. even though Flags shouldn't be required
|
// .. even though Flags shouldn't be required
|
||||||
if self.required.contains(v.name) {
|
|
||||||
|
// No need to check for existance, returns None if it didn't exist
|
||||||
|
// if self.required.contains(v.name) {
|
||||||
self.required.remove(v.name);
|
self.required.remove(v.name);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Add all of this flags "mutually excludes" list to the master list
|
// Add all of this flags "mutually excludes" list to the master list
|
||||||
if let Some(ref bl) = v.blacklist {
|
if let Some(ref bl) = v.blacklist {
|
||||||
|
@ -1145,6 +1192,7 @@ impl<'a, 'v, 'ab, 'u, 'ar> App<'a, 'v, 'ab, 'u, 'ar>{
|
||||||
// Add all required args which aren't already found in matches to the master list
|
// Add all required args which aren't already found in matches to the master list
|
||||||
if let Some(ref reqs) = v.requires {
|
if let Some(ref reqs) = v.requires {
|
||||||
for n in reqs {
|
for n in reqs {
|
||||||
|
self.matched_reqs.insert(n);
|
||||||
if matches.flags.contains_key(n) { continue; }
|
if matches.flags.contains_key(n) { continue; }
|
||||||
if matches.opts.contains_key(n) { continue; }
|
if matches.opts.contains_key(n) { continue; }
|
||||||
if matches.positionals.contains_key(n) { continue; }
|
if matches.positionals.contains_key(n) { continue; }
|
||||||
|
|
Loading…
Reference in a new issue