use std::any::Any;
use std::ffi::{OsStr, OsString};
use std::fmt::Debug;
use std::iter::{Cloned, Flatten, Map};
use std::slice::Iter;
#[cfg(debug_assertions)]
use crate::builder::Str;
use crate::parser::AnyValue;
use crate::parser::AnyValueId;
use crate::parser::MatchedArg;
use crate::parser::MatchesError;
use crate::parser::ValueSource;
use crate::util::FlatMap;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ArgMatches {
#[cfg(debug_assertions)]
pub(crate) valid_args: Vec<Id>,
#[cfg(debug_assertions)]
pub(crate) valid_subcommands: Vec<Str>,
pub(crate) args: FlatMap<Id, MatchedArg>,
pub(crate) subcommand: Option<Box<SubCommand>>,
}
impl ArgMatches {
#[cfg_attr(debug_assertions, track_caller)]
pub fn get_one<T: Any + Clone + Send + Sync + 'static>(&self, id: &str) -> Option<&T> {
MatchesError::unwrap(id, self.try_get_one(id))
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn get_count(&self, id: &str) -> u8 {
*self
.get_one::<u8>(id)
.expect("ArgAction::Count is defaulted")
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn get_flag(&self, id: &str) -> bool {
*self
.get_one::<bool>(id)
.expect("ArgAction::SetTrue / ArgAction::SetFalse is defaulted")
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn get_many<T: Any + Clone + Send + Sync + 'static>(
&self,
id: &str,
) -> Option<ValuesRef<T>> {
MatchesError::unwrap(id, self.try_get_many(id))
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
#[cfg_attr(debug_assertions, track_caller)]
pub fn get_raw(&self, id: &str) -> Option<RawValues<'_>> {
MatchesError::unwrap(id, self.try_get_raw(id))
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn remove_one<T: Any + Clone + Send + Sync + 'static>(&mut self, id: &str) -> Option<T> {
MatchesError::unwrap(id, self.try_remove_one(id))
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn remove_many<T: Any + Clone + Send + Sync + 'static>(
&mut self,
id: &str,
) -> Option<Values<T>> {
MatchesError::unwrap(id, self.try_remove_many(id))
}
pub fn contains_id(&self, id: &str) -> bool {
MatchesError::unwrap(id, self.try_contains_id(id))
}
pub fn ids(&self) -> IdsRef<'_> {
IdsRef {
iter: self.args.keys(),
}
}
pub fn args_present(&self) -> bool {
!self.args.is_empty()
}
#[cfg(feature = "unstable-grouped")]
#[cfg_attr(debug_assertions, track_caller)]
pub fn grouped_values_of(&self, id: &str) -> Option<GroupedValues> {
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)
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn value_source(&self, id: &str) -> Option<ValueSource> {
let value = self.get_arg(id);
value.and_then(MatchedArg::source)
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn index_of(&self, id: &str) -> Option<usize> {
let arg = self.get_arg(id)?;
let i = arg.get_index(0)?;
Some(i)
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn indices_of(&self, id: &str) -> Option<Indices<'_>> {
let arg = self.get_arg(id)?;
let i = Indices {
iter: arg.indices(),
len: arg.num_vals(),
};
Some(i)
}
}
impl ArgMatches {
#[inline]
pub fn subcommand(&self) -> Option<(&str, &ArgMatches)> {
self.subcommand.as_ref().map(|sc| (&*sc.name, &sc.matches))
}
pub fn remove_subcommand(&mut self) -> Option<(String, ArgMatches)> {
self.subcommand.take().map(|sc| (sc.name, sc.matches))
}
pub fn subcommand_matches(&self, name: &str) -> Option<&ArgMatches> {
self.get_subcommand(name).map(|sc| &sc.matches)
}
#[inline]
pub fn subcommand_name(&self) -> Option<&str> {
self.subcommand.as_ref().map(|sc| &*sc.name)
}
#[inline]
#[doc(hidden)]
pub fn is_valid_subcommand(&self, _name: &str) -> bool {
#[cfg(debug_assertions)]
{
_name.is_empty() || self.valid_subcommands.iter().any(|s| *s == _name)
}
#[cfg(not(debug_assertions))]
{
true
}
}
}
impl ArgMatches {
pub fn try_get_one<T: Any + Clone + Send + Sync + 'static>(
&self,
id: &str,
) -> Result<Option<&T>, MatchesError> {
let arg = self.try_get_arg_t::<T>(id)?;
let value = match arg.and_then(|a| a.first()) {
Some(value) => value,
None => {
return Ok(None);
}
};
Ok(value
.downcast_ref::<T>()
.map(Some)
.expect(INTERNAL_ERROR_MSG)) }
pub fn try_get_many<T: Any + Clone + Send + Sync + 'static>(
&self,
id: &str,
) -> Result<Option<ValuesRef<T>>, MatchesError> {
let arg = match self.try_get_arg_t::<T>(id)? {
Some(arg) => arg,
None => return Ok(None),
};
let len = arg.num_vals();
let values = arg.vals_flatten();
let values = ValuesRef {
iter: values.map(|v| v.downcast_ref::<T>().expect(INTERNAL_ERROR_MSG)),
len,
};
Ok(Some(values))
}
pub fn try_get_raw(&self, id: &str) -> Result<Option<RawValues<'_>>, MatchesError> {
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))
}
pub fn try_remove_one<T: Any + Clone + Send + Sync + 'static>(
&mut self,
id: &str,
) -> Result<Option<T>, MatchesError> {
match self.try_remove_arg_t::<T>(id)? {
Some(values) => Ok(values
.into_vals_flatten()
.map(|v| v.downcast_into::<T>().expect(INTERNAL_ERROR_MSG))
.next()),
None => Ok(None),
}
}
pub fn try_remove_many<T: Any + Clone + Send + Sync + 'static>(
&mut self,
id: &str,
) -> Result<Option<Values<T>>, MatchesError> {
let arg = match self.try_remove_arg_t::<T>(id)? {
Some(arg) => arg,
None => return Ok(None),
};
let len = arg.num_vals();
let values = arg.into_vals_flatten();
let values = Values {
iter: values.map(|v| v.downcast_into::<T>().expect(INTERNAL_ERROR_MSG)),
len,
};
Ok(Some(values))
}
pub fn try_contains_id(&self, id: &str) -> Result<bool, MatchesError> {
self.verify_arg(id)?;
let presence = self.args.contains_key(id);
Ok(presence)
}
}
impl ArgMatches {
#[inline]
fn try_get_arg(&self, arg: &str) -> Result<Option<&MatchedArg>, MatchesError> {
self.verify_arg(arg)?;
Ok(self.args.get(arg))
}
#[inline]
fn try_get_arg_t<T: Any + Send + Sync + 'static>(
&self,
arg: &str,
) -> Result<Option<&MatchedArg>, MatchesError> {
let arg = match self.try_get_arg(arg)? {
Some(arg) => arg,
None => {
return Ok(None);
}
};
self.verify_arg_t::<T>(arg)?;
Ok(Some(arg))
}
#[inline]
fn try_remove_arg_t<T: Any + Send + Sync + 'static>(
&mut self,
arg: &str,
) -> Result<Option<MatchedArg>, MatchesError> {
self.verify_arg(arg)?;
let (id, matched) = match self.args.remove_entry(arg) {
Some((id, matched)) => (id, matched),
None => {
return Ok(None);
}
};
let expected = AnyValueId::of::<T>();
let actual = matched.infer_type_id(expected);
if actual == expected {
Ok(Some(matched))
} else {
self.args.insert(id, matched);
Err(MatchesError::Downcast { actual, expected })
}
}
fn verify_arg_t<T: Any + Send + Sync + 'static>(
&self,
arg: &MatchedArg,
) -> Result<(), MatchesError> {
let expected = AnyValueId::of::<T>();
let actual = arg.infer_type_id(expected);
if expected == actual {
Ok(())
} else {
Err(MatchesError::Downcast { actual, expected })
}
}
#[inline]
fn verify_arg(&self, _arg: &str) -> Result<(), MatchesError> {
#[cfg(debug_assertions)]
{
if _arg == Id::EXTERNAL || self.valid_args.iter().any(|s| *s == _arg) {
} 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<'s>(&'s self, arg: &str) -> Option<&'s MatchedArg> {
#[cfg(debug_assertions)]
{
if arg == Id::EXTERNAL || self.valid_args.iter().any(|s| *s == 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, name: &str) -> Option<&SubCommand> {
#[cfg(debug_assertions)]
{
if name.is_empty() || self.valid_subcommands.iter().any(|s| *s == name) {
} else {
panic!("`{}` is not a name of a subcommand.", name);
}
}
if let Some(ref sc) = self.subcommand {
if sc.name == name {
return Some(sc);
}
}
None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SubCommand {
pub(crate) name: String,
pub(crate) matches: ArgMatches,
}
#[derive(Clone, Debug)]
pub struct IdsRef<'a> {
iter: std::slice::Iter<'a, Id>,
}
impl<'a> Iterator for IdsRef<'a> {
type Item = &'a Id;
fn next(&mut self) -> Option<&'a Id> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a> DoubleEndedIterator for IdsRef<'a> {
fn next_back(&mut self) -> Option<&'a Id> {
self.iter.next_back()
}
}
impl<'a> ExactSizeIterator for IdsRef<'a> {}
#[derive(Clone, Debug)]
pub struct Values<T> {
#[allow(clippy::type_complexity)]
iter: Map<Flatten<std::vec::IntoIter<Vec<AnyValue>>>, fn(AnyValue) -> T>,
len: usize,
}
impl<T> Iterator for Values<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<T> DoubleEndedIterator for Values<T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back()
}
}
impl<T> ExactSizeIterator for Values<T> {}
impl<T> Default for Values<T> {
fn default() -> Self {
let empty: Vec<Vec<AnyValue>> = Default::default();
Values {
iter: empty.into_iter().flatten().map(|_| unreachable!()),
len: 0,
}
}
}
#[derive(Clone, Debug)]
pub struct ValuesRef<'a, T> {
#[allow(clippy::type_complexity)]
iter: Map<Flatten<Iter<'a, Vec<AnyValue>>>, fn(&AnyValue) -> &T>,
len: usize,
}
impl<'a, T: 'a> Iterator for ValuesRef<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<'a, T: 'a> DoubleEndedIterator for ValuesRef<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back()
}
}
impl<'a, T: 'a> ExactSizeIterator for ValuesRef<'a, T> {}
impl<'a, T: 'a> Default for ValuesRef<'a, T> {
fn default() -> Self {
static EMPTY: [Vec<AnyValue>; 0] = [];
ValuesRef {
iter: EMPTY[..].iter().flatten().map(|_| unreachable!()),
len: 0,
}
}
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
#[derive(Clone, Debug)]
pub struct RawValues<'a> {
#[allow(clippy::type_complexity)]
iter: Map<Flatten<Iter<'a, Vec<OsString>>>, 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<usize>) {
(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> {}
impl Default for RawValues<'_> {
fn default() -> Self {
static EMPTY: [Vec<OsString>; 0] = [];
RawValues {
iter: EMPTY[..].iter().flatten().map(|_| unreachable!()),
len: 0,
}
}
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct GroupedValues<'a> {
#[allow(clippy::type_complexity)]
iter: Map<Iter<'a, Vec<AnyValue>>, fn(&Vec<AnyValue>) -> Vec<&str>>,
len: usize,
}
impl<'a> Iterator for GroupedValues<'a> {
type Item = Vec<&'a str>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<'a> DoubleEndedIterator for GroupedValues<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back()
}
}
impl<'a> ExactSizeIterator for GroupedValues<'a> {}
impl<'a> Default for GroupedValues<'a> {
fn default() -> Self {
static EMPTY: [Vec<AnyValue>; 0] = [];
GroupedValues {
iter: EMPTY[..].iter().map(|_| unreachable!()),
len: 0,
}
}
}
#[derive(Clone, Debug)]
pub struct Indices<'a> {
iter: Cloned<Iter<'a, usize>>,
len: usize,
}
impl<'a> Iterator for Indices<'a> {
type Item = usize;
fn next(&mut self) -> Option<usize> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<'a> DoubleEndedIterator for Indices<'a> {
fn next_back(&mut self) -> Option<usize> {
self.iter.next_back()
}
}
impl<'a> ExactSizeIterator for Indices<'a> {}
impl<'a> Default for Indices<'a> {
fn default() -> Self {
static EMPTY: [usize; 0] = [];
Indices {
iter: EMPTY[..].iter().cloned(),
len: 0,
}
}
}
#[cfg(feature = "unstable-grouped")]
#[cfg_attr(debug_assertions, track_caller)]
#[inline]
fn unwrap_string(value: &AnyValue) -> &str {
match value.downcast_ref::<String>() {
Some(value) => value,
None => {
panic!("Must use `_os` lookups with `Arg::allow_invalid_utf8`",)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ArgAction;
#[test]
fn check_auto_traits() {
static_assertions::assert_impl_all!(ArgMatches: Send, Sync, Unpin);
}
#[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")
.action(ArgAction::Set)
.num_args(1..)
.required(true),
)
.try_get_matches_from(["test", "one"])
.unwrap()
.get_many::<String>("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")
.action(ArgAction::Set)
.num_args(1..)
.value_parser(crate::builder::ValueParser::os_string())
.required(true),
)
.try_get_matches_from(["test", "one"])
.unwrap()
.get_many::<std::ffi::OsString>("POTATO")
.expect("present")
.count();
assert_eq!(l, 1);
}
#[test]
fn indices_exact_size() {
let l = crate::Command::new("test")
.arg(
crate::Arg::new("POTATO")
.action(ArgAction::Set)
.num_args(1..)
.required(true),
)
.try_get_matches_from(["test", "one"])
.unwrap()
.indices_of("POTATO")
.expect("present")
.len();
assert_eq!(l, 1);
}
}