aboutsummaryrefslogtreecommitdiff
path: root/clap/src/app/validator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'clap/src/app/validator.rs')
-rw-r--r--clap/src/app/validator.rs573
1 files changed, 573 insertions, 0 deletions
diff --git a/clap/src/app/validator.rs b/clap/src/app/validator.rs
new file mode 100644
index 0000000..181b831
--- /dev/null
+++ b/clap/src/app/validator.rs
@@ -0,0 +1,573 @@
+// std
+use std::fmt::Display;
+#[allow(deprecated, unused_imports)]
+use std::ascii::AsciiExt;
+
+// 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 app::settings::AppSettings as AS;
+use app::parser::{ParseResult, Parser};
+use fmt::{Colorizer, ColorizerOption};
+use app::usage;
+
+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: ParseResult<'a>,
+ subcmd_name: Option<String>,
+ matcher: &mut ArgMatcher<'a>,
+ ) -> ClapResult<()> {
+ debugln!("Validator::validate;");
+ let mut reqs_validated = false;
+ self.0.add_env(matcher)?;
+ self.0.add_defaults(matcher)?;
+ if let ParseResult::Opt(a) = needs_val_of {
+ debugln!("Validator::validate: needs_val_of={:?}", a);
+ let o = {
+ self.0
+ .opts
+ .iter()
+ .find(|o| o.b.name == a)
+ .expect(INTERNAL_ERROR_MSG)
+ .clone()
+ };
+ 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,
+ &*usage::create_error_usage(self.0, matcher, None),
+ self.0.color(),
+ ));
+ }
+ }
+
+ if matcher.is_empty() && matcher.subcommand_name().is_none()
+ && self.0.is_set(AS::ArgRequiredElseHelp)
+ {
+ let mut out = vec![];
+ self.0.write_help_err(&mut out)?;
+ return Err(Error {
+ message: String::from_utf8_lossy(&*out).into_owned(),
+ kind: ErrorKind::MissingArgumentOrSubcommand,
+ info: None,
+ });
+ }
+ self.validate_blacklist(matcher)?;
+ if !(self.0.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated {
+ self.validate_required(matcher)?;
+ }
+ self.validate_matched_args(matcher)?;
+ matcher.usage(usage::create_usage_with_title(self.0, &[]));
+
+ Ok(())
+ }
+
+ fn validate_arg_values<A>(
+ &self,
+ arg: &A,
+ ma: &MatchedArg,
+ matcher: &ArgMatcher<'a>,
+ ) -> ClapResult<()>
+ where
+ A: AnyArg<'a, 'b> + Display,
+ {
+ debugln!("Validator::validate_arg_values: arg={:?}", arg.name());
+ for val in &ma.vals {
+ if self.0.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::create_error_usage(self.0, matcher, None),
+ self.0.color(),
+ ));
+ }
+ if let Some(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::CaseInsensitive) {
+ p_vals.iter().any(|pv| pv.eq_ignore_ascii_case(&*val_str))
+ } else {
+ p_vals.contains(&&*val_str)
+ };
+ if !ok {
+ return Err(Error::invalid_value(
+ val_str,
+ p_vals,
+ arg,
+ &*usage::create_error_usage(self.0, matcher, None),
+ self.0.color(),
+ ));
+ }
+ }
+ if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty()
+ && matcher.contains(&*arg.name())
+ {
+ debugln!("Validator::validate_arg_values: illegal empty val found");
+ return Err(Error::empty_value(
+ arg,
+ &*usage::create_error_usage(self.0, matcher, None),
+ self.0.color(),
+ ));
+ }
+ if let Some(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.0.color()));
+ } else {
+ sdebugln!("good");
+ }
+ }
+ if let Some(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_lossy().to_string(),
+ self.0.color(),
+ ));
+ } else {
+ sdebugln!("good");
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn build_err(&self, name: &str, matcher: &ArgMatcher) -> ClapResult<()> {
+ debugln!("build_err!: name={}", name);
+ let mut c_with = find_from!(self.0, &name, blacklist, matcher);
+ c_with = c_with.or(
+ self.0.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| self.0.find_any_arg(an))
+ .map_or(None, |aa| Some(format!("{}", aa)))
+ );
+ debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, &name);
+// matcher.remove(&name);
+ let usg = usage::create_error_usage(self.0, matcher, None);
+ if let Some(f) = find_by_name!(self.0, name, flags, iter) {
+ debugln!("build_err!: It was a flag...");
+ Err(Error::argument_conflict(f, c_with, &*usg, self.0.color()))
+ } else if let Some(o) = find_by_name!(self.0, name, opts, iter) {
+ debugln!("build_err!: It was an option...");
+ Err(Error::argument_conflict(o, c_with, &*usg, self.0.color()))
+ } else {
+ match find_by_name!(self.0, name, positionals, values) {
+ Some(p) => {
+ debugln!("build_err!: It was a positional...");
+ Err(Error::argument_conflict(p, c_with, &*usg, self.0.color()))
+ },
+ None => panic!(INTERNAL_ERROR_MSG)
+ }
+ }
+ }
+
+ fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
+ debugln!("Validator::validate_blacklist;");
+ let mut conflicts: Vec<&str> = vec![];
+ for (&name, _) in matcher.iter() {
+ debugln!("Validator::validate_blacklist:iter:{};", name);
+ if let Some(grps) = self.0.groups_for_arg(name) {
+ for grp in &grps {
+ if let Some(g) = self.0.groups.iter().find(|g| &g.name == grp) {
+ if !g.multiple {
+ for arg in &g.args {
+ if arg == &name {
+ continue;
+ }
+ conflicts.push(arg);
+ }
+ }
+ if let Some(ref gc) = g.conflicts {
+ conflicts.extend(&*gc);
+ }
+ }
+ }
+ }
+ if let Some(arg) = find_any_by_name!(self.0, name) {
+ if let Some(bl) = arg.blacklist() {
+ for conf in bl {
+ if matcher.get(conf).is_some() {
+ conflicts.push(conf);
+ }
+ }
+ }
+ } else {
+ debugln!("Validator::validate_blacklist:iter:{}:group;", name);
+ let args = self.0.arg_names_in_group(name);
+ for arg in &args {
+ debugln!("Validator::validate_blacklist:iter:{}:group:iter:{};", name, arg);
+ if let Some(bl) = find_any_by_name!(self.0, *arg).unwrap().blacklist() {
+ for conf in bl {
+ if matcher.get(conf).is_some() {
+ conflicts.push(conf);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for name in &conflicts {
+ debugln!(
+ "Validator::validate_blacklist:iter:{}: Checking blacklisted arg",
+ name
+ );
+ let mut should_err = false;
+ if self.0.groups.iter().any(|g| &g.name == name) {
+ debugln!(
+ "Validator::validate_blacklist:iter:{}: groups contains it...",
+ name
+ );
+ for n in self.0.arg_names_in_group(name) {
+ debugln!(
+ "Validator::validate_blacklist:iter:{}:iter:{}: looking in group...",
+ name,
+ n
+ );
+ if matcher.contains(n) {
+ debugln!(
+ "Validator::validate_blacklist:iter:{}:iter:{}: matcher contains it...",
+ name,
+ n
+ );
+ return self.build_err(n, matcher);
+ }
+ }
+ } else if let Some(ma) = matcher.get(name) {
+ debugln!(
+ "Validator::validate_blacklist:iter:{}: matcher contains it...",
+ name
+ );
+ should_err = ma.occurs > 0;
+ }
+ if should_err {
+ return self.build_err(*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) {
+ self.validate_arg_num_vals(opt, ma, matcher)?;
+ self.validate_arg_values(opt, ma, matcher)?;
+ self.validate_arg_requires(opt, ma, matcher)?;
+ self.validate_arg_num_occurs(opt, ma, matcher)?;
+ } else if let Some(flag) = find_by_name!(self.0, *name, flags, iter) {
+ self.validate_arg_requires(flag, ma, matcher)?;
+ self.validate_arg_num_occurs(flag, ma, matcher)?;
+ } else if let Some(pos) = find_by_name!(self.0, *name, positionals, values) {
+ self.validate_arg_num_vals(pos, ma, matcher)?;
+ self.validate_arg_num_occurs(pos, ma, matcher)?;
+ self.validate_arg_values(pos, ma, matcher)?;
+ 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,
+ &*usage::create_error_usage(self.0, 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:{}", a.name());
+ 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"
+ },
+ &*usage::create_error_usage(self.0, 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,
+ &*usage::create_error_usage(self.0, matcher, None),
+ self.0.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::create_error_usage(self.0, matcher, None),
+ self.0.color(),
+ ));
+ }
+ num == 0
+ } else {
+ false
+ };
+ // Issue 665 (https://github.com/clap-rs/clap/issues/665)
+ // Issue 1105 (https://github.com/clap-rs/clap/issues/1105)
+ if a.takes_value() && !min_vals_zero && ma.vals.is_empty() {
+ return Err(Error::empty_value(
+ a,
+ &*usage::create_error_usage(self.0, 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:{};", a.name());
+ if let Some(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, None);
+ }
+ }
+ 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.0.required
+ );
+
+ let mut should_err = false;
+ let mut to_rem = Vec::new();
+ for name in &self.0.required {
+ debugln!("Validator::validate_required:iter:{}:", name);
+ if matcher.contains(name) {
+ continue;
+ }
+ if to_rem.contains(name) {
+ continue;
+ } else if let Some(a) = find_any_by_name!(self.0, *name) {
+ if self.is_missing_required_ok(a, matcher) {
+ to_rem.push(a.name());
+ if let Some(reqs) = a.requires() {
+ for r in reqs
+ .iter()
+ .filter(|&&(val, _)| val.is_none())
+ .map(|&(_, name)| name)
+ {
+ to_rem.push(r);
+ }
+ }
+ continue;
+ }
+ }
+ should_err = true;
+ break;
+ }
+ if should_err {
+ for r in &to_rem {
+ 'inner: for i in (0 .. self.0.required.len()).rev() {
+ if &self.0.required[i] == r {
+ self.0.required.swap_remove(i);
+ break 'inner;
+ }
+ }
+ }
+ 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() && ma.vals.iter().any(|val| val == v) {
+ return self.missing_required_error(matcher, Some(r));
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn validate_arg_conflicts(&self, a: &AnyArg, matcher: &ArgMatcher) -> Option<bool> {
+ debugln!("Validator::validate_arg_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(&self, a: &AnyArg, matcher: &ArgMatcher) -> Option<bool> {
+ 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)
+ }
+ }
+
+ fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> {
+ debugln!("Validator::missing_required_error: extra={:?}", extra);
+ let c = Colorizer::new(ColorizerOption {
+ 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);
+ let req_args =
+ usage::get_required_usage_from(self.0, &reqs[..], Some(matcher), extra, true)
+ .iter()
+ .fold(String::new(), |acc, s| {
+ acc + &format!("\n {}", c.error(s))[..]
+ });
+ debugln!(
+ "Validator::missing_required_error: req_args={:#?}",
+ req_args
+ );
+ Err(Error::missing_required_argument(
+ &*req_args,
+ &*usage::create_error_usage(self.0, matcher, extra),
+ self.0.color(),
+ ))
+ }
+
+ #[inline]
+ fn is_missing_required_ok(&self, a: &AnyArg, matcher: &ArgMatcher) -> bool {
+ debugln!("Validator::is_missing_required_ok: a={}", a.name());
+ self.validate_arg_conflicts(a, matcher).unwrap_or(false)
+ || self.validate_required_unless(a, matcher).unwrap_or(false)
+ }
+}