From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/clap/src/parser/arg_matcher.rs | 282 ++++ vendor/clap/src/parser/error.rs | 56 + vendor/clap/src/parser/features/mod.rs | 1 + vendor/clap/src/parser/features/suggestions.rs | 105 ++ vendor/clap/src/parser/matches/any_value.rs | 112 ++ vendor/clap/src/parser/matches/arg_matches.rs | 1828 ++++++++++++++++++++++++ vendor/clap/src/parser/matches/matched_arg.rs | 240 ++++ vendor/clap/src/parser/matches/mod.rs | 17 + vendor/clap/src/parser/matches/value_source.rs | 11 + vendor/clap/src/parser/mod.rs | 27 + vendor/clap/src/parser/parser.rs | 1727 ++++++++++++++++++++++ vendor/clap/src/parser/validator.rs | 692 +++++++++ 12 files changed, 5098 insertions(+) create mode 100644 vendor/clap/src/parser/arg_matcher.rs create mode 100644 vendor/clap/src/parser/error.rs create mode 100644 vendor/clap/src/parser/features/mod.rs create mode 100644 vendor/clap/src/parser/features/suggestions.rs create mode 100644 vendor/clap/src/parser/matches/any_value.rs create mode 100644 vendor/clap/src/parser/matches/arg_matches.rs create mode 100644 vendor/clap/src/parser/matches/matched_arg.rs create mode 100644 vendor/clap/src/parser/matches/mod.rs create mode 100644 vendor/clap/src/parser/matches/value_source.rs create mode 100644 vendor/clap/src/parser/mod.rs create mode 100644 vendor/clap/src/parser/parser.rs create mode 100644 vendor/clap/src/parser/validator.rs (limited to 'vendor/clap/src/parser') diff --git a/vendor/clap/src/parser/arg_matcher.rs b/vendor/clap/src/parser/arg_matcher.rs new file mode 100644 index 000000000..8d15c5799 --- /dev/null +++ b/vendor/clap/src/parser/arg_matcher.rs @@ -0,0 +1,282 @@ +// Std +use std::collections::HashMap; +use std::ffi::OsString; +use std::mem; +use std::ops::Deref; + +// Internal +use crate::builder::{Arg, ArgPredicate, Command}; +use crate::parser::AnyValue; +use crate::parser::Identifier; +use crate::parser::PendingArg; +use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource}; +use crate::util::Id; +use crate::INTERNAL_ERROR_MSG; + +#[derive(Debug, Default)] +pub(crate) struct ArgMatcher { + matches: ArgMatches, + pending: Option, +} + +impl ArgMatcher { + pub(crate) fn new(_cmd: &Command) -> Self { + ArgMatcher { + matches: ArgMatches { + #[cfg(debug_assertions)] + valid_args: { + let args = _cmd.get_arguments().map(|a| a.id.clone()); + let groups = _cmd.get_groups().map(|g| g.id.clone()); + args.chain(groups).collect() + }, + #[cfg(debug_assertions)] + valid_subcommands: _cmd.get_subcommands().map(|sc| sc.get_id()).collect(), + // HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches + // since users can't detect it and avoid the asserts. + // + // See clap-rs/clap#3263 + #[cfg(debug_assertions)] + #[cfg(not(feature = "unstable-v4"))] + disable_asserts: _cmd.is_allow_external_subcommands_set(), + #[cfg(debug_assertions)] + #[cfg(feature = "unstable-v4")] + disable_asserts: false, + ..Default::default() + }, + pending: None, + } + } + + pub(crate) fn into_inner(self) -> ArgMatches { + self.matches + } + + pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) { + debug!( + "ArgMatcher::get_global_values: global_arg_vec={:?}", + global_arg_vec + ); + let mut vals_map = HashMap::new(); + self.fill_in_global_values(global_arg_vec, &mut vals_map); + } + + fn fill_in_global_values( + &mut self, + global_arg_vec: &[Id], + vals_map: &mut HashMap, + ) { + for global_arg in global_arg_vec { + if let Some(ma) = self.get(global_arg) { + // We have to check if the parent's global arg wasn't used but still exists + // such as from a default value. + // + // For example, `myprog subcommand --global-arg=value` where `--global-arg` defines + // a default value of `other` myprog would have an existing MatchedArg for + // `--global-arg` where the value is `other` + let to_update = if let Some(parent_ma) = vals_map.get(global_arg) { + if parent_ma.check_explicit(ArgPredicate::IsPresent) + && !ma.check_explicit(ArgPredicate::IsPresent) + { + parent_ma + } else { + ma + } + } else { + ma + } + .clone(); + vals_map.insert(global_arg.clone(), to_update); + } + } + if let Some(ref mut sc) = self.matches.subcommand { + let mut am = ArgMatcher { + matches: mem::take(&mut sc.matches), + pending: None, + }; + am.fill_in_global_values(global_arg_vec, vals_map); + mem::swap(&mut am.matches, &mut sc.matches); + } + + for (name, matched_arg) in vals_map.iter_mut() { + self.matches.args.insert(name.clone(), matched_arg.clone()); + } + } + + pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> { + self.matches.args.get(arg) + } + + pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> { + self.matches.args.get_mut(arg) + } + + pub(crate) fn remove(&mut self, arg: &Id) { + self.matches.args.swap_remove(arg); + } + + pub(crate) fn contains(&self, arg: &Id) -> bool { + self.matches.args.contains_key(arg) + } + + pub(crate) fn arg_ids(&self) -> indexmap::map::Keys { + self.matches.args.keys() + } + + pub(crate) fn entry(&mut self, arg: &Id) -> indexmap::map::Entry { + self.matches.args.entry(arg.clone()) + } + + pub(crate) fn subcommand(&mut self, sc: SubCommand) { + self.matches.subcommand = Some(Box::new(sc)); + } + + pub(crate) fn subcommand_name(&self) -> Option<&str> { + self.matches.subcommand_name() + } + + pub(crate) fn iter(&self) -> indexmap::map::Iter { + self.matches.args.iter() + } + + pub(crate) fn check_explicit<'a>(&self, arg: &Id, predicate: ArgPredicate<'a>) -> bool { + self.get(arg).map_or(false, |a| a.check_explicit(predicate)) + } + + pub(crate) fn start_custom_arg(&mut self, arg: &Arg, source: ValueSource) { + let id = &arg.id; + debug!( + "ArgMatcher::start_custom_arg: id={:?}, source={:?}", + id, source + ); + let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg)); + debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id())); + ma.set_source(source); + ma.new_val_group(); + } + + pub(crate) fn start_custom_group(&mut self, id: &Id, source: ValueSource) { + debug!( + "ArgMatcher::start_custom_arg: id={:?}, source={:?}", + id, source + ); + let ma = self.entry(id).or_insert(MatchedArg::new_group()); + debug_assert_eq!(ma.type_id(), None); + ma.set_source(source); + ma.new_val_group(); + } + + pub(crate) fn start_occurrence_of_arg(&mut self, arg: &Arg) { + let id = &arg.id; + debug!("ArgMatcher::start_occurrence_of_arg: id={:?}", id); + let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg)); + debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id())); + ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] + ma.inc_occurrences(); + ma.new_val_group(); + } + + pub(crate) fn start_occurrence_of_group(&mut self, id: &Id) { + debug!("ArgMatcher::start_occurrence_of_group: id={:?}", id); + let ma = self.entry(id).or_insert(MatchedArg::new_group()); + debug_assert_eq!(ma.type_id(), None); + ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] + ma.inc_occurrences(); + ma.new_val_group(); + } + + pub(crate) fn start_occurrence_of_external(&mut self, cmd: &crate::Command) { + let id = &Id::empty_hash(); + debug!("ArgMatcher::start_occurrence_of_external: id={:?}", id,); + let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd)); + debug_assert_eq!( + ma.type_id(), + Some( + cmd.get_external_subcommand_value_parser() + .expect(INTERNAL_ERROR_MSG) + .type_id() + ) + ); + ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] + ma.inc_occurrences(); + ma.new_val_group(); + } + + pub(crate) fn add_val_to(&mut self, arg: &Id, val: AnyValue, raw_val: OsString) { + let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG); + ma.append_val(val, raw_val); + } + + pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) { + let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG); + ma.push_index(idx); + } + + pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool { + let num_resolved = self.get(&o.id).map(|ma| ma.num_vals()).unwrap_or(0); + let num_pending = self + .pending + .as_ref() + .and_then(|p| (p.id == o.id).then(|| p.raw_vals.len())) + .unwrap_or(0); + let current_num = num_resolved + num_pending; + debug!( + "ArgMatcher::needs_more_vals: o={}, resolved={}, pending={}", + o.name, num_resolved, num_pending + ); + if current_num == 0 { + true + } else if let Some(num) = o.num_vals { + debug!("ArgMatcher::needs_more_vals: num_vals...{}", num); + #[allow(deprecated)] + if o.is_multiple_occurrences_set() { + (current_num % num) != 0 + } else { + num != current_num + } + } else if let Some(num) = o.max_vals { + debug!("ArgMatcher::needs_more_vals: max_vals...{}", num); + current_num < num + } else if o.min_vals.is_some() { + debug!("ArgMatcher::needs_more_vals: min_vals...true"); + true + } else { + o.is_multiple_values_set() + } + } + + pub(crate) fn pending_arg_id(&self) -> Option<&Id> { + self.pending.as_ref().map(|p| &p.id) + } + + pub(crate) fn pending_values_mut( + &mut self, + id: &Id, + ident: Option, + ) -> &mut Vec { + let pending = self.pending.get_or_insert_with(|| PendingArg { + id: id.clone(), + ident, + raw_vals: Default::default(), + }); + debug_assert_eq!(pending.id, *id, "{}", INTERNAL_ERROR_MSG); + if ident.is_some() { + debug_assert_eq!(pending.ident, ident, "{}", INTERNAL_ERROR_MSG); + } + &mut pending.raw_vals + } + + pub(crate) fn take_pending(&mut self) -> Option { + self.pending.take() + } +} + +impl Deref for ArgMatcher { + type Target = ArgMatches; + + fn deref(&self) -> &Self::Target { + &self.matches + } +} diff --git a/vendor/clap/src/parser/error.rs b/vendor/clap/src/parser/error.rs new file mode 100644 index 000000000..bdafa9ae5 --- /dev/null +++ b/vendor/clap/src/parser/error.rs @@ -0,0 +1,56 @@ +use crate::util::Id; + +/// Violation of [`ArgMatches`][crate::ArgMatches] assumptions +#[derive(Clone, Debug)] +#[allow(missing_copy_implementations)] // We might add non-Copy types in the future +#[non_exhaustive] +pub enum MatchesError { + /// Failed to downcast `AnyValue` to the specified type + #[non_exhaustive] + Downcast { + /// Type for value stored in [`ArgMatches`][crate::ArgMatches] + actual: super::AnyValueId, + /// The target type to downcast to + expected: super::AnyValueId, + }, + /// Argument not defined in [`Command`][crate::Command] + #[non_exhaustive] + UnknownArgument { + // Missing `id` but blocked on a public id type which will hopefully come with `unstable-v4` + }, +} + +impl MatchesError { + #[track_caller] + pub(crate) fn unwrap(id: &Id, r: Result) -> T { + let err = match r { + Ok(t) => { + return t; + } + Err(err) => err, + }; + panic!( + "Mismatch between definition and access of `{:?}`. {}", + id, err + ) + } +} + +impl std::error::Error for MatchesError {} + +impl std::fmt::Display for MatchesError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Downcast { actual, expected } => { + writeln!( + f, + "Could not downcast to {:?}, need to downcast to {:?}", + expected, actual + ) + } + Self::UnknownArgument {} => { + writeln!(f, "Unknown argument or group id. Make sure you are using the argument id and not the short or long flags") + } + } + } +} diff --git a/vendor/clap/src/parser/features/mod.rs b/vendor/clap/src/parser/features/mod.rs new file mode 100644 index 000000000..bdeb766ec --- /dev/null +++ b/vendor/clap/src/parser/features/mod.rs @@ -0,0 +1 @@ +pub(crate) mod suggestions; diff --git a/vendor/clap/src/parser/features/suggestions.rs b/vendor/clap/src/parser/features/suggestions.rs new file mode 100644 index 000000000..9e46f3c9e --- /dev/null +++ b/vendor/clap/src/parser/features/suggestions.rs @@ -0,0 +1,105 @@ +#[cfg(feature = "suggestions")] +use std::cmp::Ordering; + +// Internal +use crate::builder::Command; + +/// Produces multiple strings from a given list of possible values which are similar +/// to the passed in value `v` within a certain confidence by least confidence. +/// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield +/// `Some("foo")`, whereas "blark" would yield `None`. +#[cfg(feature = "suggestions")] +pub(crate) fn did_you_mean(v: &str, possible_values: I) -> Vec +where + T: AsRef, + I: IntoIterator, +{ + let mut candidates: Vec<(f64, String)> = possible_values + .into_iter() + .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) + .filter(|(confidence, _)| *confidence > 0.8) + .collect(); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.into_iter().map(|(_, pv)| pv).collect() +} + +#[cfg(not(feature = "suggestions"))] +pub(crate) fn did_you_mean(_: &str, _: I) -> Vec +where + T: AsRef, + I: IntoIterator, +{ + Vec::new() +} + +/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase +pub(crate) fn did_you_mean_flag<'a, 'help, I, T>( + arg: &str, + remaining_args: &[&str], + longs: I, + subcommands: impl IntoIterator>, +) -> Option<(String, Option)> +where + 'help: 'a, + T: AsRef, + I: IntoIterator, +{ + use crate::mkeymap::KeyType; + + match did_you_mean(arg, longs).pop() { + Some(candidate) => Some((candidate, None)), + None => subcommands + .into_iter() + .filter_map(|subcommand| { + subcommand._build_self(); + + let longs = subcommand.get_keymap().keys().filter_map(|a| { + if let KeyType::Long(v) = a { + Some(v.to_string_lossy().into_owned()) + } else { + None + } + }); + + let subcommand_name = subcommand.get_name(); + + let candidate = did_you_mean(arg, longs).pop()?; + let score = remaining_args.iter().position(|x| *x == subcommand_name)?; + Some((score, (candidate, Some(subcommand_name.to_string())))) + }) + .min_by_key(|(x, _)| *x) + .map(|(_, suggestion)| suggestion), + } +} + +#[cfg(all(test, features = "suggestions"))] +mod test { + use super::*; + + #[test] + fn possible_values_match() { + let p_vals = ["test", "possible", "values"]; + assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test")); + } + + #[test] + fn possible_values_match() { + let p_vals = ["test", "temp"]; + assert_eq!(did_you_mean("te", p_vals.iter()), Some("test")); + } + + #[test] + fn possible_values_nomatch() { + let p_vals = ["test", "possible", "values"]; + assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none()); + } + + #[test] + fn flag() { + let p_vals = ["test", "possible", "values"]; + assert_eq!( + did_you_mean_flag("tst", p_vals.iter(), []), + Some(("test", None)) + ); + } +} diff --git a/vendor/clap/src/parser/matches/any_value.rs b/vendor/clap/src/parser/matches/any_value.rs new file mode 100644 index 000000000..a9277e75f --- /dev/null +++ b/vendor/clap/src/parser/matches/any_value.rs @@ -0,0 +1,112 @@ +#[derive(Clone)] +pub(crate) struct AnyValue { + inner: std::sync::Arc, + // While we can extract `TypeId` from `inner`, the debug repr is of a number, so let's track + // the type_name in debug builds. + id: AnyValueId, +} + +impl AnyValue { + pub(crate) fn new(inner: V) -> Self { + let id = AnyValueId::of::(); + let inner = std::sync::Arc::new(inner); + Self { inner, id } + } + + pub(crate) fn downcast_ref( + &self, + ) -> Option<&T> { + self.inner.downcast_ref::() + } + + pub(crate) fn downcast_into(self) -> Result { + let id = self.id; + let value = + std::sync::Arc::downcast::(self.inner).map_err(|inner| Self { inner, id })?; + let value = std::sync::Arc::try_unwrap(value).unwrap_or_else(|arc| (*arc).clone()); + Ok(value) + } + + pub(crate) fn type_id(&self) -> AnyValueId { + self.id + } +} + +impl std::fmt::Debug for AnyValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.debug_struct("AnyValue").field("inner", &self.id).finish() + } +} + +#[derive(Copy, Clone)] +pub struct AnyValueId { + type_id: std::any::TypeId, + #[cfg(debug_assertions)] + type_name: &'static str, +} + +impl AnyValueId { + pub(crate) fn of() -> Self { + Self { + type_id: std::any::TypeId::of::(), + #[cfg(debug_assertions)] + type_name: std::any::type_name::(), + } + } +} + +impl PartialEq for AnyValueId { + fn eq(&self, other: &Self) -> bool { + self.type_id == other.type_id + } +} + +impl Eq for AnyValueId {} + +impl PartialOrd for AnyValueId { + fn partial_cmp(&self, other: &Self) -> Option { + self.type_id.partial_cmp(&other.type_id) + } +} + +impl Ord for AnyValueId { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.type_id.cmp(&other.type_id) + } +} + +impl std::hash::Hash for AnyValueId { + fn hash(&self, state: &mut H) { + self.type_id.hash(state); + } +} + +impl std::fmt::Debug for AnyValueId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + #[cfg(not(debug_assertions))] + { + self.type_id.fmt(f) + } + #[cfg(debug_assertions)] + { + f.debug_struct(self.type_name).finish() + } + } +} + +impl<'a, A: ?Sized + 'static> From<&'a A> for AnyValueId { + fn from(_: &'a A) -> Self { + Self::of::() + } +} + +#[cfg(test)] +mod test { + #[test] + #[cfg(debug_assertions)] + fn debug_impl() { + use super::*; + + assert_eq!(format!("{:?}", AnyValue::new(5)), "AnyValue { inner: i32 }"); + } +} diff --git a/vendor/clap/src/parser/matches/arg_matches.rs b/vendor/clap/src/parser/matches/arg_matches.rs new file mode 100644 index 000000000..17fa63ca6 --- /dev/null +++ b/vendor/clap/src/parser/matches/arg_matches.rs @@ -0,0 +1,1828 @@ +// Std +use std::any::Any; +use std::borrow::Cow; +use std::ffi::{OsStr, OsString}; +use std::fmt::{Debug, Display}; +use std::iter::{Cloned, Flatten, Map}; +use std::slice::Iter; +use std::str::FromStr; + +// Third Party +use indexmap::IndexMap; + +// Internal +use crate::parser::AnyValue; +use crate::parser::AnyValueId; +use crate::parser::MatchedArg; +use crate::parser::MatchesError; +use crate::parser::ValueSource; +use crate::util::{Id, Key}; +use crate::Error; +use crate::INTERNAL_ERROR_MSG; + +/// Container for parse results. +/// +/// Used to get information about the arguments that were supplied to the program at runtime by +/// the user. New instances of this struct are obtained by using the [`Command::get_matches`] family of +/// methods. +/// +/// # Examples +/// +/// ```no_run +/// # use clap::{Command, Arg, ValueSource}; +/// let matches = Command::new("MyApp") +/// .arg(Arg::new("out") +/// .long("output") +/// .required(true) +/// .takes_value(true) +/// .default_value("-")) +/// .arg(Arg::new("cfg") +/// .short('c') +/// .takes_value(true)) +/// .get_matches(); // builds the instance of ArgMatches +/// +/// // to get information about the "cfg" argument we created, such as the value supplied we use +/// // various ArgMatches methods, such as [ArgMatches::get_one] +/// if let Some(c) = matches.get_one::("cfg") { +/// println!("Value for -c: {}", c); +/// } +/// +/// // The ArgMatches::get_one method returns an Option because the user may not have supplied +/// // that argument at runtime. But if we specified that the argument was "required" as we did +/// // with the "out" argument, we can safely unwrap because `clap` verifies that was actually +/// // used at runtime. +/// println!("Value for --output: {}", matches.get_one::("out").unwrap()); +/// +/// // You can check the presence of an argument's values +/// if matches.contains_id("out") { +/// // However, if you want to know where the value came from +/// if matches.value_source("out").expect("checked contains_id") == ValueSource::CommandLine { +/// println!("`out` set by user"); +/// } else { +/// println!("`out` is defaulted"); +/// } +/// } +/// ``` +/// [`Command::get_matches`]: crate::Command::get_matches() +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ArgMatches { + #[cfg(debug_assertions)] + pub(crate) valid_args: Vec, + #[cfg(debug_assertions)] + pub(crate) valid_subcommands: Vec, + #[cfg(debug_assertions)] + pub(crate) disable_asserts: bool, + pub(crate) args: IndexMap, + pub(crate) subcommand: Option>, +} + +/// # Arguments +impl ArgMatches { + /// Gets the value of a specific option or positional argument. + /// + /// i.e. an argument that [takes an additional value][crate::Arg::takes_value] at runtime. + /// + /// Returns an error if the wrong type was used. + /// + /// Returns `None` if the option wasn't present. + /// + /// *NOTE:* This will always return `Some(value)` if [`default_value`] has been set. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. + /// + /// # Panic + /// + /// If the argument definition and access mismatch. To handle this case programmatically, see + /// [`ArgMatches::try_get_one`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, value_parser}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("port") + /// .value_parser(value_parser!(usize)) + /// .takes_value(true) + /// .required(true)) + /// .get_matches_from(vec!["myapp", "2020"]); + /// + /// let port: usize = *m + /// .get_one("port") + /// .expect("`port`is required"); + /// assert_eq!(port, 2020); + /// ``` + /// [option]: crate::Arg::takes_value() + /// [positional]: crate::Arg::index() + /// [`default_value`]: crate::Arg::default_value() + #[track_caller] + pub fn get_one(&self, id: &str) -> Option<&T> { + let internal_id = Id::from(id); + MatchesError::unwrap(&internal_id, self.try_get_one(id)) + } + + /// Iterate over values of a specific option or positional argument. + /// + /// i.e. an argument that takes multiple values at runtime. + /// + /// Returns an error if the wrong type was used. + /// + /// Returns `None` if the option wasn't present. + /// + /// # Panic + /// + /// If the argument definition and access mismatch. To handle this case programmatically, see + /// [`ArgMatches::try_get_many`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, value_parser, ArgAction}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("ports") + /// .action(ArgAction::Append) + /// .value_parser(value_parser!(usize)) + /// .short('p') + /// .takes_value(true) + /// .required(true)) + /// .get_matches_from(vec![ + /// "myprog", "-p", "22", "-p", "80", "-p", "2020" + /// ]); + /// let vals: Vec = m.get_many("ports") + /// .expect("`port`is required") + /// .copied() + /// .collect(); + /// assert_eq!(vals, [22, 80, 2020]); + /// ``` + #[track_caller] + pub fn get_many( + &self, + id: &str, + ) -> Option> { + let internal_id = Id::from(id); + MatchesError::unwrap(&internal_id, self.try_get_many(id)) + } + + /// Iterate over the original argument values. + /// + /// An `OsStr` on Unix-like systems is any series of bytes, regardless of whether or not they + /// contain valid UTF-8. Since [`String`]s in Rust are guaranteed to be valid UTF-8, a valid + /// filename on a Unix system as an argument value may contain invalid UTF-8. + /// + /// Returns `None` if the option wasn't present. + /// + /// # Panic + /// + /// If the argument definition and access mismatch. To handle this case programmatically, see + /// [`ArgMatches::try_get_raw`]. + /// + /// # Examples + /// + #[cfg_attr(not(unix), doc = " ```ignore")] + #[cfg_attr(unix, doc = " ```")] + /// # use clap::{Command, arg, value_parser}; + /// # use std::ffi::{OsStr,OsString}; + /// # use std::os::unix::ffi::{OsStrExt,OsStringExt}; + /// use std::path::PathBuf; + /// + /// let m = Command::new("utf8") + /// .arg(arg!( ... "some arg").value_parser(value_parser!(PathBuf))) + /// .get_matches_from(vec![OsString::from("myprog"), + /// // "Hi" + /// OsString::from_vec(vec![b'H', b'i']), + /// // "{0xe9}!" + /// OsString::from_vec(vec![0xe9, b'!'])]); + /// + /// let mut itr = m.get_raw("arg") + /// .expect("`port`is required") + /// .into_iter(); + /// assert_eq!(itr.next(), Some(OsStr::new("Hi"))); + /// assert_eq!(itr.next(), Some(OsStr::from_bytes(&[0xe9, b'!']))); + /// assert_eq!(itr.next(), None); + /// ``` + /// [`Iterator`]: std::iter::Iterator + /// [`OsSt`]: std::ffi::OsStr + /// [values]: OsValues + /// [`String`]: std::string::String + #[track_caller] + pub fn get_raw(&self, id: &str) -> Option> { + let internal_id = Id::from(id); + MatchesError::unwrap(&internal_id, self.try_get_raw(id)) + } + + /// Returns the value of a specific option or positional argument. + /// + /// i.e. an argument that [takes an additional value][crate::Arg::takes_value] at runtime. + /// + /// Returns an error if the wrong type was used. No item will have been removed. + /// + /// Returns `None` if the option wasn't present. + /// + /// *NOTE:* This will always return `Some(value)` if [`default_value`] has been set. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. + /// + /// # Panic + /// + /// If the argument definition and access mismatch. To handle this case programmatically, see + /// [`ArgMatches::try_remove_one`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, value_parser}; + /// let mut m = Command::new("myprog") + /// .arg(Arg::new("file") + /// .required(true) + /// .takes_value(true)) + /// .get_matches_from(vec![ + /// "myprog", "file.txt", + /// ]); + /// let vals: String = m.remove_one("file") + /// .expect("`file`is required"); + /// assert_eq!(vals, "file.txt"); + /// ``` + /// [option]: crate::Arg::takes_value() + /// [positional]: crate::Arg::index() + /// [`default_value`]: crate::Arg::default_value() + #[track_caller] + pub fn remove_one(&mut self, id: &str) -> Option { + let internal_id = Id::from(id); + MatchesError::unwrap(&internal_id, self.try_remove_one(id)) + } + + /// Return values of a specific option or positional argument. + /// + /// i.e. an argument that takes multiple values at runtime. + /// + /// Returns an error if the wrong type was used. No item will have been removed. + /// + /// Returns `None` if the option wasn't present. + /// + /// # Panic + /// + /// If the argument definition and access mismatch. To handle this case programmatically, see + /// [`ArgMatches::try_remove_many`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, value_parser, ArgAction}; + /// let mut m = Command::new("myprog") + /// .arg(Arg::new("file") + /// .action(ArgAction::Append) + /// .multiple_values(true) + /// .required(true) + /// .takes_value(true)) + /// .get_matches_from(vec![ + /// "myprog", "file1.txt", "file2.txt", "file3.txt", "file4.txt", + /// ]); + /// let vals: Vec = m.remove_many("file") + /// .expect("`file`is required") + /// .collect(); + /// assert_eq!(vals, ["file1.txt", "file2.txt", "file3.txt", "file4.txt"]); + /// ``` + #[track_caller] + pub fn remove_many( + &mut self, + id: &str, + ) -> Option> { + let internal_id = Id::from(id); + MatchesError::unwrap(&internal_id, self.try_remove_many(id)) + } + + /// Check if values are present for the argument or group id + /// + /// *NOTE:* This will always return `true` if [`default_value`] has been set. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. + /// + /// # Panics + /// + /// If `id` is is not a valid argument or group name. To handle this case programmatically, see + /// [`ArgMatches::try_contains_id`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("debug") + /// .short('d')) + /// .get_matches_from(vec![ + /// "myprog", "-d" + /// ]); + /// + /// assert!(m.contains_id("debug")); + /// ``` + /// + /// [`default_value`]: crate::Arg::default_value() + pub fn contains_id(&self, id: &str) -> bool { + let internal_id = Id::from(id); + MatchesError::unwrap(&internal_id, self.try_contains_id(id)) + } + + /// Check if any args were present on the command line + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let mut cmd = Command::new("myapp") + /// .arg(Arg::new("output") + /// .takes_value(true)); + /// + /// let m = cmd + /// .try_get_matches_from_mut(vec!["myapp", "something"]) + /// .unwrap(); + /// assert!(m.args_present()); + /// + /// let m = cmd + /// .try_get_matches_from_mut(vec!["myapp"]) + /// .unwrap(); + /// assert!(! m.args_present()); + pub fn args_present(&self) -> bool { + !self.args.is_empty() + } + + /// Deprecated, replaced with [`ArgMatches::get_one()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_one()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn value_of(&self, id: T) -> Option<&str> { + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = unwrap_string_arg(&id, arg.first()?); + Some(v) + } + + /// Deprecated, replaced with [`ArgMatches::get_one()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_one()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn value_of_lossy(&self, id: T) -> Option> { + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = unwrap_os_string_arg(&id, arg.first()?); + Some(v.to_string_lossy()) + } + + /// Deprecated, replaced with [`ArgMatches::get_one()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_one()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn value_of_os(&self, id: T) -> Option<&OsStr> { + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = unwrap_os_string_arg(&id, arg.first()?); + Some(v) + } + + /// Deprecated, replaced with [`ArgMatches::get_many()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn values_of(&self, id: T) -> Option { + #![allow(deprecated)] + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = Values { + iter: arg.vals_flatten().map(unwrap_string), + len: arg.num_vals(), + }; + Some(v) + } + + /// Get an [`Iterator`] over groups of values of a specific option. + /// + /// specifically grouped by the occurrences of the options. + /// + /// Each group is a `Vec<&str>` containing the arguments passed to a single occurrence + /// of the option. + /// + /// If the option doesn't support multiple occurrences, or there was only a single occurrence, + /// the iterator will only contain a single item. + /// + /// Returns `None` if the option wasn't present. + /// + /// # Panics + /// + /// If the value is invalid UTF-8. + /// + /// If `id` is not a valid argument or group id. + /// + /// # Examples + /// ```rust + /// # use clap::{Command,Arg, ArgAction}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("exec") + /// .short('x') + /// .min_values(1) + /// .action(ArgAction::Append) + /// .value_terminator(";")) + /// .get_matches_from(vec![ + /// "myprog", "-x", "echo", "hi", ";", "-x", "echo", "bye"]); + /// let vals: Vec> = m.grouped_values_of("exec").unwrap().collect(); + /// assert_eq!(vals, [["echo", "hi"], ["echo", "bye"]]); + /// ``` + /// [`Iterator`]: std::iter::Iterator + #[cfg(feature = "unstable-grouped")] + #[cfg_attr(debug_assertions, track_caller)] + pub fn grouped_values_of(&self, id: T) -> Option { + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = GroupedValues { + iter: arg.vals().map(|g| g.iter().map(unwrap_string).collect()), + len: arg.vals().len(), + }; + Some(v) + } + + /// Deprecated, replaced with [`ArgMatches::get_many()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn values_of_lossy(&self, id: T) -> Option> { + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = arg + .vals_flatten() + .map(|v| unwrap_os_string_arg(&id, v).to_string_lossy().into_owned()) + .collect(); + Some(v) + } + + /// Deprecated, replaced with [`ArgMatches::get_many()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn values_of_os(&self, id: T) -> Option { + #![allow(deprecated)] + let id = Id::from(id); + let arg = self.get_arg(&id)?; + let v = OsValues { + iter: arg.vals_flatten().map(unwrap_os_string), + len: arg.num_vals(), + }; + Some(v) + } + + /// Deprecated, replaced with [`ArgMatches::get_one()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_one()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn value_of_t(&self, name: &str) -> Result + where + R: FromStr, + ::Err: Display, + { + #![allow(deprecated)] + let v = self + .value_of(name) + .ok_or_else(|| Error::argument_not_found_auto(name.to_string()))?; + v.parse::().map_err(|e| { + let message = format!( + "The argument '{}' isn't a valid value for '{}': {}", + v, name, e + ); + + Error::value_validation(name.to_string(), v.to_string(), message.into()) + }) + } + + /// Deprecated, replaced with [`ArgMatches::get_one()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_one()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn value_of_t_or_exit(&self, name: &str) -> R + where + R: FromStr, + ::Err: Display, + { + #![allow(deprecated)] + self.value_of_t(name).unwrap_or_else(|e| e.exit()) + } + + /// Deprecated, replaced with [`ArgMatches::get_many()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn values_of_t(&self, name: &str) -> Result, Error> + where + R: FromStr, + ::Err: Display, + { + #![allow(deprecated)] + let v = self + .values_of(name) + .ok_or_else(|| Error::argument_not_found_auto(name.to_string()))?; + v.map(|v| { + v.parse::().map_err(|e| { + let message = format!("The argument '{}' isn't a valid value: {}", v, e); + + Error::value_validation(name.to_string(), v.to_string(), message.into()) + }) + }) + .collect() + } + + /// Deprecated, replaced with [`ArgMatches::get_many()`] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn values_of_t_or_exit(&self, name: &str) -> Vec + where + R: FromStr, + ::Err: Display, + { + #![allow(deprecated)] + self.values_of_t(name).unwrap_or_else(|e| e.exit()) + } + + /// Deprecated, replaced with [`ArgAction::SetTrue`][crate::ArgAction] or + /// [`ArgMatches::contains_id`]. + #[cfg_attr( + feature = "deprecated", + deprecated( + since = "3.2.0", + note = "Replaced with either `ArgAction::SetTrue` or `ArgMatches::contains_id(...)`" + ) + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn is_present(&self, id: T) -> bool { + let id = Id::from(id); + + #[cfg(debug_assertions)] + self.get_arg(&id); + + self.args.contains_key(&id) + } + + /// Report where argument value came from + /// + /// # Panics + /// + /// If `id` is is not a valid argument or group id. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ValueSource}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("debug") + /// .short('d')) + /// .get_matches_from(vec![ + /// "myprog", "-d" + /// ]); + /// + /// assert_eq!(m.value_source("debug"), Some(ValueSource::CommandLine)); + /// ``` + /// + /// [`default_value`]: crate::Arg::default_value() + #[cfg_attr(debug_assertions, track_caller)] + pub fn value_source(&self, id: T) -> Option { + let id = Id::from(id); + + let value = self.get_arg(&id); + + value.and_then(MatchedArg::source) + } + + /// Deprecated, replaced with [`ArgAction::Count`][crate::ArgAction] or + /// [`ArgMatches::get_many`]`.len()`. + #[cfg_attr( + feature = "deprecated", + deprecated( + since = "3.2.0", + note = "Replaced with either `ArgAction::Count` or `ArgMatches::get_many(...).len()`" + ) + )] + #[cfg_attr(debug_assertions, track_caller)] + pub fn occurrences_of(&self, id: T) -> u64 { + #![allow(deprecated)] + self.get_arg(&Id::from(id)) + .map_or(0, |a| a.get_occurrences()) + } + + /// The first index of that an argument showed up. + /// + /// Indices are similar to argv indices, but are not exactly 1:1. + /// + /// For flags (i.e. those arguments which don't have an associated value), indices refer + /// to occurrence of the switch, such as `-f`, or `--flag`. However, for options the indices + /// refer to the *values* `-o val` would therefore not represent two distinct indices, only the + /// index for `val` would be recorded. This is by design. + /// + /// Besides the flag/option discrepancy, the primary difference between an argv index and clap + /// index, is that clap continues counting once all arguments have properly separated, whereas + /// an argv index does not. + /// + /// The examples should clear this up. + /// + /// *NOTE:* If an argument is allowed multiple times, this method will only give the *first* + /// index. See [`ArgMatches::indices_of`]. + /// + /// # Panics + /// + /// If `id` is is not a valid argument or group id. + /// + /// # Examples + /// + /// The argv indices are listed in the comments below. See how they correspond to the clap + /// indices. Note that if it's not listed in a clap index, this is because it's not saved in + /// in an `ArgMatches` struct for querying. + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("flag") + /// .short('f')) + /// .arg(Arg::new("option") + /// .short('o') + /// .takes_value(true)) + /// .get_matches_from(vec!["myapp", "-f", "-o", "val"]); + /// // ARGV indices: ^0 ^1 ^2 ^3 + /// // clap indices: ^1 ^3 + /// + /// assert_eq!(m.index_of("flag"), Some(1)); + /// assert_eq!(m.index_of("option"), Some(3)); + /// ``` + /// + /// Now notice, if we use one of the other styles of options: + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("flag") + /// .short('f')) + /// .arg(Arg::new("option") + /// .short('o') + /// .takes_value(true)) + /// .get_matches_from(vec!["myapp", "-f", "-o=val"]); + /// // ARGV indices: ^0 ^1 ^2 + /// // clap indices: ^1 ^3 + /// + /// assert_eq!(m.index_of("flag"), Some(1)); + /// assert_eq!(m.index_of("option"), Some(3)); + /// ``` + /// + /// Things become much more complicated, or clear if we look at a more complex combination of + /// flags. Let's also throw in the final option style for good measure. + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("flag") + /// .short('f')) + /// .arg(Arg::new("flag2") + /// .short('F')) + /// .arg(Arg::new("flag3") + /// .short('z')) + /// .arg(Arg::new("option") + /// .short('o') + /// .takes_value(true)) + /// .get_matches_from(vec!["myapp", "-fzF", "-oval"]); + /// // ARGV indices: ^0 ^1 ^2 + /// // clap indices: ^1,2,3 ^5 + /// // + /// // clap sees the above as 'myapp -f -z -F -o val' + /// // ^0 ^1 ^2 ^3 ^4 ^5 + /// assert_eq!(m.index_of("flag"), Some(1)); + /// assert_eq!(m.index_of("flag2"), Some(3)); + /// assert_eq!(m.index_of("flag3"), Some(2)); + /// assert_eq!(m.index_of("option"), Some(5)); + /// ``` + /// + /// One final combination of flags/options to see how they combine: + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("flag") + /// .short('f')) + /// .arg(Arg::new("flag2") + /// .short('F')) + /// .arg(Arg::new("flag3") + /// .short('z')) + /// .arg(Arg::new("option") + /// .short('o') + /// .takes_value(true)) + /// .get_matches_from(vec!["myapp", "-fzFoval"]); + /// // ARGV indices: ^0 ^1 + /// // clap indices: ^1,2,3^5 + /// // + /// // clap sees the above as 'myapp -f -z -F -o val' + /// // ^0 ^1 ^2 ^3 ^4 ^5 + /// assert_eq!(m.index_of("flag"), Some(1)); + /// assert_eq!(m.index_of("flag2"), Some(3)); + /// assert_eq!(m.index_of("flag3"), Some(2)); + /// assert_eq!(m.index_of("option"), Some(5)); + /// ``` + /// + /// The last part to mention is when values are sent in multiple groups with a [delimiter]. + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("option") + /// .short('o') + /// .use_value_delimiter(true) + /// .multiple_values(true)) + /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); + /// // ARGV indices: ^0 ^1 + /// // clap indices: ^2 ^3 ^4 + /// // + /// // clap sees the above as 'myapp -o val1 val2 val3' + /// // ^0 ^1 ^2 ^3 ^4 + /// assert_eq!(m.index_of("option"), Some(2)); + /// assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2, 3, 4]); + /// ``` + /// [delimiter]: crate::Arg::value_delimiter() + #[cfg_attr(debug_assertions, track_caller)] + pub fn index_of(&self, id: T) -> Option { + let arg = self.get_arg(&Id::from(id))?; + let i = arg.get_index(0)?; + Some(i) + } + + /// All indices an argument appeared at when parsing. + /// + /// Indices are similar to argv indices, but are not exactly 1:1. + /// + /// For flags (i.e. those arguments which don't have an associated value), indices refer + /// to occurrence of the switch, such as `-f`, or `--flag`. However, for options the indices + /// refer to the *values* `-o val` would therefore not represent two distinct indices, only the + /// index for `val` would be recorded. This is by design. + /// + /// *NOTE:* For more information about how clap indices compared to argv indices, see + /// [`ArgMatches::index_of`] + /// + /// # Panics + /// + /// If `id` is is not a valid argument or group id. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("option") + /// .short('o') + /// .use_value_delimiter(true) + /// .multiple_values(true)) + /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); + /// // ARGV indices: ^0 ^1 + /// // clap indices: ^2 ^3 ^4 + /// // + /// // clap sees the above as 'myapp -o val1 val2 val3' + /// // ^0 ^1 ^2 ^3 ^4 + /// assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2, 3, 4]); + /// ``` + /// + /// Another quick example is when flags and options are used together + /// + /// ```rust + /// # use clap::{Command, Arg, ArgAction}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("option") + /// .short('o') + /// .takes_value(true) + /// .action(ArgAction::Append)) + /// .arg(Arg::new("flag") + /// .short('f') + /// .action(ArgAction::Count)) + /// .get_matches_from(vec!["myapp", "-o", "val1", "-f", "-o", "val2", "-f"]); + /// // ARGV indices: ^0 ^1 ^2 ^3 ^4 ^5 ^6 + /// // clap indices: ^2 ^3 ^5 ^6 + /// + /// assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2, 5]); + /// assert_eq!(m.indices_of("flag").unwrap().collect::>(), &[6]); + /// ``` + /// + /// One final example, which is an odd case; if we *don't* use value delimiter as we did with + /// the first example above instead of `val1`, `val2` and `val3` all being distinc values, they + /// would all be a single value of `val1,val2,val3`, in which case they'd only receive a single + /// index. + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") + /// .arg(Arg::new("option") + /// .short('o') + /// .takes_value(true) + /// .multiple_values(true)) + /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); + /// // ARGV indices: ^0 ^1 + /// // clap indices: ^2 + /// // + /// // clap sees the above as 'myapp -o "val1,val2,val3"' + /// // ^0 ^1 ^2 + /// assert_eq!(m.indices_of("option").unwrap().collect::>(), &[2]); + /// ``` + /// [`ArgMatches::index_of`]: ArgMatches::index_of() + /// [delimiter]: Arg::value_delimiter() + #[cfg_attr(debug_assertions, track_caller)] + pub fn indices_of(&self, id: T) -> Option> { + let arg = self.get_arg(&Id::from(id))?; + let i = Indices { + iter: arg.indices(), + len: arg.num_vals(), + }; + Some(i) + } + + #[inline] + #[doc(hidden)] + #[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::try_get_one()`") + )] + pub fn is_valid_arg(&self, _id: impl Key) -> bool { + #[cfg(debug_assertions)] + { + let id = Id::from(_id); + self.disable_asserts || id == Id::empty_hash() || self.valid_args.contains(&id) + } + #[cfg(not(debug_assertions))] + { + true + } + } +} + +/// # Subcommands +impl ArgMatches { + /// The name and `ArgMatches` of the current [subcommand]. + /// + /// Subcommand values are put in a child [`ArgMatches`] + /// + /// Returns `None` if the subcommand wasn't present at runtime, + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg, }; + /// let app_m = Command::new("git") + /// .subcommand(Command::new("clone")) + /// .subcommand(Command::new("push")) + /// .subcommand(Command::new("commit")) + /// .get_matches(); + /// + /// match app_m.subcommand() { + /// Some(("clone", sub_m)) => {}, // clone was used + /// Some(("push", sub_m)) => {}, // push was used + /// Some(("commit", sub_m)) => {}, // commit was used + /// _ => {}, // Either no subcommand or one not tested for... + /// } + /// ``` + /// + /// Another useful scenario is when you want to support third party, or external, subcommands. + /// In these cases you can't know the subcommand name ahead of time, so use a variable instead + /// with pattern matching! + /// + /// ```rust + /// # use clap::Command; + /// // Assume there is an external subcommand named "subcmd" + /// let app_m = Command::new("myprog") + /// .allow_external_subcommands(true) + /// .get_matches_from(vec![ + /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" + /// ]); + /// + /// // All trailing arguments will be stored under the subcommand's sub-matches using an empty + /// // string argument name + /// match app_m.subcommand() { + /// Some((external, sub_m)) => { + /// let ext_args: Vec<&str> = sub_m.get_many::("") + /// .unwrap().map(|s| s.as_str()).collect(); + /// assert_eq!(external, "subcmd"); + /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); + /// }, + /// _ => {}, + /// } + /// ``` + /// [subcommand]: crate::Command::subcommand + #[inline] + pub fn subcommand(&self) -> Option<(&str, &ArgMatches)> { + self.subcommand.as_ref().map(|sc| (&*sc.name, &sc.matches)) + } + + /// Return the name and `ArgMatches` of the current [subcommand]. + /// + /// Subcommand values are put in a child [`ArgMatches`] + /// + /// Returns `None` if the subcommand wasn't present at runtime, + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg, }; + /// let mut app_m = Command::new("git") + /// .subcommand(Command::new("clone")) + /// .subcommand(Command::new("push")) + /// .subcommand(Command::new("commit")) + /// .subcommand_required(true) + /// .get_matches(); + /// + /// let (name, sub_m) = app_m.remove_subcommand().expect("required"); + /// match (name.as_str(), sub_m) { + /// ("clone", sub_m) => {}, // clone was used + /// ("push", sub_m) => {}, // push was used + /// ("commit", sub_m) => {}, // commit was used + /// (name, _) => unimplemented!("{}", name), + /// } + /// ``` + /// + /// Another useful scenario is when you want to support third party, or external, subcommands. + /// In these cases you can't know the subcommand name ahead of time, so use a variable instead + /// with pattern matching! + /// + /// ```rust + /// # use clap::Command; + /// // Assume there is an external subcommand named "subcmd" + /// let mut app_m = Command::new("myprog") + /// .allow_external_subcommands(true) + /// .get_matches_from(vec![ + /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" + /// ]); + /// + /// // All trailing arguments will be stored under the subcommand's sub-matches using an empty + /// // string argument name + /// match app_m.remove_subcommand() { + /// Some((external, mut sub_m)) => { + /// let ext_args: Vec = sub_m.remove_many("") + /// .expect("`file`is required") + /// .collect(); + /// assert_eq!(external, "subcmd"); + /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); + /// }, + /// _ => {}, + /// } + /// ``` + /// [subcommand]: crate::Command::subcommand + pub fn remove_subcommand(&mut self) -> Option<(String, ArgMatches)> { + self.subcommand.take().map(|sc| (sc.name, sc.matches)) + } + + /// The `ArgMatches` for the current [subcommand]. + /// + /// Subcommand values are put in a child [`ArgMatches`] + /// + /// Returns `None` if the subcommand wasn't present at runtime, + /// + /// # Panics + /// + /// If `id` is is not a valid subcommand. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ArgAction}; + /// let app_m = Command::new("myprog") + /// .arg(Arg::new("debug") + /// .short('d') + /// .action(ArgAction::SetTrue) + /// ) + /// .subcommand(Command::new("test") + /// .arg(Arg::new("opt") + /// .long("option") + /// .takes_value(true))) + /// .get_matches_from(vec![ + /// "myprog", "-d", "test", "--option", "val" + /// ]); + /// + /// // Both parent commands, and child subcommands can have arguments present at the same times + /// assert!(*app_m.get_one::("debug").expect("defaulted by clap")); + /// + /// // Get the subcommand's ArgMatches instance + /// if let Some(sub_m) = app_m.subcommand_matches("test") { + /// // Use the struct like normal + /// assert_eq!(sub_m.get_one::("opt").map(|s| s.as_str()), Some("val")); + /// } + /// ``` + /// + /// [subcommand]: crate::Command::subcommand + /// [`Command`]: crate::Command + pub fn subcommand_matches(&self, id: T) -> Option<&ArgMatches> { + self.get_subcommand(&id.into()).map(|sc| &sc.matches) + } + + /// The name of the current [subcommand]. + /// + /// Returns `None` if the subcommand wasn't present at runtime, + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg, }; + /// let app_m = Command::new("git") + /// .subcommand(Command::new("clone")) + /// .subcommand(Command::new("push")) + /// .subcommand(Command::new("commit")) + /// .get_matches(); + /// + /// match app_m.subcommand_name() { + /// Some("clone") => {}, // clone was used + /// Some("push") => {}, // push was used + /// Some("commit") => {}, // commit was used + /// _ => {}, // Either no subcommand or one not tested for... + /// } + /// ``` + /// [subcommand]: crate::Command::subcommand + /// [`Command`]: crate::Command + #[inline] + pub fn subcommand_name(&self) -> Option<&str> { + self.subcommand.as_ref().map(|sc| &*sc.name) + } + + /// Check if a subcommand can be queried + /// + /// By default, `ArgMatches` functions assert on undefined `Id`s to help catch programmer + /// mistakes. In some context, this doesn't work, so users can use this function to check + /// before they do a query on `ArgMatches`. + #[inline] + #[doc(hidden)] + pub fn is_valid_subcommand(&self, _id: impl Key) -> bool { + #[cfg(debug_assertions)] + { + let id = Id::from(_id); + self.disable_asserts || id == Id::empty_hash() || self.valid_subcommands.contains(&id) + } + #[cfg(not(debug_assertions))] + { + true + } + } +} + +/// # Advanced +impl ArgMatches { + /// Non-panicking version of [`ArgMatches::get_one`] + pub fn try_get_one( + &self, + id: &str, + ) -> Result, MatchesError> { + let id = Id::from(id); + let arg = self.try_get_arg_t::(&id)?; + let value = match arg.and_then(|a| a.first()) { + Some(value) => value, + None => { + return Ok(None); + } + }; + Ok(value + .downcast_ref::() + .map(Some) + .expect(INTERNAL_ERROR_MSG)) // enforced by `try_get_arg_t` + } + + /// Non-panicking version of [`ArgMatches::get_many`] + pub fn try_get_many( + &self, + id: &str, + ) -> Result>, MatchesError> { + let id = Id::from(id); + let arg = match self.try_get_arg_t::(&id)? { + Some(arg) => arg, + None => return Ok(None), + }; + let len = arg.num_vals(); + let values = arg.vals_flatten(); + let values = ValuesRef { + // enforced by `try_get_arg_t` + iter: values.map(|v| v.downcast_ref::().expect(INTERNAL_ERROR_MSG)), + len, + }; + Ok(Some(values)) + } + + /// Non-panicking version of [`ArgMatches::get_raw`] + pub fn try_get_raw(&self, id: &str) -> Result>, MatchesError> { + let id = Id::from(id); + let arg = match self.try_get_arg(&id)? { + Some(arg) => arg, + None => return Ok(None), + }; + let len = arg.num_vals(); + let values = arg.raw_vals_flatten(); + let values = RawValues { + iter: values.map(OsString::as_os_str), + len, + }; + Ok(Some(values)) + } + + /// Non-panicking version of [`ArgMatches::remove_one`] + pub fn try_remove_one( + &mut self, + id: &str, + ) -> Result, MatchesError> { + let id = Id::from(id); + match self.try_remove_arg_t::(&id)? { + Some(values) => Ok(values + .into_vals_flatten() + // enforced by `try_get_arg_t` + .map(|v| v.downcast_into::().expect(INTERNAL_ERROR_MSG)) + .next()), + None => Ok(None), + } + } + + /// Non-panicking version of [`ArgMatches::remove_many`] + pub fn try_remove_many( + &mut self, + id: &str, + ) -> Result>, MatchesError> { + let id = Id::from(id); + let arg = match self.try_remove_arg_t::(&id)? { + Some(arg) => arg, + None => return Ok(None), + }; + let len = arg.num_vals(); + let values = arg.into_vals_flatten(); + let values = Values2 { + // enforced by `try_get_arg_t` + iter: values.map(|v| v.downcast_into::().expect(INTERNAL_ERROR_MSG)), + len, + }; + Ok(Some(values)) + } + + /// Non-panicking version of [`ArgMatches::contains_id`] + pub fn try_contains_id(&self, id: &str) -> Result { + let id = Id::from(id); + + self.verify_arg(&id)?; + + let presence = self.args.contains_key(&id); + Ok(presence) + } +} + +// Private methods +impl ArgMatches { + #[inline] + fn try_get_arg(&self, arg: &Id) -> Result, MatchesError> { + self.verify_arg(arg)?; + Ok(self.args.get(arg)) + } + + #[inline] + fn try_get_arg_t( + &self, + arg: &Id, + ) -> Result, MatchesError> { + let arg = match self.try_get_arg(arg)? { + Some(arg) => arg, + None => { + return Ok(None); + } + }; + self.verify_arg_t::(arg)?; + Ok(Some(arg)) + } + + #[inline] + fn try_remove_arg_t( + &mut self, + arg: &Id, + ) -> Result, MatchesError> { + self.verify_arg(arg)?; + let matched = match self.args.remove(arg) { + Some(matched) => matched, + None => { + return Ok(None); + } + }; + + let expected = AnyValueId::of::(); + let actual = matched.infer_type_id(expected); + if actual == expected { + Ok(Some(matched)) + } else { + self.args.insert(arg.clone(), matched); + Err(MatchesError::Downcast { actual, expected }) + } + } + + fn verify_arg_t( + &self, + arg: &MatchedArg, + ) -> Result<(), MatchesError> { + let expected = AnyValueId::of::(); + let actual = arg.infer_type_id(expected); + if expected == actual { + Ok(()) + } else { + Err(MatchesError::Downcast { actual, expected }) + } + } + + #[inline] + fn verify_arg(&self, _arg: &Id) -> Result<(), MatchesError> { + #[cfg(debug_assertions)] + { + if self.disable_asserts || *_arg == Id::empty_hash() || self.valid_args.contains(_arg) { + } else if self.valid_subcommands.contains(_arg) { + debug!( + "Subcommand `{:?}` used where an argument or group name was expected.", + _arg + ); + return Err(MatchesError::UnknownArgument {}); + } else { + debug!( + "`{:?}` is not an id of an argument or a group.\n\ + Make sure you're using the name of the argument itself \ + and not the name of short or long flags.", + _arg + ); + return Err(MatchesError::UnknownArgument {}); + } + } + Ok(()) + } + + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn get_arg(&self, arg: &Id) -> Option<&MatchedArg> { + #[cfg(debug_assertions)] + { + if self.disable_asserts || *arg == Id::empty_hash() || self.valid_args.contains(arg) { + } else if self.valid_subcommands.contains(arg) { + panic!( + "Subcommand `{:?}` used where an argument or group name was expected.", + arg + ); + } else { + panic!( + "`{:?}` is not an id of an argument or a group.\n\ + Make sure you're using the name of the argument itself \ + and not the name of short or long flags.", + arg + ); + } + } + + self.args.get(arg) + } + + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn get_subcommand(&self, id: &Id) -> Option<&SubCommand> { + #[cfg(debug_assertions)] + { + if self.disable_asserts + || *id == Id::empty_hash() + || self.valid_subcommands.contains(id) + { + } else if self.valid_args.contains(id) { + panic!( + "Argument or group `{:?}` used where a subcommand name was expected.", + id + ); + } else { + panic!("`{:?}` is not a name of a subcommand.", id); + } + } + + if let Some(ref sc) = self.subcommand { + if sc.id == *id { + return Some(sc); + } + } + + None + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct SubCommand { + pub(crate) id: Id, + pub(crate) name: String, + pub(crate) matches: ArgMatches, +} + +/// Iterate over multiple values for an argument via [`ArgMatches::remove_many`]. +/// +/// # Examples +/// +/// ```rust +/// # use clap::{Command, Arg, ArgAction}; +/// let mut m = Command::new("myapp") +/// .arg(Arg::new("output") +/// .short('o') +/// .action(ArgAction::Append) +/// .takes_value(true)) +/// .get_matches_from(vec!["myapp", "-o", "val1", "-o", "val2"]); +/// +/// let mut values = m.remove_many::("output") +/// .unwrap(); +/// +/// assert_eq!(values.next(), Some(String::from("val1"))); +/// assert_eq!(values.next(), Some(String::from("val2"))); +/// assert_eq!(values.next(), None); +/// ``` +#[derive(Clone, Debug)] +pub struct Values2 { + #[allow(clippy::type_complexity)] + iter: Map>>, fn(AnyValue) -> T>, + len: usize, +} + +impl Iterator for Values2 { + type Item = T; + + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl DoubleEndedIterator for Values2 { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl ExactSizeIterator for Values2 {} + +/// Creates an empty iterator. +impl Default for Values2 { + fn default() -> Self { + let empty: Vec> = Default::default(); + Values2 { + iter: empty.into_iter().flatten().map(|_| unreachable!()), + len: 0, + } + } +} + +/// Iterate over multiple values for an argument via [`ArgMatches::get_many`]. +/// +/// # Examples +/// +/// ```rust +/// # use clap::{Command, Arg, ArgAction}; +/// let m = Command::new("myapp") +/// .arg(Arg::new("output") +/// .short('o') +/// .action(ArgAction::Append) +/// .takes_value(true)) +/// .get_matches_from(vec!["myapp", "-o", "val1", "-o", "val2"]); +/// +/// let mut values = m.get_many::("output") +/// .unwrap() +/// .map(|s| s.as_str()); +/// +/// assert_eq!(values.next(), Some("val1")); +/// assert_eq!(values.next(), Some("val2")); +/// assert_eq!(values.next(), None); +/// ``` +#[derive(Clone, Debug)] +pub struct ValuesRef<'a, T> { + #[allow(clippy::type_complexity)] + iter: Map>>, fn(&AnyValue) -> &T>, + len: usize, +} + +impl<'a, T: 'a> Iterator for ValuesRef<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl<'a, T: 'a> DoubleEndedIterator for ValuesRef<'a, T> { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl<'a, T: 'a> ExactSizeIterator for ValuesRef<'a, T> {} + +/// Creates an empty iterator. +impl<'a, T: 'a> Default for ValuesRef<'a, T> { + fn default() -> Self { + static EMPTY: [Vec; 0] = []; + ValuesRef { + iter: EMPTY[..].iter().flatten().map(|_| unreachable!()), + len: 0, + } + } +} + +/// Iterate over raw argument values via [`ArgMatches::get_raw`]. +/// +/// # Examples +/// +#[cfg_attr(not(unix), doc = " ```ignore")] +#[cfg_attr(unix, doc = " ```")] +/// # use clap::{Command, arg, value_parser}; +/// use std::ffi::OsString; +/// use std::os::unix::ffi::{OsStrExt,OsStringExt}; +/// +/// let m = Command::new("utf8") +/// .arg(arg!( "some arg") +/// .value_parser(value_parser!(OsString))) +/// .get_matches_from(vec![OsString::from("myprog"), +/// // "Hi {0xe9}!" +/// OsString::from_vec(vec![b'H', b'i', b' ', 0xe9, b'!'])]); +/// assert_eq!( +/// &*m.get_raw("arg") +/// .unwrap() +/// .next().unwrap() +/// .as_bytes(), +/// [b'H', b'i', b' ', 0xe9, b'!'] +/// ); +/// ``` +#[derive(Clone, Debug)] +pub struct RawValues<'a> { + #[allow(clippy::type_complexity)] + iter: Map>>, fn(&OsString) -> &OsStr>, + len: usize, +} + +impl<'a> Iterator for RawValues<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl<'a> DoubleEndedIterator for RawValues<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.iter.next_back() + } +} + +impl<'a> ExactSizeIterator for RawValues<'a> {} + +/// Creates an empty iterator. +impl Default for RawValues<'_> { + fn default() -> Self { + static EMPTY: [Vec; 0] = []; + RawValues { + iter: EMPTY[..].iter().flatten().map(|_| unreachable!()), + len: 0, + } + } +} + +// The following were taken and adapted from vec_map source +// repo: https://github.com/contain-rs/vec-map +// commit: be5e1fa3c26e351761b33010ddbdaf5f05dbcc33 +// license: MIT - Copyright (c) 2015 The Rust Project Developers + +/// Deprecated, replaced with [`ArgMatches::get_many()`] +#[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") +)] +#[derive(Clone, Debug)] +pub struct Values<'a> { + #[allow(clippy::type_complexity)] + iter: Map>>, for<'r> fn(&'r AnyValue) -> &'r str>, + len: usize, +} + +#[allow(deprecated)] +impl<'a> Iterator for Values<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +#[allow(deprecated)] +impl<'a> DoubleEndedIterator for Values<'a> { + fn next_back(&mut self) -> Option<&'a str> { + self.iter.next_back() + } +} + +#[allow(deprecated)] +impl<'a> ExactSizeIterator for Values<'a> {} + +/// Creates an empty iterator. +#[allow(deprecated)] +impl<'a> Default for Values<'a> { + fn default() -> Self { + static EMPTY: [Vec; 0] = []; + Values { + iter: EMPTY[..].iter().flatten().map(|_| unreachable!()), + len: 0, + } + } +} + +#[derive(Clone)] +#[allow(missing_debug_implementations)] +pub struct GroupedValues<'a> { + #[allow(clippy::type_complexity)] + iter: Map>, fn(&Vec) -> Vec<&str>>, + len: usize, +} + +impl<'a> Iterator for GroupedValues<'a> { + type Item = Vec<&'a str>; + + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl<'a> DoubleEndedIterator for GroupedValues<'a> { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl<'a> ExactSizeIterator for GroupedValues<'a> {} + +/// Creates an empty iterator. Used for `unwrap_or_default()`. +impl<'a> Default for GroupedValues<'a> { + fn default() -> Self { + #![allow(deprecated)] + static EMPTY: [Vec; 0] = []; + GroupedValues { + iter: EMPTY[..].iter().map(|_| unreachable!()), + len: 0, + } + } +} + +/// Deprecated, replaced with [`ArgMatches::get_many()`] +#[cfg_attr( + feature = "deprecated", + deprecated(since = "3.2.0", note = "Replaced with `ArgMatches::get_many()`") +)] +#[derive(Clone, Debug)] +pub struct OsValues<'a> { + #[allow(clippy::type_complexity)] + iter: Map>>, fn(&AnyValue) -> &OsStr>, + len: usize, +} + +#[allow(deprecated)] +impl<'a> Iterator for OsValues<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +#[allow(deprecated)] +impl<'a> DoubleEndedIterator for OsValues<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.iter.next_back() + } +} + +#[allow(deprecated)] +impl<'a> ExactSizeIterator for OsValues<'a> {} + +/// Creates an empty iterator. +#[allow(deprecated)] +impl Default for OsValues<'_> { + fn default() -> Self { + static EMPTY: [Vec; 0] = []; + OsValues { + iter: EMPTY[..].iter().flatten().map(|_| unreachable!()), + len: 0, + } + } +} + +/// Iterate over indices for where an argument appeared when parsing, via [`ArgMatches::indices_of`] +/// +/// # Examples +/// +/// ```rust +/// # use clap::{Command, Arg}; +/// let m = Command::new("myapp") +/// .arg(Arg::new("output") +/// .short('o') +/// .multiple_values(true) +/// .takes_value(true)) +/// .get_matches_from(vec!["myapp", "-o", "val1", "val2"]); +/// +/// let mut indices = m.indices_of("output").unwrap(); +/// +/// assert_eq!(indices.next(), Some(2)); +/// assert_eq!(indices.next(), Some(3)); +/// assert_eq!(indices.next(), None); +/// ``` +/// [`ArgMatches::indices_of`]: ArgMatches::indices_of() +#[derive(Clone, Debug)] +pub struct Indices<'a> { + iter: Cloned>, + len: usize, +} + +impl<'a> Iterator for Indices<'a> { + type Item = usize; + + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl<'a> DoubleEndedIterator for Indices<'a> { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl<'a> ExactSizeIterator for Indices<'a> {} + +/// Creates an empty iterator. +impl<'a> Default for Indices<'a> { + fn default() -> Self { + static EMPTY: [usize; 0] = []; + // This is never called because the iterator is empty: + Indices { + iter: EMPTY[..].iter().cloned(), + len: 0, + } + } +} + +#[cfg_attr(debug_assertions, track_caller)] +#[inline] +fn unwrap_string(value: &AnyValue) -> &str { + match value.downcast_ref::() { + Some(value) => value, + None => { + panic!("Must use `_os` lookups with `Arg::allow_invalid_utf8`",) + } + } +} + +#[cfg_attr(debug_assertions, track_caller)] +#[inline] +fn unwrap_string_arg<'v>(id: &Id, value: &'v AnyValue) -> &'v str { + match value.downcast_ref::() { + Some(value) => value, + None => { + panic!( + "Must use `_os` lookups with `Arg::allow_invalid_utf8` at `{:?}`", + id + ) + } + } +} + +#[cfg_attr(debug_assertions, track_caller)] +#[inline] +fn unwrap_os_string(value: &AnyValue) -> &OsStr { + match value.downcast_ref::() { + Some(value) => value, + None => { + panic!("Must use `Arg::allow_invalid_utf8` with `_os` lookups",) + } + } +} + +#[cfg_attr(debug_assertions, track_caller)] +#[inline] +fn unwrap_os_string_arg<'v>(id: &Id, value: &'v AnyValue) -> &'v OsStr { + match value.downcast_ref::() { + Some(value) => value, + None => { + panic!( + "Must use `Arg::allow_invalid_utf8` with `_os` lookups at `{:?}`", + id + ) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_values() { + #![allow(deprecated)] + let mut values: Values = Values::default(); + assert_eq!(values.next(), None); + } + + #[test] + fn test_default_osvalues() { + #![allow(deprecated)] + let mut values: OsValues = OsValues::default(); + assert_eq!(values.next(), None); + } + + #[test] + fn test_default_raw_values() { + let mut values: RawValues = Default::default(); + assert_eq!(values.next(), None); + } + + #[test] + fn test_default_indices() { + let mut indices: Indices = Indices::default(); + assert_eq!(indices.next(), None); + } + + #[test] + fn test_default_indices_with_shorter_lifetime() { + let matches = ArgMatches::default(); + let mut indices = matches.indices_of("").unwrap_or_default(); + assert_eq!(indices.next(), None); + } + + #[test] + fn values_exact_size() { + let l = crate::Command::new("test") + .arg( + crate::Arg::new("POTATO") + .takes_value(true) + .multiple_values(true) + .required(true), + ) + .try_get_matches_from(["test", "one"]) + .unwrap() + .get_many::("POTATO") + .expect("present") + .count(); + assert_eq!(l, 1); + } + + #[test] + fn os_values_exact_size() { + let l = crate::Command::new("test") + .arg( + crate::Arg::new("POTATO") + .takes_value(true) + .multiple_values(true) + .value_parser(crate::builder::ValueParser::os_string()) + .required(true), + ) + .try_get_matches_from(["test", "one"]) + .unwrap() + .get_many::("POTATO") + .expect("present") + .count(); + assert_eq!(l, 1); + } + + #[test] + fn indices_exact_size() { + let l = crate::Command::new("test") + .arg( + crate::Arg::new("POTATO") + .takes_value(true) + .multiple_values(true) + .required(true), + ) + .try_get_matches_from(["test", "one"]) + .unwrap() + .indices_of("POTATO") + .expect("present") + .len(); + assert_eq!(l, 1); + } +} diff --git a/vendor/clap/src/parser/matches/matched_arg.rs b/vendor/clap/src/parser/matches/matched_arg.rs new file mode 100644 index 000000000..fde6d37f3 --- /dev/null +++ b/vendor/clap/src/parser/matches/matched_arg.rs @@ -0,0 +1,240 @@ +// Std +use std::{ + ffi::{OsStr, OsString}, + iter::{Cloned, Flatten}, + slice::Iter, +}; + +use crate::builder::ArgPredicate; +use crate::parser::AnyValue; +use crate::parser::AnyValueId; +use crate::parser::ValueSource; +use crate::util::eq_ignore_case; +use crate::INTERNAL_ERROR_MSG; + +#[derive(Debug, Clone)] +pub(crate) struct MatchedArg { + occurs: u64, + source: Option, + indices: Vec, + type_id: Option, + vals: Vec>, + raw_vals: Vec>, + ignore_case: bool, +} + +impl MatchedArg { + pub(crate) fn new_arg(arg: &crate::Arg) -> Self { + let ignore_case = arg.is_ignore_case_set(); + Self { + occurs: 0, + source: None, + indices: Vec::new(), + type_id: Some(arg.get_value_parser().type_id()), + vals: Vec::new(), + raw_vals: Vec::new(), + ignore_case, + } + } + + pub(crate) fn new_group() -> Self { + let ignore_case = false; + Self { + occurs: 0, + source: None, + indices: Vec::new(), + type_id: None, + vals: Vec::new(), + raw_vals: Vec::new(), + ignore_case, + } + } + + pub(crate) fn new_external(cmd: &crate::Command) -> Self { + let ignore_case = false; + Self { + occurs: 0, + source: None, + indices: Vec::new(), + type_id: Some( + cmd.get_external_subcommand_value_parser() + .expect(INTERNAL_ERROR_MSG) + .type_id(), + ), + vals: Vec::new(), + raw_vals: Vec::new(), + ignore_case, + } + } + + #[cfg_attr(feature = "deprecated", deprecated(since = "3.2.0"))] + pub(crate) fn inc_occurrences(&mut self) { + self.occurs += 1; + } + + #[cfg_attr(feature = "deprecated", deprecated(since = "3.2.0"))] + pub(crate) fn set_occurrences(&mut self, occurs: u64) { + self.occurs = occurs + } + + #[cfg_attr(feature = "deprecated", deprecated(since = "3.2.0"))] + pub(crate) fn get_occurrences(&self) -> u64 { + self.occurs + } + + pub(crate) fn indices(&self) -> Cloned> { + self.indices.iter().cloned() + } + + pub(crate) fn get_index(&self, index: usize) -> Option { + self.indices.get(index).cloned() + } + + pub(crate) fn push_index(&mut self, index: usize) { + self.indices.push(index) + } + + #[cfg(feature = "unstable-grouped")] + pub(crate) fn vals(&self) -> Iter> { + self.vals.iter() + } + + pub(crate) fn vals_flatten(&self) -> Flatten>> { + self.vals.iter().flatten() + } + + pub(crate) fn into_vals_flatten(self) -> Flatten>> { + self.vals.into_iter().flatten() + } + + pub(crate) fn raw_vals_flatten(&self) -> Flatten>> { + self.raw_vals.iter().flatten() + } + + pub(crate) fn first(&self) -> Option<&AnyValue> { + self.vals_flatten().next() + } + + #[cfg(test)] + pub(crate) fn first_raw(&self) -> Option<&OsString> { + self.raw_vals_flatten().next() + } + + pub(crate) fn new_val_group(&mut self) { + self.vals.push(vec![]); + self.raw_vals.push(vec![]); + } + + pub(crate) fn append_val(&mut self, val: AnyValue, raw_val: OsString) { + // We assume there is always a group created before. + self.vals.last_mut().expect(INTERNAL_ERROR_MSG).push(val); + self.raw_vals + .last_mut() + .expect(INTERNAL_ERROR_MSG) + .push(raw_val); + } + + pub(crate) fn num_vals(&self) -> usize { + self.vals.iter().map(|v| v.len()).sum() + } + + // Will be used later + #[allow(dead_code)] + pub(crate) fn num_vals_last_group(&self) -> usize { + self.vals.last().map(|x| x.len()).unwrap_or(0) + } + + pub(crate) fn all_val_groups_empty(&self) -> bool { + self.vals.iter().flatten().count() == 0 + } + + pub(crate) fn check_explicit(&self, predicate: ArgPredicate) -> bool { + if self.source == Some(ValueSource::DefaultValue) { + return false; + } + + match predicate { + ArgPredicate::Equals(val) => self.raw_vals_flatten().any(|v| { + if self.ignore_case { + // If `v` isn't utf8, it can't match `val`, so `OsStr::to_str` should be fine + eq_ignore_case(&v.to_string_lossy(), &val.to_string_lossy()) + } else { + OsString::as_os_str(v) == OsStr::new(val) + } + }), + ArgPredicate::IsPresent => true, + } + } + + pub(crate) fn source(&self) -> Option { + self.source + } + + pub(crate) fn set_source(&mut self, source: ValueSource) { + if let Some(existing) = self.source { + self.source = Some(existing.max(source)); + } else { + self.source = Some(source) + } + } + + pub(crate) fn type_id(&self) -> Option { + self.type_id + } + + pub(crate) fn infer_type_id(&self, expected: AnyValueId) -> AnyValueId { + self.type_id() + .or_else(|| { + self.vals_flatten() + .map(|v| v.type_id()) + .find(|actual| *actual != expected) + }) + .unwrap_or(expected) + } +} + +impl PartialEq for MatchedArg { + fn eq(&self, other: &MatchedArg) -> bool { + let MatchedArg { + occurs: self_occurs, + source: self_source, + indices: self_indices, + type_id: self_type_id, + vals: _, + raw_vals: self_raw_vals, + ignore_case: self_ignore_case, + } = self; + let MatchedArg { + occurs: other_occurs, + source: other_source, + indices: other_indices, + type_id: other_type_id, + vals: _, + raw_vals: other_raw_vals, + ignore_case: other_ignore_case, + } = other; + self_occurs == other_occurs + && self_source == other_source + && self_indices == other_indices + && self_type_id == other_type_id + && self_raw_vals == other_raw_vals + && self_ignore_case == other_ignore_case + } +} + +impl Eq for MatchedArg {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_grouped_vals_first() { + let mut m = MatchedArg::new_group(); + m.new_val_group(); + m.new_val_group(); + m.append_val(AnyValue::new(String::from("bbb")), "bbb".into()); + m.append_val(AnyValue::new(String::from("ccc")), "ccc".into()); + assert_eq!(m.first_raw(), Some(&OsString::from("bbb"))); + } +} diff --git a/vendor/clap/src/parser/matches/mod.rs b/vendor/clap/src/parser/matches/mod.rs new file mode 100644 index 000000000..7b88eeca7 --- /dev/null +++ b/vendor/clap/src/parser/matches/mod.rs @@ -0,0 +1,17 @@ +mod any_value; +mod arg_matches; +mod matched_arg; +mod value_source; + +pub use any_value::AnyValueId; +pub use arg_matches::RawValues; +pub use arg_matches::ValuesRef; +pub use arg_matches::{ArgMatches, Indices}; +pub use value_source::ValueSource; + +pub(crate) use any_value::AnyValue; +pub(crate) use arg_matches::SubCommand; +pub(crate) use matched_arg::MatchedArg; + +#[allow(deprecated)] +pub use arg_matches::{OsValues, Values}; diff --git a/vendor/clap/src/parser/matches/value_source.rs b/vendor/clap/src/parser/matches/value_source.rs new file mode 100644 index 000000000..fb762d2af --- /dev/null +++ b/vendor/clap/src/parser/matches/value_source.rs @@ -0,0 +1,11 @@ +/// Origin of the argument's value +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[non_exhaustive] +pub enum ValueSource { + /// Value came [`Arg::default_value`][crate::Arg::default_value] + DefaultValue, + /// Value came [`Arg::env`][crate::Arg::env] + EnvVariable, + /// Value was passed in on the command-line + CommandLine, +} diff --git a/vendor/clap/src/parser/mod.rs b/vendor/clap/src/parser/mod.rs new file mode 100644 index 000000000..da81648e1 --- /dev/null +++ b/vendor/clap/src/parser/mod.rs @@ -0,0 +1,27 @@ +//! [`Command`][crate::Command] line argument parser + +mod arg_matcher; +mod error; +mod matches; +#[allow(clippy::module_inception)] +mod parser; +mod validator; + +pub(crate) mod features; + +pub(crate) use self::arg_matcher::ArgMatcher; +pub(crate) use self::matches::AnyValue; +pub(crate) use self::matches::AnyValueId; +pub(crate) use self::matches::{MatchedArg, SubCommand}; +pub(crate) use self::parser::Identifier; +pub(crate) use self::parser::PendingArg; +pub(crate) use self::parser::{ParseState, Parser}; +pub(crate) use self::validator::Validator; + +pub use self::matches::RawValues; +pub use self::matches::ValuesRef; +pub use self::matches::{ArgMatches, Indices, ValueSource}; +pub use error::MatchesError; + +#[allow(deprecated)] +pub use self::matches::{OsValues, Values}; diff --git a/vendor/clap/src/parser/parser.rs b/vendor/clap/src/parser/parser.rs new file mode 100644 index 000000000..fc95dad22 --- /dev/null +++ b/vendor/clap/src/parser/parser.rs @@ -0,0 +1,1727 @@ +// Std +use std::{ + cell::Cell, + ffi::{OsStr, OsString}, +}; + +// Third Party +use clap_lex::RawOsStr; + +// Internal +use crate::builder::AppSettings as AS; +use crate::builder::{Arg, Command}; +use crate::error::Error as ClapError; +use crate::error::Result as ClapResult; +use crate::mkeymap::KeyType; +use crate::output::fmt::Stream; +use crate::output::{fmt::Colorizer, Usage}; +use crate::parser::features::suggestions; +use crate::parser::{ArgMatcher, SubCommand}; +use crate::parser::{Validator, ValueSource}; +use crate::util::Id; +use crate::ArgAction; +use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8}; + +pub(crate) struct Parser<'help, 'cmd> { + cmd: &'cmd mut Command<'help>, + cur_idx: Cell, + /// Index of the previous flag subcommand in a group of flags. + flag_subcmd_at: Option, + /// Counter indicating the number of items to skip + /// when revisiting the group of flags which includes the flag subcommand. + flag_subcmd_skip: usize, +} + +// Initializing Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd mut Command<'help>) -> Self { + Parser { + cmd, + cur_idx: Cell::new(0), + flag_subcmd_at: None, + flag_subcmd_skip: 0, + } + } +} + +// Parsing Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + // The actual parsing function + #[allow(clippy::cognitive_complexity)] + pub(crate) fn get_matches_with( + &mut self, + matcher: &mut ArgMatcher, + raw_args: &mut clap_lex::RawArgs, + mut args_cursor: clap_lex::ArgCursor, + ) -> ClapResult<()> { + debug!("Parser::get_matches_with"); + // Verify all positional assertions pass + + let mut subcmd_name: Option = None; + let mut keep_state = false; + let mut parse_state = ParseState::ValuesDone; + let mut pos_counter = 1; + + // Already met any valid arg(then we shouldn't expect subcommands after it). + let mut valid_arg_found = false; + // If the user already passed '--'. Meaning only positional args follow. + let mut trailing_values = false; + + // Count of positional args + let positional_count = self + .cmd + .get_keymap() + .keys() + .filter(|x| x.is_position()) + .count(); + // If any arg sets .last(true) + let contains_last = self.cmd.get_arguments().any(|x| x.is_last_set()); + + while let Some(arg_os) = raw_args.next(&mut args_cursor) { + // Recover the replaced items if any. + if let Some(replaced_items) = arg_os + .to_value() + .ok() + .and_then(|a| self.cmd.get_replacement(a)) + { + debug!( + "Parser::get_matches_with: found replacer: {:?}, target: {:?}", + arg_os, replaced_items + ); + raw_args.insert(&args_cursor, replaced_items); + continue; + } + + debug!( + "Parser::get_matches_with: Begin parsing '{:?}' ({:?})", + arg_os.to_value_os(), + arg_os.to_value_os().as_raw_bytes() + ); + + // Correct pos_counter. + pos_counter = { + let is_second_to_last = pos_counter + 1 == positional_count; + + // The last positional argument, or second to last positional + // argument may be set to .multiple_values(true) or `.multiple_occurrences(true)` + let low_index_mults = is_second_to_last + && self + .cmd + .get_positionals() + .any(|a| a.is_multiple() && (positional_count != a.index.unwrap_or(0))) + && self + .cmd + .get_positionals() + .last() + .map_or(false, |p_name| !p_name.is_last_set()); + + let missing_pos = self.cmd.is_allow_missing_positional_set() + && is_second_to_last + && !trailing_values; + + debug!( + "Parser::get_matches_with: Positional counter...{}", + pos_counter + ); + debug!( + "Parser::get_matches_with: Low index multiples...{:?}", + low_index_mults + ); + + if low_index_mults || missing_pos { + let skip_current = if let Some(n) = raw_args.peek(&args_cursor) { + if let Some(arg) = self + .cmd + .get_positionals() + .find(|a| a.index == Some(pos_counter)) + { + // If next value looks like a new_arg or it's a + // subcommand, skip positional argument under current + // pos_counter(which means current value cannot be a + // positional argument with a value next to it), assume + // current value matches the next arg. + self.is_new_arg(&n, arg) + || self + .possible_subcommand(n.to_value(), valid_arg_found) + .is_some() + } else { + true + } + } else { + true + }; + + if skip_current { + debug!("Parser::get_matches_with: Bumping the positional counter..."); + pos_counter + 1 + } else { + pos_counter + } + } else if trailing_values + && (self.cmd.is_allow_missing_positional_set() || contains_last) + { + // Came to -- and one positional has .last(true) set, so we go immediately + // to the last (highest index) positional + debug!("Parser::get_matches_with: .last(true) and --, setting last pos"); + positional_count + } else { + pos_counter + } + }; + + // Has the user already passed '--'? Meaning only positional args follow + if !trailing_values { + if self.cmd.is_subcommand_precedence_over_arg_set() + || !matches!(parse_state, ParseState::Opt(_) | ParseState::Pos(_)) + { + // Does the arg match a subcommand name, or any of its aliases (if defined) + let sc_name = self.possible_subcommand(arg_os.to_value(), valid_arg_found); + debug!("Parser::get_matches_with: sc={:?}", sc_name); + if let Some(sc_name) = sc_name { + #[allow(deprecated)] + if sc_name == "help" + && !self.is_set(AS::NoAutoHelp) + && !self.cmd.is_disable_help_subcommand_set() + { + self.parse_help_subcommand(raw_args.remaining(&mut args_cursor))?; + unreachable!("`parse_help_subcommand` always errors"); + } else { + subcmd_name = Some(sc_name.to_owned()); + } + break; + } + } + + if arg_os.is_escape() { + if matches!(&parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if + self.cmd[opt].is_allow_hyphen_values_set()) + { + // ParseResult::MaybeHyphenValue, do nothing + } else { + debug!("Parser::get_matches_with: setting TrailingVals=true"); + trailing_values = true; + continue; + } + } else if let Some((long_arg, long_value)) = arg_os.to_long() { + let parse_result = self.parse_long_arg( + matcher, + long_arg, + long_value, + &parse_state, + &mut valid_arg_found, + trailing_values, + )?; + debug!( + "Parser::get_matches_with: After parse_long_arg {:?}", + parse_result + ); + match parse_result { + ParseResult::NoArg => { + unreachable!("`to_long` always has the flag specified") + } + ParseResult::ValuesDone => { + parse_state = ParseState::ValuesDone; + continue; + } + ParseResult::Opt(id) => { + parse_state = ParseState::Opt(id); + continue; + } + ParseResult::FlagSubCommand(name) => { + debug!( + "Parser::get_matches_with: FlagSubCommand found in long arg {:?}", + &name + ); + subcmd_name = Some(name); + break; + } + ParseResult::EqualsNotProvided { arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::no_equals( + self.cmd, + arg, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + ParseResult::NoMatchingArg { arg } => { + let _ = self.resolve_pending(matcher); + let remaining_args: Vec<_> = raw_args + .remaining(&mut args_cursor) + .map(|x| x.to_str().expect(INVALID_UTF8)) + .collect(); + return Err(self.did_you_mean_error(&arg, matcher, &remaining_args)); + } + ParseResult::UnneededAttachedValue { rest, used, arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::too_many_values( + self.cmd, + rest, + arg, + Usage::new(self.cmd).create_usage_no_title(&used), + )); + } + ParseResult::MaybeHyphenValue => { + // Maybe a hyphen value, do nothing. + } + ParseResult::AttachedValueNotConsumed => { + unreachable!() + } + } + } else if let Some(short_arg) = arg_os.to_short() { + // Arg looks like a short flag, and not a possible number + + // Try to parse short args like normal, if allow_hyphen_values or + // AllowNegativeNumbers is set, parse_short_arg will *not* throw + // an error, and instead return Ok(None) + let parse_result = self.parse_short_arg( + matcher, + short_arg, + &parse_state, + pos_counter, + &mut valid_arg_found, + trailing_values, + )?; + // If it's None, we then check if one of those two AppSettings was set + debug!( + "Parser::get_matches_with: After parse_short_arg {:?}", + parse_result + ); + match parse_result { + ParseResult::NoArg => { + // Is a single dash `-`, try positional. + } + ParseResult::ValuesDone => { + parse_state = ParseState::ValuesDone; + continue; + } + ParseResult::Opt(id) => { + parse_state = ParseState::Opt(id); + continue; + } + ParseResult::FlagSubCommand(name) => { + // If there are more short flags to be processed, we should keep the state, and later + // revisit the current group of short flags skipping the subcommand. + keep_state = self + .flag_subcmd_at + .map(|at| { + raw_args + .seek(&mut args_cursor, clap_lex::SeekFrom::Current(-1)); + // Since we are now saving the current state, the number of flags to skip during state recovery should + // be the current index (`cur_idx`) minus ONE UNIT TO THE LEFT of the starting position. + self.flag_subcmd_skip = self.cur_idx.get() - at + 1; + }) + .is_some(); + + debug!( + "Parser::get_matches_with:FlagSubCommandShort: subcmd_name={}, keep_state={}, flag_subcmd_skip={}", + name, + keep_state, + self.flag_subcmd_skip + ); + + subcmd_name = Some(name); + break; + } + ParseResult::EqualsNotProvided { arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::no_equals( + self.cmd, + arg, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + ParseResult::NoMatchingArg { arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::unknown_argument( + self.cmd, + arg, + None, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + ParseResult::MaybeHyphenValue => { + // Maybe a hyphen value, do nothing. + } + ParseResult::UnneededAttachedValue { .. } + | ParseResult::AttachedValueNotConsumed => unreachable!(), + } + } + + if let ParseState::Opt(id) = &parse_state { + // Assume this is a value of a previous arg. + + // get the option so we can check the settings + let arg_values = matcher.pending_values_mut(id, None); + let arg = &self.cmd[id]; + let parse_result = self.split_arg_values( + arg, + arg_os.to_value_os(), + trailing_values, + arg_values, + ); + let parse_result = parse_result.unwrap_or_else(|| { + if matcher.needs_more_vals(arg) { + ParseResult::Opt(arg.id.clone()) + } else { + ParseResult::ValuesDone + } + }); + parse_state = match parse_result { + ParseResult::Opt(id) => ParseState::Opt(id), + ParseResult::ValuesDone => ParseState::ValuesDone, + _ => unreachable!(), + }; + // get the next value from the iterator + continue; + } + } + + if let Some(arg) = self.cmd.get_keymap().get(&pos_counter) { + if arg.is_last_set() && !trailing_values { + let _ = self.resolve_pending(matcher); + return Err(ClapError::unknown_argument( + self.cmd, + arg_os.display().to_string(), + None, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + + if self.cmd.is_trailing_var_arg_set() && pos_counter == positional_count { + trailing_values = true; + } + + if matcher.pending_arg_id() != Some(&arg.id) || !arg.is_multiple_values_set() { + self.resolve_pending(matcher)?; + } + let arg_values = matcher.pending_values_mut(&arg.id, Some(Identifier::Index)); + let _parse_result = + self.split_arg_values(arg, arg_os.to_value_os(), trailing_values, arg_values); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!( + "Parser::get_matches_with: Ignoring state {:?}; positionals do their own thing", + _parse_result + ); + } + } + + // Only increment the positional counter if it doesn't allow multiples + if !arg.is_multiple() { + pos_counter += 1; + parse_state = ParseState::ValuesDone; + } else { + parse_state = ParseState::Pos(arg.id.clone()); + } + valid_arg_found = true; + } else if let Some(external_parser) = + self.cmd.get_external_subcommand_value_parser().cloned() + { + // Get external subcommand name + let sc_name = match arg_os.to_value() { + Ok(s) => s.to_string(), + Err(_) => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::invalid_utf8( + self.cmd, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + }; + + // Collect the external subcommand args + let mut sc_m = ArgMatcher::new(self.cmd); + if cfg!(feature = "unstable-v4") || !raw_args.is_end(&args_cursor) { + sc_m.start_occurrence_of_external(self.cmd); + } + + for raw_val in raw_args.remaining(&mut args_cursor) { + let val = external_parser.parse_ref(self.cmd, None, raw_val)?; + let external_id = &Id::empty_hash(); + sc_m.add_val_to(external_id, val, raw_val.to_os_string()); + } + + matcher.subcommand(SubCommand { + id: Id::from(&*sc_name), + name: sc_name, + matches: sc_m.into_inner(), + }); + + self.resolve_pending(matcher)?; + #[cfg(feature = "env")] + self.add_env(matcher)?; + self.add_defaults(matcher)?; + return Validator::new(self.cmd).validate(parse_state, matcher); + } else { + // Start error processing + let _ = self.resolve_pending(matcher); + return Err(self.match_arg_error(&arg_os, valid_arg_found, trailing_values)); + } + } + + if let Some(ref pos_sc_name) = subcmd_name { + let sc_name = self + .cmd + .find_subcommand(pos_sc_name) + .expect(INTERNAL_ERROR_MSG) + .get_name() + .to_owned(); + self.parse_subcommand(&sc_name, matcher, raw_args, args_cursor, keep_state)?; + } + + self.resolve_pending(matcher)?; + #[cfg(feature = "env")] + self.add_env(matcher)?; + self.add_defaults(matcher)?; + Validator::new(self.cmd).validate(parse_state, matcher) + } + + fn match_arg_error( + &self, + arg_os: &clap_lex::ParsedArg<'_>, + valid_arg_found: bool, + trailing_values: bool, + ) -> ClapError { + // If argument follows a `--` + if trailing_values { + // If the arg matches a subcommand name, or any of its aliases (if defined) + if self + .possible_subcommand(arg_os.to_value(), valid_arg_found) + .is_some() + { + return ClapError::unnecessary_double_dash( + self.cmd, + arg_os.display().to_string(), + Usage::new(self.cmd).create_usage_with_title(&[]), + ); + } + } + let candidates = suggestions::did_you_mean( + &arg_os.display().to_string(), + self.cmd.all_subcommand_names(), + ); + // If the argument looks like a subcommand. + if !candidates.is_empty() { + let candidates: Vec<_> = candidates + .iter() + .map(|candidate| format!("'{}'", candidate)) + .collect(); + return ClapError::invalid_subcommand( + self.cmd, + arg_os.display().to_string(), + candidates.join(" or "), + self.cmd + .get_bin_name() + .unwrap_or_else(|| self.cmd.get_name()) + .to_owned(), + Usage::new(self.cmd).create_usage_with_title(&[]), + ); + } + // If the argument must be a subcommand. + if !self.cmd.has_args() || self.cmd.is_infer_subcommands_set() && self.cmd.has_subcommands() + { + return ClapError::unrecognized_subcommand( + self.cmd, + arg_os.display().to_string(), + Usage::new(self.cmd).create_usage_with_title(&[]), + ); + } + ClapError::unknown_argument( + self.cmd, + arg_os.display().to_string(), + None, + Usage::new(self.cmd).create_usage_with_title(&[]), + ) + } + + // Checks if the arg matches a subcommand name, or any of its aliases (if defined) + fn possible_subcommand( + &self, + arg: Result<&str, &RawOsStr>, + valid_arg_found: bool, + ) -> Option<&str> { + debug!("Parser::possible_subcommand: arg={:?}", arg); + let arg = arg.ok()?; + + if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { + if self.cmd.is_infer_subcommands_set() { + // For subcommand `test`, we accepts it's prefix: `t`, `te`, + // `tes` and `test`. + let v = self + .cmd + .all_subcommand_names() + .filter(|s| s.starts_with(arg)) + .collect::>(); + + if v.len() == 1 { + return Some(v[0]); + } + + // If there is any ambiguity, fallback to non-infer subcommand + // search. + } + if let Some(sc) = self.cmd.find_subcommand(arg) { + return Some(sc.get_name()); + } + } + None + } + + // Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined) + fn possible_long_flag_subcommand(&self, arg: &str) -> Option<&str> { + debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg); + if self.cmd.is_infer_subcommands_set() { + let options = self + .cmd + .get_subcommands() + .fold(Vec::new(), |mut options, sc| { + if let Some(long) = sc.get_long_flag() { + if long.starts_with(arg) { + options.push(long); + } + options.extend(sc.get_all_aliases().filter(|alias| alias.starts_with(arg))) + } + options + }); + if options.len() == 1 { + return Some(options[0]); + } + + for sc in options { + if sc == arg { + return Some(sc); + } + } + } else if let Some(sc_name) = self.cmd.find_long_subcmd(arg) { + return Some(sc_name); + } + None + } + + fn parse_help_subcommand( + &self, + cmds: impl Iterator, + ) -> ClapResult { + debug!("Parser::parse_help_subcommand"); + + let mut cmd = self.cmd.clone(); + let sc = { + let mut sc = &mut cmd; + + for cmd in cmds { + sc = if let Some(sc_name) = + sc.find_subcommand(cmd).map(|sc| sc.get_name().to_owned()) + { + sc._build_subcommand(&sc_name).unwrap() + } else { + return Err(ClapError::unrecognized_subcommand( + sc, + cmd.to_string_lossy().into_owned(), + Usage::new(sc).create_usage_with_title(&[]), + )); + }; + } + + sc + }; + let parser = Parser::new(sc); + + Err(parser.help_err(true, Stream::Stdout)) + } + + fn is_new_arg(&self, next: &clap_lex::ParsedArg<'_>, current_positional: &Arg) -> bool { + #![allow(clippy::needless_bool)] // Prefer consistent if/else-if ladder + + debug!( + "Parser::is_new_arg: {:?}:{:?}", + next.to_value_os(), + current_positional.name + ); + + if self.cmd.is_allow_hyphen_values_set() + || self.cmd[¤t_positional.id].is_allow_hyphen_values_set() + || (self.cmd.is_allow_negative_numbers_set() && next.is_number()) + { + // If allow hyphen, this isn't a new arg. + debug!("Parser::is_new_arg: Allow hyphen"); + false + } else if next.is_long() { + // If this is a long flag, this is a new arg. + debug!("Parser::is_new_arg: -- found"); + true + } else if next.is_short() { + // If this is a short flag, this is a new arg. But a singe '-' by + // itself is a value and typically means "stdin" on unix systems. + debug!("Parser::is_new_arg: - found"); + true + } else { + // Nothing special, this is a value. + debug!("Parser::is_new_arg: value"); + false + } + } + + fn parse_subcommand( + &mut self, + sc_name: &str, + matcher: &mut ArgMatcher, + raw_args: &mut clap_lex::RawArgs, + args_cursor: clap_lex::ArgCursor, + keep_state: bool, + ) -> ClapResult<()> { + debug!("Parser::parse_subcommand"); + + let partial_parsing_enabled = self.cmd.is_ignore_errors_set(); + + if let Some(sc) = self.cmd._build_subcommand(sc_name) { + let mut sc_matcher = ArgMatcher::new(sc); + + debug!( + "Parser::parse_subcommand: About to parse sc={}", + sc.get_name() + ); + + { + let mut p = Parser::new(sc); + // HACK: maintain indexes between parsers + // FlagSubCommand short arg needs to revisit the current short args, but skip the subcommand itself + if keep_state { + p.cur_idx.set(self.cur_idx.get()); + p.flag_subcmd_at = self.flag_subcmd_at; + p.flag_subcmd_skip = self.flag_subcmd_skip; + } + if let Err(error) = p.get_matches_with(&mut sc_matcher, raw_args, args_cursor) { + if partial_parsing_enabled { + debug!( + "Parser::parse_subcommand: ignored error in subcommand {}: {:?}", + sc_name, error + ); + } else { + return Err(error); + } + } + } + matcher.subcommand(SubCommand { + id: sc.get_id(), + name: sc.get_name().to_owned(), + matches: sc_matcher.into_inner(), + }); + } + Ok(()) + } + + fn parse_long_arg( + &mut self, + matcher: &mut ArgMatcher, + long_arg: Result<&str, &RawOsStr>, + long_value: Option<&RawOsStr>, + parse_state: &ParseState, + valid_arg_found: &mut bool, + trailing_values: bool, + ) -> ClapResult { + // maybe here lifetime should be 'a + debug!("Parser::parse_long_arg"); + + if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if + self.cmd[opt].is_allow_hyphen_values_set()) + { + return Ok(ParseResult::MaybeHyphenValue); + } + + debug!("Parser::parse_long_arg: Does it contain '='..."); + let long_arg = match long_arg { + Ok(long_arg) => long_arg, + Err(long_arg) => { + return Ok(ParseResult::NoMatchingArg { + arg: long_arg.to_str_lossy().into_owned(), + }); + } + }; + if long_arg.is_empty() { + debug_assert!(long_value.is_none(), "{:?}", long_value); + return Ok(ParseResult::NoArg); + } + + let arg = if let Some(arg) = self.cmd.get_keymap().get(long_arg) { + debug!( + "Parser::parse_long_arg: Found valid arg or flag '{}'", + arg.to_string() + ); + Some((long_arg, arg)) + } else if self.cmd.is_infer_long_args_set() { + self.cmd.get_arguments().find_map(|a| { + if let Some(long) = a.long { + if long.starts_with(long_arg) { + return Some((long, a)); + } + } + a.aliases + .iter() + .find_map(|(alias, _)| alias.starts_with(long_arg).then(|| (*alias, a))) + }) + } else { + None + }; + + if let Some((_long_arg, arg)) = arg { + let ident = Identifier::Long; + *valid_arg_found = true; + if arg.is_takes_value_set() { + debug!( + "Parser::parse_long_arg({:?}): Found an arg with value '{:?}'", + long_arg, &long_value + ); + let has_eq = long_value.is_some(); + self.parse_opt_value(ident, long_value, arg, matcher, trailing_values, has_eq) + } else if let Some(rest) = long_value { + let required = self.cmd.required_graph(); + debug!( + "Parser::parse_long_arg({:?}): Got invalid literal `{:?}`", + long_arg, rest + ); + let used: Vec = matcher + .arg_ids() + .filter(|arg_id| { + matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + }) + .filter(|&n| { + self.cmd + .find(n) + .map_or(true, |a| !(a.is_hide_set() || required.contains(&a.id))) + }) + .cloned() + .collect(); + + Ok(ParseResult::UnneededAttachedValue { + rest: rest.to_str_lossy().into_owned(), + used, + arg: arg.to_string(), + }) + } else { + debug!("Parser::parse_long_arg({:?}): Presence validated", long_arg); + self.react(Some(ident), ValueSource::CommandLine, arg, vec![], matcher) + } + } else if let Some(sc_name) = self.possible_long_flag_subcommand(long_arg) { + Ok(ParseResult::FlagSubCommand(sc_name.to_string())) + } else if self.cmd.is_allow_hyphen_values_set() { + Ok(ParseResult::MaybeHyphenValue) + } else { + Ok(ParseResult::NoMatchingArg { + arg: long_arg.to_owned(), + }) + } + } + + fn parse_short_arg( + &mut self, + matcher: &mut ArgMatcher, + mut short_arg: clap_lex::ShortFlags<'_>, + parse_state: &ParseState, + // change this to possible pos_arg when removing the usage of &mut Parser. + pos_counter: usize, + valid_arg_found: &mut bool, + trailing_values: bool, + ) -> ClapResult { + debug!("Parser::parse_short_arg: short_arg={:?}", short_arg); + + #[allow(clippy::blocks_in_if_conditions)] + if self.cmd.is_allow_negative_numbers_set() && short_arg.is_number() { + debug!("Parser::parse_short_arg: negative number"); + return Ok(ParseResult::MaybeHyphenValue); + } else if self.cmd.is_allow_hyphen_values_set() + && short_arg + .clone() + .any(|c| !c.map(|c| self.cmd.contains_short(c)).unwrap_or_default()) + { + debug!("Parser::parse_short_args: contains non-short flag"); + return Ok(ParseResult::MaybeHyphenValue); + } else if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) + if self.cmd[opt].is_allow_hyphen_values_set()) + { + debug!("Parser::parse_short_args: prior arg accepts hyphenated values",); + return Ok(ParseResult::MaybeHyphenValue); + } else if self + .cmd + .get_keymap() + .get(&pos_counter) + .map_or(false, |arg| { + arg.is_allow_hyphen_values_set() && !arg.is_last_set() + }) + { + debug!( + "Parser::parse_short_args: positional at {} allows hyphens", + pos_counter + ); + return Ok(ParseResult::MaybeHyphenValue); + } + + let mut ret = ParseResult::NoArg; + + let skip = self.flag_subcmd_skip; + self.flag_subcmd_skip = 0; + let res = short_arg.advance_by(skip); + debug_assert_eq!( + res, + Ok(()), + "tracking of `flag_subcmd_skip` is off for `{:?}`", + short_arg + ); + while let Some(c) = short_arg.next_flag() { + let c = match c { + Ok(c) => c, + Err(rest) => { + return Ok(ParseResult::NoMatchingArg { + arg: format!("-{}", rest.to_str_lossy()), + }); + } + }; + debug!("Parser::parse_short_arg:iter:{}", c); + + // Check for matching short options, and return the name if there is no trailing + // concatenated value: -oval + // Option: -o + // Value: val + if let Some(arg) = self.cmd.get_keymap().get(&c) { + let ident = Identifier::Short; + debug!( + "Parser::parse_short_arg:iter:{}: Found valid opt or flag", + c + ); + *valid_arg_found = true; + if !arg.is_takes_value_set() { + ret = + self.react(Some(ident), ValueSource::CommandLine, arg, vec![], matcher)?; + continue; + } + + // Check for trailing concatenated value + // + // Cloning the iterator, so we rollback if it isn't there. + let val = short_arg.clone().next_value_os().unwrap_or_default(); + debug!( + "Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii), short_arg={:?}", + c, val, val.as_raw_bytes(), short_arg + ); + let val = Some(val).filter(|v| !v.is_empty()); + + // Default to "we're expecting a value later". + // + // If attached value is not consumed, we may have more short + // flags to parse, continue. + // + // e.g. `-xvf`, when require_equals && x.min_vals == 0, we don't + // consume the `vf`, even if it's provided as value. + let (val, has_eq) = if let Some(val) = val.and_then(|v| v.strip_prefix('=')) { + (Some(val), true) + } else { + (val, false) + }; + match self.parse_opt_value(ident, val, arg, matcher, trailing_values, has_eq)? { + ParseResult::AttachedValueNotConsumed => continue, + x => return Ok(x), + } + } + + return if let Some(sc_name) = self.cmd.find_short_subcmd(c) { + debug!("Parser::parse_short_arg:iter:{}: subcommand={}", c, sc_name); + // Make sure indices get updated before reading `self.cur_idx` + self.resolve_pending(matcher)?; + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::parse_short_arg: cur_idx:={}", self.cur_idx.get()); + + let name = sc_name.to_string(); + // Get the index of the previously saved flag subcommand in the group of flags (if exists). + // If it is a new flag subcommand, then the formentioned index should be the current one + // (ie. `cur_idx`), and should be registered. + let cur_idx = self.cur_idx.get(); + self.flag_subcmd_at.get_or_insert(cur_idx); + let done_short_args = short_arg.is_empty(); + if done_short_args { + self.flag_subcmd_at = None; + } + Ok(ParseResult::FlagSubCommand(name)) + } else { + Ok(ParseResult::NoMatchingArg { + arg: format!("-{}", c), + }) + }; + } + Ok(ret) + } + + fn parse_opt_value( + &self, + ident: Identifier, + attached_value: Option<&RawOsStr>, + arg: &Arg<'help>, + matcher: &mut ArgMatcher, + trailing_values: bool, + has_eq: bool, + ) -> ClapResult { + debug!( + "Parser::parse_opt_value; arg={}, val={:?}, has_eq={:?}", + arg.name, attached_value, has_eq + ); + debug!("Parser::parse_opt_value; arg.settings={:?}", arg.settings); + + debug!("Parser::parse_opt_value; Checking for val..."); + // require_equals is set, but no '=' is provided, try throwing error. + if arg.is_require_equals_set() && !has_eq { + if arg.min_vals == Some(0) { + debug!("Requires equals, but min_vals == 0"); + let mut arg_values = Vec::new(); + // We assume this case is valid: require equals, but min_vals == 0. + if !arg.default_missing_vals.is_empty() { + debug!("Parser::parse_opt_value: has default_missing_vals"); + for v in arg.default_missing_vals.iter() { + let trailing_values = false; // CLI should not be affecting default_missing_values + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(v), + trailing_values, + &mut arg_values, + ); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::parse_opt_value: Ignoring state {:?}; no values accepted after default_missing_values", _parse_result); + } + } + } + }; + let react_result = self.react( + Some(ident), + ValueSource::CommandLine, + arg, + arg_values, + matcher, + )?; + debug_assert_eq!(react_result, ParseResult::ValuesDone); + if attached_value.is_some() { + Ok(ParseResult::AttachedValueNotConsumed) + } else { + Ok(ParseResult::ValuesDone) + } + } else { + debug!("Requires equals but not provided. Error."); + Ok(ParseResult::EqualsNotProvided { + arg: arg.to_string(), + }) + } + } else if let Some(v) = attached_value { + let mut arg_values = Vec::new(); + let parse_result = self.split_arg_values(arg, v, trailing_values, &mut arg_values); + let react_result = self.react( + Some(ident), + ValueSource::CommandLine, + arg, + arg_values, + matcher, + )?; + debug_assert_eq!(react_result, ParseResult::ValuesDone); + let mut parse_result = parse_result.unwrap_or_else(|| { + if matcher.needs_more_vals(arg) { + ParseResult::Opt(arg.id.clone()) + } else { + ParseResult::ValuesDone + } + }); + if parse_result != ParseResult::ValuesDone { + debug!("Parser::parse_opt_value: Overriding state {:?}; no values accepted after attached", parse_result); + parse_result = ParseResult::ValuesDone; + } + Ok(parse_result) + } else { + debug!("Parser::parse_opt_value: More arg vals required..."); + self.resolve_pending(matcher)?; + matcher.pending_values_mut(&arg.id, Some(ident)); + Ok(ParseResult::Opt(arg.id.clone())) + } + } + + fn split_arg_values( + &self, + arg: &Arg<'help>, + val: &RawOsStr, + trailing_values: bool, + output: &mut Vec, + ) -> Option { + debug!("Parser::split_arg_values; arg={}, val={:?}", arg.name, val); + debug!( + "Parser::split_arg_values; trailing_values={:?}, DontDelimTrailingVals={:?}", + trailing_values, + self.cmd.is_dont_delimit_trailing_values_set() + ); + + let mut delim = arg.val_delim; + if trailing_values && self.cmd.is_dont_delimit_trailing_values_set() { + delim = None; + } + match delim { + Some(delim) if val.contains(delim) => { + let vals = val.split(delim).map(|x| x.to_os_str().into_owned()); + for raw_val in vals { + if Some(raw_val.as_os_str()) == arg.terminator.map(OsStr::new) { + return Some(ParseResult::ValuesDone); + } + output.push(raw_val); + } + // Delimited values are always considered the final value + Some(ParseResult::ValuesDone) + } + _ if Some(val) == arg.terminator.map(RawOsStr::from_str) => { + Some(ParseResult::ValuesDone) + } + _ => { + output.push(val.to_os_str().into_owned()); + if arg.is_require_value_delimiter_set() { + Some(ParseResult::ValuesDone) + } else { + None + } + } + } + } + + fn push_arg_values( + &self, + arg: &Arg<'help>, + raw_vals: Vec, + matcher: &mut ArgMatcher, + ) -> ClapResult<()> { + debug!("Parser::push_arg_values: {:?}", raw_vals); + + for raw_val in raw_vals { + // update the current index because each value is a distinct index to clap + self.cur_idx.set(self.cur_idx.get() + 1); + debug!( + "Parser::add_single_val_to_arg: cur_idx:={}", + self.cur_idx.get() + ); + let value_parser = arg.get_value_parser(); + let val = value_parser.parse_ref(self.cmd, Some(arg), &raw_val)?; + + // Increment or create the group "args" + for group in self.cmd.groups_for_arg(&arg.id) { + matcher.add_val_to(&group, val.clone(), raw_val.clone()); + } + + matcher.add_val_to(&arg.id, val, raw_val); + matcher.add_index_to(&arg.id, self.cur_idx.get()); + } + + Ok(()) + } + + fn resolve_pending(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { + let pending = match matcher.take_pending() { + Some(pending) => pending, + None => { + return Ok(()); + } + }; + + debug!("Parser::resolve_pending: id={:?}", pending.id); + let arg = self.cmd.find(&pending.id).expect(INTERNAL_ERROR_MSG); + let _ = self.react( + pending.ident, + ValueSource::CommandLine, + arg, + pending.raw_vals, + matcher, + )?; + + Ok(()) + } + + fn react( + &self, + ident: Option, + source: ValueSource, + arg: &Arg<'help>, + raw_vals: Vec, + matcher: &mut ArgMatcher, + ) -> ClapResult { + self.resolve_pending(matcher)?; + + debug!( + "Parser::react action={:?}, identifier={:?}, source={:?}", + arg.get_action(), + ident, + source + ); + match arg.get_action() { + ArgAction::Set => { + if source == ValueSource::CommandLine + && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) + { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { + debug!( + "Parser::react not enough values passed in, leaving it to the validator to complain", + ); + } + Ok(ParseResult::ValuesDone) + } + ArgAction::Append => { + if source == ValueSource::CommandLine + && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) + { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { + debug!( + "Parser::react not enough values passed in, leaving it to the validator to complain", + ); + } + Ok(ParseResult::ValuesDone) + } + #[allow(deprecated)] + ArgAction::StoreValue => { + if ident == Some(Identifier::Index) + && arg.is_multiple_values_set() + && matcher.contains(&arg.id) + { + // HACK: Reuse existing occurrence + } else if source == ValueSource::CommandLine { + if matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + self.start_occurrence_of_arg(matcher, arg); + } else { + self.start_custom_arg(matcher, arg, source); + } + self.push_arg_values(arg, raw_vals, matcher)?; + if ident == Some(Identifier::Index) && arg.is_multiple_values_set() { + // HACK: Maintain existing occurrence behavior + let matched = matcher.get_mut(&arg.id).unwrap(); + #[allow(deprecated)] + matched.set_occurrences(matched.num_vals() as u64); + } + if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { + debug!( + "Parser::react not enough values passed in, leaving it to the validator to complain", + ); + } + Ok(ParseResult::ValuesDone) + } + #[allow(deprecated)] + ArgAction::IncOccurrence => { + debug_assert_eq!(raw_vals, Vec::::new()); + if source == ValueSource::CommandLine { + if matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + self.start_occurrence_of_arg(matcher, arg); + } else { + self.start_custom_arg(matcher, arg, source); + } + matcher.add_index_to(&arg.id, self.cur_idx.get()); + Ok(ParseResult::ValuesDone) + } + ArgAction::SetTrue => { + let raw_vals = match raw_vals.len() { + 0 => { + vec![OsString::from("true")] + } + 1 => raw_vals, + _ => { + debug!("Parser::react ignoring trailing values: {:?}", raw_vals); + let mut raw_vals = raw_vals; + raw_vals.resize(1, Default::default()); + raw_vals + } + }; + + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + Ok(ParseResult::ValuesDone) + } + ArgAction::SetFalse => { + let raw_vals = match raw_vals.len() { + 0 => { + vec![OsString::from("false")] + } + 1 => raw_vals, + _ => { + debug!("Parser::react ignoring trailing values: {:?}", raw_vals); + let mut raw_vals = raw_vals; + raw_vals.resize(1, Default::default()); + raw_vals + } + }; + + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + Ok(ParseResult::ValuesDone) + } + ArgAction::Count => { + let raw_vals = match raw_vals.len() { + 0 => { + let existing_value = *matcher + .get_one::(arg.get_id()) + .unwrap_or(&0); + let next_value = existing_value.saturating_add(1); + vec![OsString::from(next_value.to_string())] + } + 1 => raw_vals, + _ => { + debug!("Parser::react ignoring trailing values: {:?}", raw_vals); + let mut raw_vals = raw_vals; + raw_vals.resize(1, Default::default()); + raw_vals + } + }; + + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + Ok(ParseResult::ValuesDone) + } + ArgAction::Help => { + debug_assert_eq!(raw_vals, Vec::::new()); + let use_long = match ident { + Some(Identifier::Long) => true, + Some(Identifier::Short) => false, + Some(Identifier::Index) => true, + None => true, + }; + debug!("Help: use_long={}", use_long); + Err(self.help_err(use_long, Stream::Stdout)) + } + ArgAction::Version => { + debug_assert_eq!(raw_vals, Vec::::new()); + let use_long = match ident { + Some(Identifier::Long) => true, + Some(Identifier::Short) => false, + Some(Identifier::Index) => true, + None => true, + }; + debug!("Version: use_long={}", use_long); + Err(self.version_err(use_long)) + } + } + } + + fn remove_overrides(&self, arg: &Arg<'help>, matcher: &mut ArgMatcher) { + debug!("Parser::remove_overrides: id={:?}", arg.id); + for override_id in &arg.overrides { + debug!("Parser::remove_overrides:iter:{:?}: removing", override_id); + matcher.remove(override_id); + } + + // Override anything that can override us + let mut transitive = Vec::new(); + for arg_id in matcher.arg_ids() { + if let Some(overrider) = self.cmd.find(arg_id) { + if overrider.overrides.contains(&arg.id) { + transitive.push(&overrider.id); + } + } + } + for overrider_id in transitive { + debug!("Parser::remove_overrides:iter:{:?}: removing", overrider_id); + matcher.remove(overrider_id); + } + } + + #[cfg(feature = "env")] + fn add_env(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> { + debug!("Parser::add_env"); + use crate::util::str_to_bool; + + let trailing_values = false; // defaults are independent of the commandline + for arg in self.cmd.get_arguments() { + // Use env only if the arg was absent among command line args, + // early return if this is not the case. + if matcher.contains(&arg.id) { + debug!("Parser::add_env: Skipping existing arg `{}`", arg); + continue; + } + + debug!("Parser::add_env: Checking arg `{}`", arg); + if let Some((_, Some(ref val))) = arg.env { + let val = RawOsStr::new(val); + + if arg.is_takes_value_set() { + debug!( + "Parser::add_env: Found an opt with value={:?}, trailing={:?}", + val, trailing_values + ); + let mut arg_values = Vec::new(); + let _parse_result = + self.split_arg_values(arg, &val, trailing_values, &mut arg_values); + let _ = self.react(None, ValueSource::EnvVariable, arg, arg_values, matcher)?; + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_env: Ignoring state {:?}; env variables are outside of the parse loop", _parse_result); + } + } + } else { + match arg.get_action() { + #[allow(deprecated)] + ArgAction::StoreValue => unreachable!("{:?} is not a flag", arg.get_id()), + #[allow(deprecated)] + ArgAction::IncOccurrence => { + debug!("Parser::add_env: Found a flag with value `{:?}`", val); + let predicate = str_to_bool(val.to_str_lossy()); + debug!("Parser::add_env: Found boolean literal `{:?}`", predicate); + if predicate.unwrap_or(true) { + let _ = self.react( + None, + ValueSource::EnvVariable, + arg, + vec![], + matcher, + )?; + } + } + ArgAction::Set + | ArgAction::Append + | ArgAction::SetTrue + | ArgAction::SetFalse + | ArgAction::Count => { + let mut arg_values = Vec::new(); + let _parse_result = + self.split_arg_values(arg, &val, trailing_values, &mut arg_values); + let _ = self.react( + None, + ValueSource::EnvVariable, + arg, + arg_values, + matcher, + )?; + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_env: Ignoring state {:?}; env variables are outside of the parse loop", _parse_result); + } + } + } + // Early return on `Help` or `Version`. + ArgAction::Help | ArgAction::Version => { + let _ = + self.react(None, ValueSource::EnvVariable, arg, vec![], matcher)?; + } + } + } + } + } + + Ok(()) + } + + fn add_defaults(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { + debug!("Parser::add_defaults"); + + for arg in self.cmd.get_arguments() { + debug!("Parser::add_defaults:iter:{}:", arg.name); + self.add_default_value(arg, matcher)?; + } + + Ok(()) + } + + fn add_default_value(&self, arg: &Arg<'help>, matcher: &mut ArgMatcher) -> ClapResult<()> { + let trailing_values = false; // defaults are independent of the commandline + + if !arg.default_missing_vals.is_empty() { + debug!( + "Parser::add_default_value:iter:{}: has default missing vals", + arg.name + ); + match matcher.get(&arg.id) { + Some(ma) if ma.all_val_groups_empty() => { + debug!( + "Parser::add_default_value:iter:{}: has no user defined vals", + arg.name + ); + // The flag occurred, we just want to add the val groups + let mut arg_values = Vec::new(); + for v in arg.default_missing_vals.iter() { + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(v), + trailing_values, + &mut arg_values, + ); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_default_value: Ignoring state {:?}; defaults are outside of the parse loop", _parse_result); + } + } + } + self.start_custom_arg(matcher, arg, ValueSource::CommandLine); + self.push_arg_values(arg, arg_values, matcher)?; + } + None => { + debug!("Parser::add_default_value:iter:{}: wasn't used", arg.name); + // do nothing + } + _ => { + debug!( + "Parser::add_default_value:iter:{}: has user defined vals", + arg.name + ); + // do nothing + } + } + } else { + debug!( + "Parser::add_default_value:iter:{}: doesn't have default missing vals", + arg.name + ); + // do nothing + } + + if !arg.default_vals_ifs.is_empty() { + debug!("Parser::add_default_value: has conditional defaults"); + if !matcher.contains(&arg.id) { + for (id, val, default) in arg.default_vals_ifs.iter() { + let add = if let Some(a) = matcher.get(id) { + match val { + crate::builder::ArgPredicate::Equals(v) => { + a.raw_vals_flatten().any(|value| v == value) + } + crate::builder::ArgPredicate::IsPresent => true, + } + } else { + false + }; + + if add { + if let Some(default) = default { + let mut arg_values = Vec::new(); + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(default), + trailing_values, + &mut arg_values, + ); + let _ = self.react( + None, + ValueSource::DefaultValue, + arg, + arg_values, + matcher, + )?; + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_default_value: Ignoring state {:?}; defaults are outside of the parse loop", _parse_result); + } + } + } + return Ok(()); + } + } + } + } else { + debug!("Parser::add_default_value: doesn't have conditional defaults"); + } + + if !arg.default_vals.is_empty() { + debug!( + "Parser::add_default_value:iter:{}: has default vals", + arg.name + ); + if matcher.contains(&arg.id) { + debug!("Parser::add_default_value:iter:{}: was used", arg.name); + // do nothing + } else { + debug!("Parser::add_default_value:iter:{}: wasn't used", arg.name); + let mut arg_values = Vec::new(); + for v in arg.default_vals.iter() { + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(v), + trailing_values, + &mut arg_values, + ); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_default_value: Ignoring state {:?}; defaults are outside of the parse loop", _parse_result); + } + } + } + let _ = self.react(None, ValueSource::DefaultValue, arg, arg_values, matcher)?; + } + } else { + debug!( + "Parser::add_default_value:iter:{}: doesn't have default vals", + arg.name + ); + + // do nothing + } + + Ok(()) + } + + fn start_custom_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>, source: ValueSource) { + if source == ValueSource::CommandLine { + // With each new occurrence, remove overrides from prior occurrences + self.remove_overrides(arg, matcher); + } + matcher.start_custom_arg(arg, source); + for group in self.cmd.groups_for_arg(&arg.id) { + matcher.start_custom_group(&group, source); + } + } + + /// Increase occurrence of specific argument and the grouped arg it's in. + fn start_occurrence_of_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>) { + // With each new occurrence, remove overrides from prior occurrences + self.remove_overrides(arg, matcher); + + matcher.start_occurrence_of_arg(arg); + // Increment or create the group "args" + for group in self.cmd.groups_for_arg(&arg.id) { + matcher.start_occurrence_of_group(&group); + } + } +} + +// Error, Help, and Version Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + /// Is only used for the long flag(which is the only one needs fuzzy searching) + fn did_you_mean_error( + &mut self, + arg: &str, + matcher: &mut ArgMatcher, + remaining_args: &[&str], + ) -> ClapError { + debug!("Parser::did_you_mean_error: arg={}", arg); + // Didn't match a flag or option + let longs = self + .cmd + .get_keymap() + .keys() + .filter_map(|x| match x { + KeyType::Long(l) => Some(l.to_string_lossy().into_owned()), + _ => None, + }) + .collect::>(); + debug!("Parser::did_you_mean_error: longs={:?}", longs); + + let did_you_mean = suggestions::did_you_mean_flag( + arg, + remaining_args, + longs.iter().map(|x| &x[..]), + self.cmd.get_subcommands_mut(), + ); + + // Add the arg to the matches to build a proper usage string + if let Some((name, _)) = did_you_mean.as_ref() { + if let Some(arg) = self.cmd.get_keymap().get(&name.as_ref()) { + self.start_occurrence_of_arg(matcher, arg); + } + } + + let required = self.cmd.required_graph(); + let used: Vec = matcher + .arg_ids() + .filter(|arg_id| { + matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + }) + .filter(|n| self.cmd.find(n).map_or(true, |a| !a.is_hide_set())) + .cloned() + .collect(); + + ClapError::unknown_argument( + self.cmd, + format!("--{}", arg), + did_you_mean, + Usage::new(self.cmd) + .required(&required) + .create_usage_with_title(&*used), + ) + } + + fn help_err(&self, use_long: bool, stream: Stream) -> ClapError { + match self.cmd.write_help_err(use_long, stream) { + Ok(c) => ClapError::display_help(self.cmd, c), + Err(e) => e, + } + } + + fn version_err(&self, use_long: bool) -> ClapError { + debug!("Parser::version_err"); + + let msg = self.cmd._render_version(use_long); + let mut c = Colorizer::new(Stream::Stdout, self.cmd.color_help()); + c.none(msg); + ClapError::display_version(self.cmd, c) + } +} + +// Query Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + pub(crate) fn is_set(&self, s: AS) -> bool { + self.cmd.is_set(s) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum ParseState { + ValuesDone, + Opt(Id), + Pos(Id), +} + +/// Recoverable Parsing results. +#[derive(Debug, PartialEq, Clone)] +#[must_use] +enum ParseResult { + FlagSubCommand(String), + Opt(Id), + ValuesDone, + /// Value attached to the short flag is not consumed(e.g. 'u' for `-cu` is + /// not consumed). + AttachedValueNotConsumed, + /// This long flag doesn't need a value but is provided one. + UnneededAttachedValue { + rest: String, + used: Vec, + arg: String, + }, + /// This flag might be an hyphen Value. + MaybeHyphenValue, + /// Equals required but not provided. + EqualsNotProvided { + arg: String, + }, + /// Failed to match a Arg. + NoMatchingArg { + arg: String, + }, + /// No argument found e.g. parser is given `-` when parsing a flag. + NoArg, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct PendingArg { + pub(crate) id: Id, + pub(crate) ident: Option, + pub(crate) raw_vals: Vec, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Identifier { + Short, + Long, + Index, +} diff --git a/vendor/clap/src/parser/validator.rs b/vendor/clap/src/parser/validator.rs new file mode 100644 index 000000000..ebf2b234d --- /dev/null +++ b/vendor/clap/src/parser/validator.rs @@ -0,0 +1,692 @@ +// Internal +use crate::builder::{AppSettings, Arg, ArgPredicate, Command, PossibleValue}; +use crate::error::{Error, Result as ClapResult}; +use crate::output::fmt::Stream; +use crate::output::Usage; +use crate::parser::{ArgMatcher, MatchedArg, ParseState}; +use crate::util::ChildGraph; +use crate::util::Id; +use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8}; + +pub(crate) struct Validator<'help, 'cmd> { + cmd: &'cmd Command<'help>, + required: ChildGraph, +} + +impl<'help, 'cmd> Validator<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd Command<'help>) -> Self { + let required = cmd.required_graph(); + Validator { cmd, required } + } + + pub(crate) fn validate( + &mut self, + parse_state: ParseState, + matcher: &mut ArgMatcher, + ) -> ClapResult<()> { + debug!("Validator::validate"); + let mut conflicts = Conflicts::new(); + let has_subcmd = matcher.subcommand_name().is_some(); + + if let ParseState::Opt(a) = parse_state { + debug!("Validator::validate: needs_val_of={:?}", a); + + let o = &self.cmd[&a]; + let should_err = if let Some(v) = matcher.args.get(&o.id) { + v.all_val_groups_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0) + } else { + true + }; + if should_err { + return Err(Error::empty_value( + self.cmd, + &get_possible_values(o) + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), + o.to_string(), + )); + } + } + + if !has_subcmd && self.cmd.is_arg_required_else_help_set() { + let num_user_values = matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .count(); + if num_user_values == 0 { + let message = self.cmd.write_help_err(false, Stream::Stderr)?; + return Err(Error::display_help_error(self.cmd, message)); + } + } + #[allow(deprecated)] + if !has_subcmd && self.cmd.is_subcommand_required_set() { + let bn = self + .cmd + .get_bin_name() + .unwrap_or_else(|| self.cmd.get_name()); + return Err(Error::missing_subcommand( + self.cmd, + bn.to_string(), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } else if !has_subcmd && self.cmd.is_set(AppSettings::SubcommandRequiredElseHelp) { + debug!("Validator::new::get_matches_with: SubcommandRequiredElseHelp=true"); + let message = self.cmd.write_help_err(false, Stream::Stderr)?; + return Err(Error::display_help_error(self.cmd, message)); + } + + self.validate_conflicts(matcher, &mut conflicts)?; + if !(self.cmd.is_subcommand_negates_reqs_set() && has_subcmd) { + self.validate_required(matcher, &mut conflicts)?; + } + self.validate_matched_args(matcher)?; + + Ok(()) + } + + fn validate_arg_values(&self, arg: &Arg, ma: &MatchedArg) -> ClapResult<()> { + debug!("Validator::validate_arg_values: arg={:?}", arg.name); + for val in ma.raw_vals_flatten() { + if !arg.possible_vals.is_empty() { + debug!( + "Validator::validate_arg_values: possible_vals={:?}", + arg.possible_vals + ); + let val_str = val.to_string_lossy(); + let ok = arg + .possible_vals + .iter() + .any(|pv| pv.matches(&val_str, arg.is_ignore_case_set())); + if !ok { + return Err(Error::invalid_value( + self.cmd, + val_str.into_owned(), + &arg.possible_vals + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), + arg.to_string(), + )); + } + } + { + #![allow(deprecated)] + if arg.is_forbid_empty_values_set() && val.is_empty() { + debug!("Validator::validate_arg_values: illegal empty val found"); + return Err(Error::empty_value( + self.cmd, + &get_possible_values(arg) + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), + arg.to_string(), + )); + } + } + + if let Some(ref vtor) = arg.validator { + debug!("Validator::validate_arg_values: checking validator..."); + let mut vtor = vtor.lock().unwrap(); + if let Err(e) = vtor(&*val.to_string_lossy()) { + debug!("error"); + return Err(Error::value_validation( + arg.to_string(), + val.to_string_lossy().into_owned(), + e, + ) + .with_cmd(self.cmd)); + } else { + debug!("good"); + } + } + if let Some(ref vtor) = arg.validator_os { + debug!("Validator::validate_arg_values: checking validator_os..."); + let mut vtor = vtor.lock().unwrap(); + if let Err(e) = vtor(val) { + debug!("error"); + return Err(Error::value_validation( + arg.to_string(), + val.to_string_lossy().into(), + e, + ) + .with_cmd(self.cmd)); + } else { + debug!("good"); + } + } + } + Ok(()) + } + + fn validate_conflicts( + &mut self, + matcher: &ArgMatcher, + conflicts: &mut Conflicts, + ) -> ClapResult<()> { + debug!("Validator::validate_conflicts"); + + self.validate_exclusive(matcher)?; + + for arg_id in matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| self.cmd.find(arg_id).is_some()) + { + debug!("Validator::validate_conflicts::iter: id={:?}", arg_id); + let conflicts = conflicts.gather_conflicts(self.cmd, matcher, arg_id); + self.build_conflict_err(arg_id, &conflicts, matcher)?; + } + + Ok(()) + } + + fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> { + debug!("Validator::validate_exclusive"); + let args_count = matcher + .arg_ids() + .filter(|arg_id| { + matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + }) + .count(); + if args_count <= 1 { + // Nothing present to conflict with + return Ok(()); + } + + matcher + .arg_ids() + .filter(|arg_id| { + matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + }) + .filter_map(|name| { + debug!("Validator::validate_exclusive:iter:{:?}", name); + self.cmd + .find(name) + // Find `arg`s which are exclusive but also appear with other args. + .filter(|&arg| arg.is_exclusive_set() && args_count > 1) + }) + // Throw an error for the first conflict found. + .try_for_each(|arg| { + Err(Error::argument_conflict( + self.cmd, + arg.to_string(), + Vec::new(), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )) + }) + } + + fn build_conflict_err( + &self, + name: &Id, + conflict_ids: &[Id], + matcher: &ArgMatcher, + ) -> ClapResult<()> { + if conflict_ids.is_empty() { + return Ok(()); + } + + debug!("Validator::build_conflict_err: name={:?}", name); + let mut seen = std::collections::HashSet::new(); + let conflicts = conflict_ids + .iter() + .flat_map(|c_id| { + if self.cmd.find_group(c_id).is_some() { + self.cmd.unroll_args_in_group(c_id) + } else { + vec![c_id.clone()] + } + }) + .filter_map(|c_id| { + seen.insert(c_id.clone()).then(|| { + let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG); + c_arg.to_string() + }) + }) + .collect(); + + let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG); + let usg = self.build_conflict_err_usage(matcher, conflict_ids); + Err(Error::argument_conflict( + self.cmd, + former_arg.to_string(), + conflicts, + usg, + )) + } + + fn build_conflict_err_usage(&self, matcher: &ArgMatcher, conflicting_keys: &[Id]) -> String { + let used_filtered: Vec = matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|n| { + // Filter out the args we don't want to specify. + self.cmd.find(n).map_or(true, |a| !a.is_hide_set()) + }) + .filter(|key| !conflicting_keys.contains(key)) + .cloned() + .collect(); + let required: Vec = used_filtered + .iter() + .filter_map(|key| self.cmd.find(key)) + .flat_map(|arg| arg.requires.iter().map(|item| &item.1)) + .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key)) + .chain(used_filtered.iter()) + .cloned() + .collect(); + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&required) + } + + fn gather_requires(&mut self, matcher: &ArgMatcher) { + debug!("Validator::gather_requires"); + for name in matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + { + debug!("Validator::gather_requires:iter:{:?}", name); + if let Some(arg) = self.cmd.find(name) { + let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option { + let required = matcher.check_explicit(&arg.id, *val); + required.then(|| req_arg.clone()) + }; + + for req in self.cmd.unroll_arg_requires(is_relevant, &arg.id) { + self.required.insert(req); + } + } else if let Some(g) = self.cmd.find_group(name) { + debug!("Validator::gather_requires:iter:{:?}:group", name); + for r in &g.requires { + self.required.insert(r.clone()); + } + } + } + } + + fn validate_matched_args(&self, matcher: &ArgMatcher) -> ClapResult<()> { + debug!("Validator::validate_matched_args"); + matcher.iter().try_for_each(|(name, ma)| { + debug!( + "Validator::validate_matched_args:iter:{:?}: vals={:#?}", + name, + ma.vals_flatten() + ); + if let Some(arg) = self.cmd.find(name) { + self.validate_arg_num_vals(arg, ma)?; + self.validate_arg_values(arg, ma)?; + self.validate_arg_num_occurs(arg, ma)?; + } + Ok(()) + }) + } + + fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { + #![allow(deprecated)] + debug!( + "Validator::validate_arg_num_occurs: {:?}={}", + a.name, + ma.get_occurrences() + ); + // Occurrence of positional argument equals to number of values rather + // than number of grouped values. + if ma.get_occurrences() > 1 && !a.is_multiple_occurrences_set() && !a.is_positional() { + // Not the first time, and we don't allow multiples + return Err(Error::unexpected_multiple_usage( + self.cmd, + a.to_string(), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } + if let Some(max_occurs) = a.max_occurs { + debug!( + "Validator::validate_arg_num_occurs: max_occurs set...{}", + max_occurs + ); + let occurs = ma.get_occurrences() as usize; + if occurs > max_occurs { + return Err(Error::too_many_occurrences( + self.cmd, + a.to_string(), + max_occurs, + occurs, + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } + } + + Ok(()) + } + + fn validate_arg_num_vals(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { + debug!("Validator::validate_arg_num_vals"); + if let Some(num) = a.num_vals { + let total_num = ma.num_vals(); + debug!("Validator::validate_arg_num_vals: num_vals set...{}", num); + #[allow(deprecated)] + let should_err = if a.is_multiple_occurrences_set() { + total_num % num != 0 + } else { + num != total_num + }; + if should_err { + debug!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues"); + return Err(Error::wrong_number_of_values( + self.cmd, + a.to_string(), + num, + #[allow(deprecated)] + if a.is_multiple_occurrences_set() { + total_num % num + } else { + total_num + }, + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } + } + if let Some(num) = a.max_vals { + debug!("Validator::validate_arg_num_vals: max_vals set...{}", num); + if ma.num_vals() > num { + debug!("Validator::validate_arg_num_vals: Sending error TooManyValues"); + return Err(Error::too_many_values( + self.cmd, + ma.raw_vals_flatten() + .last() + .expect(INTERNAL_ERROR_MSG) + .to_str() + .expect(INVALID_UTF8) + .to_string(), + a.to_string(), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } + } + let min_vals_zero = if let Some(num) = a.min_vals { + debug!("Validator::validate_arg_num_vals: min_vals set: {}", num); + if ma.num_vals() < num && num != 0 { + debug!("Validator::validate_arg_num_vals: Sending error TooFewValues"); + return Err(Error::too_few_values( + self.cmd, + a.to_string(), + num, + ma.num_vals(), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } + 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.is_takes_value_set() && !min_vals_zero && ma.all_val_groups_empty() { + return Err(Error::empty_value( + self.cmd, + &get_possible_values(a) + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), + a.to_string(), + )); + } + Ok(()) + } + + fn validate_required( + &mut self, + matcher: &ArgMatcher, + conflicts: &mut Conflicts, + ) -> ClapResult<()> { + debug!("Validator::validate_required: required={:?}", self.required); + self.gather_requires(matcher); + + let is_exclusive_present = matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .any(|id| { + self.cmd + .find(id) + .map(|arg| arg.is_exclusive_set()) + .unwrap_or_default() + }); + debug!( + "Validator::validate_required: is_exclusive_present={}", + is_exclusive_present + ); + + for arg_or_group in self + .required + .iter() + .filter(|r| !matcher.check_explicit(r, ArgPredicate::IsPresent)) + { + debug!("Validator::validate_required:iter:aog={:?}", arg_or_group); + if let Some(arg) = self.cmd.find(arg_or_group) { + debug!("Validator::validate_required:iter: This is an arg"); + if !is_exclusive_present && !self.is_missing_required_ok(arg, matcher, conflicts) { + return self.missing_required_error(matcher, vec![]); + } + } else if let Some(group) = self.cmd.find_group(arg_or_group) { + debug!("Validator::validate_required:iter: This is a group"); + if !self + .cmd + .unroll_args_in_group(&group.id) + .iter() + .any(|a| matcher.check_explicit(a, ArgPredicate::IsPresent)) + { + return self.missing_required_error(matcher, vec![]); + } + } + } + + // Validate the conditionally required args + for a in self.cmd.get_arguments() { + for (other, val) in &a.r_ifs { + if matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) + && !matcher.check_explicit(&a.id, ArgPredicate::IsPresent) + { + return self.missing_required_error(matcher, vec![a.id.clone()]); + } + } + + let match_all = a.r_ifs_all.iter().all(|(other, val)| { + matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) + }); + if match_all + && !a.r_ifs_all.is_empty() + && !matcher.check_explicit(&a.id, ArgPredicate::IsPresent) + { + return self.missing_required_error(matcher, vec![a.id.clone()]); + } + } + + self.validate_required_unless(matcher)?; + + Ok(()) + } + + fn is_missing_required_ok( + &self, + a: &Arg<'help>, + matcher: &ArgMatcher, + conflicts: &mut Conflicts, + ) -> bool { + debug!("Validator::is_missing_required_ok: {}", a.name); + let conflicts = conflicts.gather_conflicts(self.cmd, matcher, &a.id); + !conflicts.is_empty() + } + + fn validate_required_unless(&self, matcher: &ArgMatcher) -> ClapResult<()> { + debug!("Validator::validate_required_unless"); + let failed_args: Vec<_> = self + .cmd + .get_arguments() + .filter(|&a| { + (!a.r_unless.is_empty() || !a.r_unless_all.is_empty()) + && !matcher.check_explicit(&a.id, ArgPredicate::IsPresent) + && self.fails_arg_required_unless(a, matcher) + }) + .map(|a| a.id.clone()) + .collect(); + if failed_args.is_empty() { + Ok(()) + } else { + self.missing_required_error(matcher, failed_args) + } + } + + // Failing a required unless means, the arg's "unless" wasn't present, and neither were they + fn fails_arg_required_unless(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { + debug!("Validator::fails_arg_required_unless: a={:?}", a.name); + let exists = |id| matcher.check_explicit(id, ArgPredicate::IsPresent); + + (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists)) + && !a.r_unless.iter().any(exists) + } + + // `incl`: an arg to include in the error even if not used + fn missing_required_error(&self, matcher: &ArgMatcher, incl: Vec) -> ClapResult<()> { + debug!("Validator::missing_required_error; incl={:?}", incl); + debug!( + "Validator::missing_required_error: reqs={:?}", + self.required + ); + + let usg = Usage::new(self.cmd).required(&self.required); + + let req_args = usg + .get_required_usage_from(&incl, Some(matcher), true) + .into_iter() + .collect::>(); + + debug!( + "Validator::missing_required_error: req_args={:#?}", + req_args + ); + + let used: Vec = matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|n| { + // Filter out the args we don't want to specify. + self.cmd.find(n).map_or(true, |a| !a.is_hide_set()) + }) + .cloned() + .chain(incl) + .collect(); + + Err(Error::missing_required_argument( + self.cmd, + req_args, + usg.create_usage_with_title(&used), + )) + } +} + +#[derive(Default, Clone, Debug)] +struct Conflicts { + potential: std::collections::HashMap>, +} + +impl Conflicts { + fn new() -> Self { + Self::default() + } + + fn gather_conflicts(&mut self, cmd: &Command, matcher: &ArgMatcher, arg_id: &Id) -> Vec { + debug!("Conflicts::gather_conflicts: arg={:?}", arg_id); + let mut conflicts = Vec::new(); + for other_arg_id in matcher + .arg_ids() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + { + if arg_id == other_arg_id { + continue; + } + + if self + .gather_direct_conflicts(cmd, arg_id) + .contains(other_arg_id) + { + conflicts.push(other_arg_id.clone()); + } + if self + .gather_direct_conflicts(cmd, other_arg_id) + .contains(arg_id) + { + conflicts.push(other_arg_id.clone()); + } + } + debug!("Conflicts::gather_conflicts: conflicts={:?}", conflicts); + conflicts + } + + fn gather_direct_conflicts(&mut self, cmd: &Command, arg_id: &Id) -> &[Id] { + self.potential.entry(arg_id.clone()).or_insert_with(|| { + let conf = if let Some(arg) = cmd.find(arg_id) { + let mut conf = arg.blacklist.clone(); + for group_id in cmd.groups_for_arg(arg_id) { + let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG); + conf.extend(group.conflicts.iter().cloned()); + if !group.multiple { + for member_id in &group.args { + if member_id != arg_id { + conf.push(member_id.clone()); + } + } + } + } + + // Overrides are implicitly conflicts + conf.extend(arg.overrides.iter().cloned()); + + conf + } else if let Some(group) = cmd.find_group(arg_id) { + group.conflicts.clone() + } else { + debug_assert!(false, "id={:?} is unknown", arg_id); + Vec::new() + }; + debug!( + "Conflicts::gather_direct_conflicts id={:?}, conflicts={:?}", + arg_id, conf + ); + conf + }) + } +} + +fn get_possible_values<'help>(a: &Arg<'help>) -> Vec> { + #![allow(deprecated)] + if !a.is_takes_value_set() { + vec![] + } else if let Some(pvs) = a.get_possible_values() { + // Check old first in case the user explicitly set possible values and the derive inferred + // a `ValueParser` with some. + pvs.to_vec() + } else { + a.get_value_parser() + .possible_values() + .map(|pvs| pvs.collect()) + .unwrap_or_default() + } +} -- cgit v1.2.3