mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 23:37:32 +00:00
feat(MultipleValues): adds support limited number and named values
This commit is contained in:
parent
42d9b142b7
commit
ae09f05e92
4 changed files with 124 additions and 8 deletions
72
src/app.rs
72
src/app.rs
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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![];
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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<_>>()));
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
|
|
@ -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> {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{""})
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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{""})
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue