refactor: moves validation code into it's own module

This commit is contained in:
Kevin K 2017-03-09 15:51:38 -05:00
parent 642db9b042
commit 44ed8b663c
No known key found for this signature in database
GPG key ID: 17218E4B3692F01A
4 changed files with 446 additions and 422 deletions

View file

@ -4,6 +4,7 @@ mod macros;
pub mod parser;
mod meta;
mod help;
mod validator;
// Std
use std::env;

View file

@ -21,17 +21,17 @@ use app::App;
use app::help::Help;
use app::meta::AppMeta;
use app::settings::AppFlags;
use args::{AnyArg, ArgMatcher, Base, Switched, Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder,
MatchedArg};
use args::{AnyArg, ArgMatcher, Base, Switched, Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder};
use args::settings::ArgSettings;
use completions::ComplGen;
use errors::{Error, ErrorKind};
use errors::Result as ClapResult;
use fmt::{Colorizer, ColorWhen};
use fmt::ColorWhen;
use osstringext::OsStrExt2;
use completions::Shell;
use suggestions;
use app::settings::AppSettings as AS;
use app::validator::Validator;
#[allow(missing_debug_implementations)]
#[doc(hidden)]
@ -46,12 +46,12 @@ pub struct Parser<'a, 'b>
pub opts: Vec<OptBuilder<'a, 'b>>,
pub positionals: VecMap<PosBuilder<'a, 'b>>,
pub subcommands: Vec<App<'a, 'b>>,
groups: Vec<ArgGroup<'a>>,
pub groups: Vec<ArgGroup<'a>>,
pub global_args: Vec<Arg<'a, 'b>>,
required: Vec<&'a str>,
r_ifs: Vec<(&'a str, &'b str, &'a str)>,
blacklist: Vec<&'b str>,
overrides: Vec<&'b str>,
pub required: Vec<&'a str>,
pub r_ifs: Vec<(&'a str, &'b str, &'a str)>,
pub blacklist: Vec<&'b str>,
pub overrides: Vec<&'b str>,
help_short: Option<char>,
version_short: Option<char>,
cache: Option<&'a str>,
@ -1097,54 +1097,9 @@ impl<'a, 'b> Parser<'a, 'b>
});
}
self.validate(needs_val_of, subcmd_name, matcher)
Validator::new(self).validate(needs_val_of, subcmd_name, matcher)
}
fn validate(&mut self,
needs_val_of: Option<&'a str>,
subcmd_name: Option<String>,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<()> {
debugln!("Parser::validate;");
let mut reqs_validated = false;
try!(self.add_defaults(matcher));
if let Some(a) = needs_val_of {
debugln!("Parser::validate: needs_val_of={:?}", a);
if let Some(o) = find_by_name!(self, &a, opts, iter) {
try!(self.validate_required(matcher));
reqs_validated = true;
let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(o,
&*self.create_current_usage(matcher, None),
self.color()));
}
}
}
try!(self.validate_blacklist(matcher));
if !(self.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated {
try!(self.validate_required(matcher));
}
try!(self.validate_matched_args(matcher));
matcher.usage(self.create_usage(&[]));
if matcher.is_empty() && matcher.subcommand_name().is_none() &&
self.is_set(AS::ArgRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
Ok(())
}
fn propogate_help_version(&mut self) {
debugln!("Parser::propogate_help_version;");
@ -1294,7 +1249,7 @@ impl<'a, 'b> Parser<'a, 'b>
args.iter().map(ToOwned::to_owned).collect()
}
fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> {
pub fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> {
let mut g_vec = vec![];
let mut args = vec![];
@ -1697,62 +1652,6 @@ impl<'a, 'b> Parser<'a, 'b>
Ok(None)
}
fn validate_values<A>(&self,
arg: &A,
ma: &MatchedArg,
matcher: &ArgMatcher<'a>)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Parser::validate_values: arg={:?}", arg.name());
for val in &ma.vals {
if self.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debugln!("Parser::validate_values: invalid UTF-8 found in val {:?}",
val);
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher, None),
self.color()));
}
if let Some(p_vals) = arg.possible_vals() {
debugln!("Parser::validate_values: possible_vals={:?}", p_vals);
let val_str = val.to_string_lossy();
if !p_vals.contains(&&*val_str) {
return Err(Error::invalid_value(val_str,
p_vals,
arg,
&*self.create_current_usage(matcher, None),
self.color()));
}
}
if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() &&
matcher.contains(&*arg.name()) {
debugln!("Parser::validate_values: illegal empty val found");
return Err(Error::empty_value(arg,
&*self.create_current_usage(matcher, None),
self.color()));
}
if let Some(vtor) = arg.validator() {
debug!("Parser::validate_values: checking validator...");
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg), e, self.color()));
} else {
sdebugln!("good");
}
}
if let Some(vtor) = arg.validator_os() {
debug!("Parser::validate_values: checking validator_os...");
if let Err(e) = vtor(&val) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg),
(*e).to_string_lossy().to_string(),
self.color()));
} else {
sdebugln!("good");
}
}
}
Ok(())
}
fn parse_flag(&self,
flag: &FlagBuilder<'a, 'b>,
@ -1767,311 +1666,6 @@ impl<'a, 'b> Parser<'a, 'b>
Ok(())
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Parser::validate_blacklist: blacklist={:?}", self.blacklist);
macro_rules! build_err {
($me:ident, $name:expr, $matcher:ident) => ({
debugln!("build_err!: name={}", $name);
let mut c_with = find_from!($me, $name, blacklist, &$matcher);
c_with = c_with.or(
$me.find_any_arg($name).map_or(None, |aa| aa.blacklist())
.map_or(None,
|bl| bl.iter().find(|arg| $matcher.contains(arg)))
.map_or(None, |an| $me.find_any_arg(an))
.map_or(None, |aa| Some(format!("{}", aa)))
);
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, $name);
$matcher.remove($name);
let usg = $me.create_current_usage($matcher, None);
if let Some(f) = find_by_name!($me, $name, flags, iter) {
debugln!("build_err!: It was a flag...");
Error::argument_conflict(f, c_with, &*usg, self.color())
} else if let Some(o) = find_by_name!($me, $name, opts, iter) {
debugln!("build_err!: It was an option...");
Error::argument_conflict(o, c_with, &*usg, self.color())
} else {
match find_by_name!($me, $name, positionals, values) {
Some(p) => {
debugln!("build_err!: It was a positional...");
Error::argument_conflict(p, c_with, &*usg, self.color())
},
None => panic!(INTERNAL_ERROR_MSG)
}
}
});
}
for name in &self.blacklist {
debugln!("Parser::validate_blacklist:iter: Checking blacklisted name: {}",
name);
if self.groups.iter().any(|g| &g.name == name) {
debugln!("Parser::validate_blacklist:iter: groups contains it...");
for n in self.arg_names_in_group(name) {
debugln!("Parser::validate_blacklist:iter:iter: Checking arg '{}' in group...",
n);
if matcher.contains(n) {
debugln!("Parser::validate_blacklist:iter:iter: matcher contains it...");
return Err(build_err!(self, &n, matcher));
}
}
} else if matcher.contains(name) {
debugln!("Parser::validate_blacklist:iter: matcher contains it...");
return Err(build_err!(self, name, matcher));
}
}
Ok(())
}
fn validate_matched_args(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Parser::validate_matched_args;");
for (name, ma) in matcher.iter() {
debugln!("Parser::validate_matched_args:iter:{}: vals={:#?}",
name,
ma.vals);
if let Some(opt) = find_by_name!(self, name, opts, iter) {
try!(self.validate_arg_num_vals(opt, ma, matcher));
try!(self.validate_values(opt, ma, matcher));
try!(self.validate_arg_requires(opt, ma, matcher));
try!(self.validate_arg_num_occurs(opt, ma, matcher));
} else if let Some(flag) = find_by_name!(self, name, flags, iter) {
try!(self.validate_arg_requires(flag, ma, matcher));
try!(self.validate_arg_num_occurs(flag, ma, matcher));
} else if let Some(pos) = find_by_name!(self, name, positionals, values) {
try!(self.validate_arg_num_vals(pos, ma, matcher));
try!(self.validate_arg_num_occurs(pos, ma, matcher));
try!(self.validate_values(pos, ma, matcher));
try!(self.validate_arg_requires(pos, ma, matcher));
} else {
let grp = self.groups.iter().find(|g| &g.name == name).expect(INTERNAL_ERROR_MSG);
if let Some(ref g_reqs) = grp.requires {
if g_reqs.iter().any(|&n| !matcher.contains(n)) {
return self.missing_required_error(matcher, None);
}
}
}
}
Ok(())
}
fn validate_arg_num_occurs<A>(&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Parser::validate_arg_num_occurs: a={};", a.name());
if ma.occurs > 1 && !a.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage(a,
&*self.create_current_usage(matcher, None),
self.color()));
}
Ok(())
}
fn validate_arg_num_vals<A>(&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Parser::validate_arg_num_vals;");
if let Some(num) = a.num_vals() {
debugln!("Parser::validate_arg_num_vals: num_vals set...{}", num);
let should_err = if a.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u64) % num) != 0
} else {
num != (ma.vals.len() as u64)
};
if should_err {
debugln!("Parser::validate_arg_num_vals: Sending error WrongNumberOfValues");
return Err(Error::wrong_number_of_values(a,
num,
if a.is_set(ArgSettings::Multiple) {
(ma.vals.len() % num as usize)
} else {
ma.vals.len()
},
if ma.vals.len() == 1 ||
(a.is_set(ArgSettings::Multiple) &&
(ma.vals.len() % num as usize) ==
1) {
"as"
} else {
"ere"
},
&*self.create_current_usage(matcher, None),
self.color()));
}
}
if let Some(num) = a.max_vals() {
debugln!("Parser::validate_arg_num_vals: max_vals set...{}", num);
if (ma.vals.len() as u64) > num {
debugln!("Parser::validate_arg_num_vals: Sending error TooManyValues");
return Err(Error::too_many_values(ma.vals
.iter()
.last()
.expect(INTERNAL_ERROR_MSG)
.to_str()
.expect(INVALID_UTF8),
a,
&*self.create_current_usage(matcher, None),
self.color()));
}
}
if let Some(num) = a.min_vals() {
debugln!("Parser::validate_arg_num_vals: min_vals set: {}", num);
if (ma.vals.len() as u64) < num {
debugln!("Parser::validate_arg_num_vals: Sending error TooFewValues");
return Err(Error::too_few_values(a,
num,
ma.vals.len(),
&*self.create_current_usage(matcher, None),
self.color()));
}
}
// Issue 665 (https://github.com/kbknapp/clap-rs/issues/665)
if a.takes_value() && !a.is_set(ArgSettings::EmptyValues) && ma.vals.is_empty() {
return Err(Error::empty_value(a,
&*self.create_current_usage(matcher, None),
self.color()));
}
Ok(())
}
fn validate_arg_requires<A>(&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Parser::validate_arg_requires;");
if let Some(a_reqs) = a.requires() {
for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) {
if ma.vals
.iter()
.any(|v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name)) {
return self.missing_required_error(matcher, None);
}
}
}
Ok(())
}
#[inline]
fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> {
debugln!("Parser::missing_required_error: extra={:?}", extra);
let c = Colorizer {
use_stderr: true,
when: self.color(),
};
let mut reqs = self.required.iter().map(|&r| &*r).collect::<Vec<_>>();
if let Some(r) = extra {
reqs.push(r);
}
reqs.retain(|n| !matcher.contains(n));
reqs.dedup();
debugln!("Parser::missing_required_error: reqs={:#?}", reqs);
Err(Error::missing_required_argument(&*self.get_required_from(&reqs[..],
Some(matcher),
extra)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
}),
&*self.create_current_usage(matcher, extra),
self.color()))
}
fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!("Parser::validate_required: required={:?};", self.required);
'outer: for name in &self.required {
debugln!("Parser::validate_required:iter:{}:", name);
if matcher.contains(name) {
continue 'outer;
}
if let Some(a) = find_by_name!(self, name, flags, iter) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = find_by_name!(self, name, opts, iter) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = find_by_name!(self, name, positionals, values) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
}
return self.missing_required_error(matcher, None);
}
// Validate the conditionally required args
for &(a, v, r) in &self.r_ifs {
if let Some(ma) = matcher.get(a) {
if matcher.get(r).is_none() {
if ma.vals.iter().any(|val| val == v) {
return self.missing_required_error(matcher, Some(r));
}
}
}
}
Ok(())
}
fn check_conflicts<A>(&self, a: &A, matcher: &ArgMatcher) -> Option<bool>
where A: AnyArg<'a, 'b>
{
debugln!("Parser::check_conflicts: a={:?};", a.name());
a.blacklist().map(|bl| {
bl.iter().any(|conf| {
matcher.contains(conf) ||
self.groups
.iter()
.find(|g| &g.name == conf)
.map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
})
})
}
fn check_required_unless<A>(&self, a: &A, matcher: &ArgMatcher) -> Option<bool>
where A: AnyArg<'a, 'b>
{
debugln!("Parser::check_required_unless: a={:?};", a.name());
macro_rules! check {
($how:ident, $_self:ident, $a:ident, $m:ident) => {{
$a.required_unless().map(|ru| {
ru.iter().$how(|n| {
$m.contains(n) || {
if let Some(grp) = $_self.groups.iter().find(|g| &g.name == n) {
grp.args.iter().any(|arg| $m.contains(arg))
} else {
false
}
}
})
})
}};
}
if a.is_set(ArgSettings::RequiredUnlessAll) {
check!(all, self, a, matcher)
} else {
check!(any, self, a, matcher)
}
}
#[inline]
fn is_missing_required_ok<A>(&self, a: &A, matcher: &ArgMatcher) -> bool
where A: AnyArg<'a, 'b>
{
debugln!("Parser::is_missing_required_ok: a={}", a.name());
self.check_conflicts(a, matcher).unwrap_or(false) ||
self.check_required_unless(a, matcher).unwrap_or(false)
}
fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
// Didn't match a flag or option...maybe it was a typo and close to one
let suffix =
@ -2261,7 +1855,7 @@ impl<'a, 'b> Parser<'a, 'b>
Help::write_parser_help_to_stderr(w, self)
}
fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
pub fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
macro_rules! add_val {
(@default $_self:ident, $a:ident, $m:ident) => {
if let Some(ref val) = $a.v.default_val {

430
src/app/validator.rs Normal file
View file

@ -0,0 +1,430 @@
// std
use std::fmt::Display;
// Internal
use INTERNAL_ERROR_MSG;
use INVALID_UTF8;
use args::{AnyArg, ArgMatcher, MatchedArg};
use args::settings::ArgSettings;
use errors::{Error, ErrorKind};
use errors::Result as ClapResult;
use osstringext::OsStrExt2;
use app::settings::AppSettings as AS;
use app::parser::Parser;
use fmt::Colorizer;
pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>) where 'a: 'b, 'b: 'z;
impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
pub fn new(p: &'z mut Parser<'a, 'b>) -> Self {
Validator(p)
}
pub fn validate(&mut self,
needs_val_of: Option<&'a str>,
subcmd_name: Option<String>,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<()> {
debugln!("Validator::validate;");
let mut reqs_validated = false;
try!(self.0.add_defaults(matcher));
if let Some(a) = needs_val_of {
debugln!("Validator::validate: needs_val_of={:?}", a);
if let Some(o) = find_by_name!(self.0, &a, opts, iter) {
try!(self.validate_required(matcher));
reqs_validated = true;
let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(o,
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
}
}
try!(self.validate_blacklist(matcher));
if !(self.0.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated {
try!(self.validate_required(matcher));
}
try!(self.validate_matched_args(matcher));
matcher.usage(self.0.create_usage(&[]));
if matcher.is_empty() && matcher.subcommand_name().is_none() &&
self.0.is_set(AS::ArgRequiredElseHelp) {
let mut out = vec![];
try!(self.0.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
Ok(())
}
fn validate_values<A>(&self,
arg: &A,
ma: &MatchedArg,
matcher: &ArgMatcher<'a>)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Validator::validate_values: arg={:?}", arg.name());
for val in &ma.vals {
if self.0.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debugln!("Validator::validate_values: invalid UTF-8 found in val {:?}",
val);
return Err(Error::invalid_utf8(&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
if let Some(p_vals) = arg.possible_vals() {
debugln!("Validator::validate_values: possible_vals={:?}", p_vals);
let val_str = val.to_string_lossy();
if !p_vals.contains(&&*val_str) {
return Err(Error::invalid_value(val_str,
p_vals,
arg,
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
}
if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() &&
matcher.contains(&*arg.name()) {
debugln!("Validator::validate_values: illegal empty val found");
return Err(Error::empty_value(arg,
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
if let Some(vtor) = arg.validator() {
debug!("Validator::validate_values: checking validator...");
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg), e, self.0.color()));
} else {
sdebugln!("good");
}
}
if let Some(vtor) = arg.validator_os() {
debug!("Validator::validate_values: checking validator_os...");
if let Err(e) = vtor(&val) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg),
(*e).to_string_lossy().to_string(),
self.0.color()));
} else {
sdebugln!("good");
}
}
}
Ok(())
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_blacklist: blacklist={:?}", self.0.blacklist);
macro_rules! build_err {
($p:expr, $name:expr, $matcher:ident) => ({
debugln!("build_err!: name={}", $name);
let mut c_with = find_from!($p, $name, blacklist, &$matcher);
c_with = c_with.or(
$p.find_any_arg($name).map_or(None, |aa| aa.blacklist())
.map_or(None,
|bl| bl.iter().find(|arg| $matcher.contains(arg)))
.map_or(None, |an| $p.find_any_arg(an))
.map_or(None, |aa| Some(format!("{}", aa)))
);
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, $name);
$matcher.remove($name);
let usg = $p.create_current_usage($matcher, None);
if let Some(f) = find_by_name!($p, $name, flags, iter) {
debugln!("build_err!: It was a flag...");
Error::argument_conflict(f, c_with, &*usg, self.0.color())
} else if let Some(o) = find_by_name!($p, $name, opts, iter) {
debugln!("build_err!: It was an option...");
Error::argument_conflict(o, c_with, &*usg, self.0.color())
} else {
match find_by_name!($p, $name, positionals, values) {
Some(p) => {
debugln!("build_err!: It was a positional...");
Error::argument_conflict(p, c_with, &*usg, self.0.color())
},
None => panic!(INTERNAL_ERROR_MSG)
}
}
});
}
for name in &self.0.blacklist {
debugln!("Validator::validate_blacklist:iter: Checking blacklisted name: {}",
name);
if self.0.groups.iter().any(|g| &g.name == name) {
debugln!("Validator::validate_blacklist:iter: groups contains it...");
for n in self.0.arg_names_in_group(name) {
debugln!("Validator::validate_blacklist:iter:iter: Checking arg '{}' in group...",
n);
if matcher.contains(n) {
debugln!("Validator::validate_blacklist:iter:iter: matcher contains it...");
return Err(build_err!(self.0, &n, matcher));
}
}
} else if matcher.contains(name) {
debugln!("Validator::validate_blacklist:iter: matcher contains it...");
return Err(build_err!(self.0, name, matcher));
}
}
Ok(())
}
fn validate_matched_args(&self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
debugln!("Validator::validate_matched_args;");
for (name, ma) in matcher.iter() {
debugln!("Validator::validate_matched_args:iter:{}: vals={:#?}",
name,
ma.vals);
if let Some(opt) = find_by_name!(self.0, name, opts, iter) {
try!(self.validate_arg_num_vals(opt, ma, matcher));
try!(self.validate_values(opt, ma, matcher));
try!(self.validate_arg_requires(opt, ma, matcher));
try!(self.validate_arg_num_occurs(opt, ma, matcher));
} else if let Some(flag) = find_by_name!(self.0, name, flags, iter) {
try!(self.validate_arg_requires(flag, ma, matcher));
try!(self.validate_arg_num_occurs(flag, ma, matcher));
} else if let Some(pos) = find_by_name!(self.0, name, positionals, values) {
try!(self.validate_arg_num_vals(pos, ma, matcher));
try!(self.validate_arg_num_occurs(pos, ma, matcher));
try!(self.validate_values(pos, ma, matcher));
try!(self.validate_arg_requires(pos, ma, matcher));
} else {
let grp = self.0.groups.iter().find(|g| &g.name == name).expect(INTERNAL_ERROR_MSG);
if let Some(ref g_reqs) = grp.requires {
if g_reqs.iter().any(|&n| !matcher.contains(n)) {
return self.missing_required_error(matcher, None);
}
}
}
}
Ok(())
}
fn validate_arg_num_occurs<A>(&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Validator::validate_arg_num_occurs: a={};", a.name());
if ma.occurs > 1 && !a.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage(a,
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
Ok(())
}
fn validate_arg_num_vals<A>(&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Validator::validate_arg_num_vals;");
if let Some(num) = a.num_vals() {
debugln!("Validator::validate_arg_num_vals: num_vals set...{}", num);
let should_err = if a.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u64) % num) != 0
} else {
num != (ma.vals.len() as u64)
};
if should_err {
debugln!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues");
return Err(Error::wrong_number_of_values(a,
num,
if a.is_set(ArgSettings::Multiple) {
(ma.vals.len() % num as usize)
} else {
ma.vals.len()
},
if ma.vals.len() == 1 ||
(a.is_set(ArgSettings::Multiple) &&
(ma.vals.len() % num as usize) ==
1) {
"as"
} else {
"ere"
},
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
}
if let Some(num) = a.max_vals() {
debugln!("Validator::validate_arg_num_vals: max_vals set...{}", num);
if (ma.vals.len() as u64) > num {
debugln!("Validator::validate_arg_num_vals: Sending error TooManyValues");
return Err(Error::too_many_values(ma.vals
.iter()
.last()
.expect(INTERNAL_ERROR_MSG)
.to_str()
.expect(INVALID_UTF8),
a,
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
}
if let Some(num) = a.min_vals() {
debugln!("Validator::validate_arg_num_vals: min_vals set: {}", num);
if (ma.vals.len() as u64) < num {
debugln!("Validator::validate_arg_num_vals: Sending error TooFewValues");
return Err(Error::too_few_values(a,
num,
ma.vals.len(),
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
}
// Issue 665 (https://github.com/kbknapp/clap-rs/issues/665)
if a.takes_value() && !a.is_set(ArgSettings::EmptyValues) && ma.vals.is_empty() {
return Err(Error::empty_value(a,
&*self.0.create_current_usage(matcher, None),
self.0.color()));
}
Ok(())
}
fn validate_arg_requires<A>(&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher)
-> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Validator::validate_arg_requires;");
if let Some(a_reqs) = a.requires() {
for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) {
if ma.vals
.iter()
.any(|v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name)) {
return self.missing_required_error(matcher, None);
}
}
}
Ok(())
}
fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_required: required={:?};", self.0.required);
'outer: for name in &self.0.required {
debugln!("Validator::validate_required:iter:{}:", name);
if matcher.contains(name) {
continue 'outer;
}
if let Some(a) = find_by_name!(self.0, name, flags, iter) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = find_by_name!(self.0, name, opts, iter) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = find_by_name!(self.0, name, positionals, values) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
}
return self.missing_required_error(matcher, None);
}
// Validate the conditionally required args
for &(a, v, r) in &self.0.r_ifs {
if let Some(ma) = matcher.get(a) {
if matcher.get(r).is_none() {
if ma.vals.iter().any(|val| val == v) {
return self.missing_required_error(matcher, Some(r));
}
}
}
}
Ok(())
}
fn validate_conflicts<A>(&self, a: &A, matcher: &ArgMatcher) -> Option<bool>
where A: AnyArg<'a, 'b>
{
debugln!("Validator::validate_conflicts: a={:?};", a.name());
a.blacklist().map(|bl| {
bl.iter().any(|conf| {
matcher.contains(conf) ||
self.0.groups
.iter()
.find(|g| &g.name == conf)
.map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
})
})
}
fn validate_required_unless<A>(&self, a: &A, matcher: &ArgMatcher) -> Option<bool>
where A: AnyArg<'a, 'b>
{
debugln!("Validator::validate_required_unless: a={:?};", a.name());
macro_rules! check {
($how:ident, $_self:expr, $a:ident, $m:ident) => {{
$a.required_unless().map(|ru| {
ru.iter().$how(|n| {
$m.contains(n) || {
if let Some(grp) = $_self.groups.iter().find(|g| &g.name == n) {
grp.args.iter().any(|arg| $m.contains(arg))
} else {
false
}
}
})
})
}};
}
if a.is_set(ArgSettings::RequiredUnlessAll) {
check!(all, self.0, a, matcher)
} else {
check!(any, self.0, a, matcher)
}
}
#[inline]
fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> {
debugln!("Validator::missing_required_error: extra={:?}", extra);
let c = Colorizer {
use_stderr: true,
when: self.0.color(),
};
let mut reqs = self.0.required.iter().map(|&r| &*r).collect::<Vec<_>>();
if let Some(r) = extra {
reqs.push(r);
}
reqs.retain(|n| !matcher.contains(n));
reqs.dedup();
debugln!("Validator::missing_required_error: reqs={:#?}", reqs);
Err(Error::missing_required_argument(&*self.0.get_required_from(&reqs[..],
Some(matcher),
extra)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
}),
&*self.0.create_current_usage(matcher, extra),
self.0.color()))
}
#[inline]
fn is_missing_required_ok<A>(&self, a: &A, matcher: &ArgMatcher) -> bool
where A: AnyArg<'a, 'b>
{
debugln!("Validator::is_missing_required_ok: a={}", a.name());
self.validate_conflicts(a, matcher).unwrap_or(false) ||
self.validate_required_unless(a, matcher).unwrap_or(false)
}
}

View file

@ -847,7 +847,7 @@ macro_rules! vec_remove_all {
};
}
macro_rules! find_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
($_self:expr, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
@ -877,7 +877,7 @@ macro_rules! find_from {
}
macro_rules! find_name_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
($_self:expr, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
@ -908,8 +908,8 @@ macro_rules! find_name_from {
// Finds an arg by name
macro_rules! find_by_name {
($_self:ident, $name:expr, $what:ident, $how:ident) => {
$_self.$what.$how().find(|o| &o.b.name == $name)
($p:expr, $name:expr, $what:ident, $how:ident) => {
$p.$what.$how().find(|o| &o.b.name == $name)
}
}
@ -1057,7 +1057,6 @@ macro_rules! _names {
.iter()
.filter(|s| s.p.meta.aliases.is_some())
.flat_map(|s| s.p.meta.aliases.as_ref().unwrap().iter().map(|&(n, _)| n)))
// .map(|n| &n)
}}
}