use crate::build::app::AppSettings as AS;
use crate::build::{Arg, ArgSettings};
use crate::output::fmt::{Colorizer, ColorizerOption};
use crate::output::Usage;
use crate::parse::errors::Result as ClapResult;
use crate::parse::errors::{Error, ErrorKind};
use crate::parse::{ArgMatcher, MatchedArg, ParseResult, Parser};
use crate::util::ChildGraph;
use crate::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8;
type Id = u64;
pub struct Validator<'b, 'c, 'z>
where
'b: 'c,
'c: 'z,
{
p: &'z mut Parser<'b, 'c>,
c: ChildGraph<Id>,
}
impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
pub fn new(p: &'z mut Parser<'b, 'c>) -> Self {
Validator {
p,
c: ChildGraph::with_capacity(5),
}
}
pub fn validate(
&mut self,
needs_val_of: ParseResult,
is_subcmd: bool,
matcher: &mut ArgMatcher,
) -> ClapResult<()> {
debugln!("Validator::validate;");
let mut reqs_validated = false;
self.p.add_env(matcher)?;
self.p.add_defaults(matcher)?;
if let ParseResult::Opt(a) = needs_val_of {
debugln!("Validator::validate: needs_val_of={:?}", a);
self.validate_required(matcher)?;
let o = self.p.app.find(a).expect(INTERNAL_ERROR_MSG);
reqs_validated = true;
let should_err = if let Some(v) = matcher.0.args.get(&o.id) {
v.vals.is_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(
o,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
}
if matcher.is_empty()
&& matcher.subcommand_name().is_none()
&& self.p.is_set(AS::ArgRequiredElseHelp)
{
let mut out = vec![];
self.p.write_help_err(&mut out)?;
return Err(Error {
cause: String::new(),
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
self.validate_conflicts(matcher)?;
if !(self.p.is_set(AS::SubcommandsNegateReqs) && is_subcmd || reqs_validated) {
self.validate_required(matcher)?;
self.validate_required_unless(matcher)?;
}
self.validate_matched_args(matcher)?;
Ok(())
}
fn validate_arg_values(
&self,
arg: &Arg,
ma: &MatchedArg,
matcher: &ArgMatcher,
) -> ClapResult<()> {
debugln!("Validator::validate_arg_values: arg={:?}", arg.name);
for val in &ma.vals {
if self.p.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debugln!(
"Validator::validate_arg_values: invalid UTF-8 found in val {:?}",
val
);
return Err(Error::invalid_utf8(
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
if let Some(ref p_vals) = arg.possible_vals {
debugln!("Validator::validate_arg_values: possible_vals={:?}", p_vals);
let val_str = val.to_string_lossy();
let ok = if arg.is_set(ArgSettings::IgnoreCase) {
p_vals.iter().any(|pv| pv.eq_ignore_ascii_case(&*val_str))
} else {
p_vals.contains(&&*val_str)
};
if !ok {
let used: Vec<Id> = matcher
.arg_names()
.filter(|&&n| {
if let Some(a) = self.p.app.find(n) {
!(self.p.required.contains(a.id) || a.is_set(ArgSettings::Hidden))
} else {
true
}
})
.cloned()
.collect();
return Err(Error::invalid_value(
val_str,
p_vals,
arg,
&*Usage::new(self.p).create_usage_with_title(&*used),
self.p.app.color(),
));
}
}
if !arg.is_set(ArgSettings::AllowEmptyValues)
&& val.is_empty()
&& matcher.contains(arg.id)
{
debugln!("Validator::validate_arg_values: illegal empty val found");
return Err(Error::empty_value(
arg,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
if let Some(ref vtor) = arg.validator {
debug!("Validator::validate_arg_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.p.app.color()));
} else {
sdebugln!("good");
}
}
if let Some(ref vtor) = arg.validator_os {
debug!("Validator::validate_arg_values: checking validator_os...");
if let Err(e) = vtor(val) {
sdebugln!("error");
return Err(Error::value_validation(
Some(arg),
&(*e).to_string(),
self.p.app.color(),
));
} else {
sdebugln!("good");
}
}
}
Ok(())
}
fn build_conflict_err(&self, name: Id, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!("build_err!: name={}", name);
let usg = Usage::new(self.p).create_usage_with_title(&[]);
if let Some(other_arg) = self.p.app.find(name) {
for &k in matcher.arg_names() {
if let Some(a) = self.p.app.find(k) {
if let Some(ref v) = a.blacklist {
if v.contains(&name) {
return Err(Error::argument_conflict(
a,
Some(other_arg.to_string()),
&*usg,
self.p.app.color(),
));
}
}
}
}
} else if let Some(g) = self.p.app.groups.iter().find(|x| x.id == name) {
let args_in_group = self.p.app.unroll_args_in_group(g.id);
let first = matcher
.arg_names()
.find(|x| args_in_group.contains(x))
.expect(INTERNAL_ERROR_MSG);
let c_with = matcher
.arg_names()
.find(|x| x != &first && args_in_group.contains(x))
.map(|&x| self.p.app.find(x).expect(INTERNAL_ERROR_MSG).to_string());
debugln!("build_err!:c_with={:?}:group", c_with);
return Err(Error::argument_conflict(
self.p.app.find(*first).expect(INTERNAL_ERROR_MSG),
c_with,
&*usg,
self.p.app.color(),
));
}
panic!(INTERNAL_ERROR_MSG);
}
fn validate_conflicts(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_conflicts;");
let validate_result = self.validate_exclusive(matcher);
if validate_result.is_err() {
return validate_result;
}
self.gather_conflicts(matcher);
for name in self.c.iter() {
debugln!("Validator::validate_conflicts:iter:{};", name);
let mut should_err = false;
if let Some(g) = self
.p
.app
.groups
.iter()
.filter(|g| !g.multiple)
.find(|g| &g.id == name)
{
let conf_with_self = self
.p
.app
.unroll_args_in_group(g.id)
.iter()
.filter(|&&a| matcher.contains(a))
.count()
> 1;
let conf_with_arg = if let Some(ref c) = g.conflicts {
c.iter().any(|&x| matcher.contains(x))
} else {
false
};
let arg_conf_with_gr = matcher
.arg_names()
.filter_map(|&x| self.p.app.find(x))
.filter_map(|x| x.blacklist.as_ref())
.any(|c| c.iter().any(|&c| c == g.id));
should_err = conf_with_self || conf_with_arg || arg_conf_with_gr;
} else if let Some(ma) = matcher.get(*name) {
debugln!(
"Validator::validate_conflicts:iter:{}: matcher contains it...",
name
);
should_err = ma.occurs > 0;
}
if should_err {
return self.build_conflict_err(*name, matcher);
}
}
Ok(())
}
fn validate_exclusive(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_exclusive;");
let args_count = matcher.arg_names().count();
for &name in matcher.arg_names() {
debugln!(
"Validator::validate_conflicts_with_everything:iter:{};",
name
);
if let Some(arg) = self.p.app.find(name) {
if arg.exclusive && args_count > 1 {
let c_with: Option<String> = None;
let err = Err(Error::argument_conflict(
arg,
c_with,
Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
debugln!(
"Validator::validate_conflicts_with_everything; ERROR: {:?}",
err
);
return err;
}
}
}
Ok(())
}
fn gather_conflicts(&mut self, matcher: &mut ArgMatcher) {
debugln!("Validator::gather_conflicts;");
for &name in matcher.arg_names() {
debugln!("Validator::gather_conflicts:iter:{};", name);
if let Some(arg) = self.p.app.find(name) {
if let Some(ref bl) = arg.blacklist {
for &conf in bl {
if self.p.app.find(conf).is_some() {
if conf != name {
self.c.insert(conf);
}
} else {
self.c.insert(conf); }
}
}
for grp in groups_for_arg!(self.p.app, name) {
if let Some(g) = self
.p
.app
.groups
.iter()
.filter(|g| !g.multiple)
.find(|g| g.id == grp)
{
self.c.insert(g.id);
}
}
} else if let Some(g) = self
.p
.app
.groups
.iter()
.filter(|g| !g.multiple)
.find(|grp| grp.id == name)
{
debugln!("Validator::gather_conflicts:iter:{}:group;", name);
self.c.insert(g.id);
}
}
}
fn gather_requirements(&mut self, matcher: &ArgMatcher) {
debugln!("Validator::gather_requirements;");
for &name in matcher.arg_names() {
debugln!("Validator::gather_requirements:iter:{};", name);
if let Some(arg) = self.p.app.find(name) {
for req in self.p.app.unroll_requirements_for_arg(arg.id, matcher) {
self.p.required.insert(req);
}
} else if let Some(g) = self.p.app.groups.iter().find(|grp| grp.id == name) {
debugln!("Validator::gather_conflicts:iter:{}:group;", name);
if let Some(ref reqs) = g.requires {
for &r in reqs {
self.p.required.insert(r);
}
}
}
}
}
fn validate_matched_args(&self, matcher: &mut ArgMatcher) -> 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(arg) = self.p.app.find(name) {
self.validate_arg_num_vals(arg, ma)?;
self.validate_arg_values(arg, ma, matcher)?;
self.validate_arg_requires(arg, ma, matcher)?;
self.validate_arg_num_occurs(arg, ma)?;
} else {
let grp = self
.p
.app
.groups
.iter()
.find(|g| g.id == 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, Some(name));
}
}
}
}
Ok(())
}
fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> {
debugln!(
"Validator::validate_arg_num_occurs: {}={};",
a.name,
ma.occurs
);
if ma.occurs > 1 && !a.is_set(ArgSettings::MultipleOccurrences) {
return Err(Error::unexpected_multiple_usage(
a,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
Ok(())
}
fn validate_arg_num_vals(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> {
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::MultipleValues) {
((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::MultipleValues) {
ma.vals.len() % num as usize
} else {
ma.vals.len()
},
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.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,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
}
let min_vals_zero = if let Some(num) = a.min_vals {
debugln!("Validator::validate_arg_num_vals: min_vals set: {}", num);
if (ma.vals.len() as u64) < num && num != 0 {
debugln!("Validator::validate_arg_num_vals: Sending error TooFewValues");
return Err(Error::too_few_values(
a,
num,
ma.vals.len(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
num == 0
} else {
false
};
if a.is_set(ArgSettings::TakesValue) && !min_vals_zero && ma.vals.is_empty() {
return Err(Error::empty_value(
a,
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
Ok(())
}
fn validate_arg_requires(
&self,
a: &Arg<'b>,
ma: &MatchedArg,
matcher: &ArgMatcher,
) -> ClapResult<()> {
debugln!("Validator::validate_arg_requires:{};", a.name);
if let Some(ref a_reqs) = a.requires {
for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) {
let missing_req =
|v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name);
if ma.vals.iter().any(missing_req) {
return self.missing_required_error(matcher, Some(a.id));
}
}
for &(_, name) in a_reqs.iter().filter(|&&(val, _)| val.is_none()) {
if !matcher.contains(name) {
return self.missing_required_error(matcher, Some(name));
}
}
}
Ok(())
}
fn validate_required(&mut self, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!(
"Validator::validate_required: required={:?};",
self.p.required
);
self.gather_requirements(matcher);
for &arg_or_group in self.p.required.iter().filter(|&&r| !matcher.contains(r)) {
debugln!("Validator::validate_required:iter:aog={:?};", arg_or_group);
if let Some(arg) = self.p.app.find(arg_or_group) {
if !self.is_missing_required_ok(arg, matcher) {
return self.missing_required_error(matcher, None);
}
} else if let Some(group) = self.p.app.groups.iter().find(|g| g.id == arg_or_group) {
if !self
.p
.app
.unroll_args_in_group(group.id)
.iter()
.any(|&a| matcher.contains(a))
{
return self.missing_required_error(matcher, None);
}
}
}
for (a, r_ifs) in self
.p
.app
.args
.args
.iter()
.filter(|a| a.r_ifs.is_some())
.map(|a| (a, a.r_ifs.as_ref().unwrap()))
{
for (other, val) in r_ifs.iter() {
if let Some(ma) = matcher.get(*other) {
if ma.contains_val(val) && !matcher.contains(a.id) {
return self.missing_required_error(matcher, Some(a.id));
}
}
}
}
Ok(())
}
fn is_missing_required_ok(&self, a: &Arg<'b>, matcher: &ArgMatcher) -> bool {
debugln!("Validator::is_missing_required_ok: {}", a.name);
self.validate_arg_conflicts(a, matcher) || self.p.overriden.contains(&a.id)
}
fn validate_arg_conflicts(&self, a: &Arg<'b>, matcher: &ArgMatcher) -> bool {
debugln!("Validator::validate_arg_conflicts: a={:?};", a.name);
a.blacklist
.as_ref()
.map(|bl| {
bl.iter().any(|&conf| {
matcher.contains(conf)
|| self
.p
.app
.groups
.iter()
.find(|g| g.id == conf)
.map_or(false, |g| g.args.iter().any(|&arg| matcher.contains(arg)))
})
})
.unwrap_or(false)
}
fn validate_required_unless(&self, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_required_unless;");
for a in self
.p
.app
.args
.args
.iter()
.filter(|a| a.r_unless.is_some())
.filter(|a| !matcher.contains(a.id))
{
debugln!("Validator::validate_required_unless:iter:{};", a.name);
if self.fails_arg_required_unless(a, matcher) {
return self.missing_required_error(matcher, Some(a.id));
}
}
Ok(())
}
fn fails_arg_required_unless(&self, a: &Arg<'b>, matcher: &ArgMatcher) -> bool {
debugln!("Validator::fails_arg_required_unless: a={:?};", a.name);
macro_rules! check {
($how:ident, $_self:expr, $a:ident, $m:ident) => {{
$a.r_unless
.as_ref()
.map(|ru| !ru.iter().$how(|&n| $m.contains(n)))
.unwrap_or(false)
}};
}
if a.is_set(ArgSettings::RequiredUnlessAll) {
debugln!("Validator::fails_arg_required_unless:{}:All;", a.name);
check!(all, self.p, a, matcher)
} else {
debugln!("Validator::fails_arg_required_unless:{}:Any;", a.name);
check!(any, self.p, a, matcher)
}
}
fn missing_required_error(&self, matcher: &ArgMatcher, incl: Option<Id>) -> ClapResult<()> {
debugln!("Validator::missing_required_error; incl={:?}", incl);
let c = Colorizer::new(&ColorizerOption {
use_stderr: true,
when: self.p.app.color(),
});
debugln!(
"Validator::missing_required_error: reqs={:?}",
self.p.required
);
let usg = Usage::new(self.p);
let req_args = if let Some(x) = incl {
usg.get_required_usage_from(&[x], Some(matcher), true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
})
} else {
usg.get_required_usage_from(&[], None, true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
})
};
debugln!(
"Validator::missing_required_error: req_args={:#?}",
req_args
);
let used: Vec<Id> = matcher
.arg_names()
.filter(|&&n| {
if let Some(a) = self.p.app.find(n) {
!(self.p.required.contains(a.id) || a.is_set(ArgSettings::Hidden))
} else {
true
}
})
.cloned()
.chain(incl)
.collect();
Err(Error::missing_required_argument(
&*req_args,
&*usg.create_usage_with_title(&*used),
self.p.app.color(),
))
}
}