use std::{
borrow::Cow,
ffi::{OsStr, OsString},
fmt::{Debug, Display},
iter::{Cloned, Flatten, Map},
slice::Iter,
str::FromStr,
};
use indexmap::IndexMap;
use crate::{
parse::MatchedArg,
util::{Id, Key},
{Error, INVALID_UTF8},
};
#[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<Id>,
pub(crate) args: IndexMap<Id, MatchedArg>,
pub(crate) subcommand: Option<Box<SubCommand>>,
}
impl ArgMatches {
pub fn value_of<T: Key>(&self, id: T) -> Option<&str> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_utf8_validation(arg, &id);
let v = arg.first()?;
Some(v.to_str().expect(INVALID_UTF8))
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn value_of_lossy<T: Key>(&self, id: T) -> Option<Cow<'_, str>> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_no_utf8_validation(arg, &id);
let v = arg.first()?;
Some(v.to_string_lossy())
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn value_of_os<T: Key>(&self, id: T) -> Option<&OsStr> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_no_utf8_validation(arg, &id);
let v = arg.first()?;
Some(v.as_os_str())
}
pub fn values_of<T: Key>(&self, id: T) -> Option<Values> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_utf8_validation(arg, &id);
fn to_str_slice(o: &OsString) -> &str {
o.to_str().expect(INVALID_UTF8)
}
let v = Values {
iter: arg.vals_flatten().map(to_str_slice),
};
Some(v)
}
#[cfg(feature = "unstable-grouped")]
pub fn grouped_values_of<T: Key>(&self, id: T) -> Option<GroupedValues> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_utf8_validation(arg, &id);
let v = GroupedValues {
iter: arg
.vals()
.map(|g| g.iter().map(|x| x.to_str().expect(INVALID_UTF8)).collect()),
};
Some(v)
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn values_of_lossy<T: Key>(&self, id: T) -> Option<Vec<String>> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_no_utf8_validation(arg, &id);
let v = arg
.vals_flatten()
.map(|v| v.to_string_lossy().into_owned())
.collect();
Some(v)
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn values_of_os<T: Key>(&self, id: T) -> Option<OsValues> {
let id = Id::from(id);
let arg = self.get_arg(&id)?;
assert_no_utf8_validation(arg, &id);
fn to_str_slice(o: &OsString) -> &OsStr {
o
}
let v = OsValues {
iter: arg.vals_flatten().map(to_str_slice),
};
Some(v)
}
pub fn value_of_t<R>(&self, name: &str) -> Result<R, Error>
where
R: FromStr,
<R as FromStr>::Err: Display,
{
let v = self
.value_of(name)
.ok_or_else(|| Error::argument_not_found_auto(name.to_string()))?;
v.parse::<R>().map_err(|e| {
let message = format!(
"The argument '{}' isn't a valid value for '{}': {}",
v, name, e
);
Error::value_validation_without_app(name.to_string(), v.to_string(), message.into())
})
}
pub fn value_of_t_or_exit<R>(&self, name: &str) -> R
where
R: FromStr,
<R as FromStr>::Err: Display,
{
self.value_of_t(name).unwrap_or_else(|e| e.exit())
}
pub fn values_of_t<R>(&self, name: &str) -> Result<Vec<R>, Error>
where
R: FromStr,
<R as FromStr>::Err: Display,
{
let v = self
.values_of(name)
.ok_or_else(|| Error::argument_not_found_auto(name.to_string()))?;
v.map(|v| {
v.parse::<R>().map_err(|e| {
let message = format!("The argument '{}' isn't a valid value: {}", v, e);
Error::value_validation_without_app(name.to_string(), v.to_string(), message.into())
})
})
.collect()
}
pub fn values_of_t_or_exit<R>(&self, name: &str) -> Vec<R>
where
R: FromStr,
<R as FromStr>::Err: Display,
{
self.values_of_t(name).unwrap_or_else(|e| e.exit())
}
pub fn is_present<T: Key>(&self, id: T) -> bool {
let id = Id::from(id);
#[cfg(debug_assertions)]
self.get_arg(&id);
self.args.contains_key(&id)
}
pub fn occurrences_of<T: Key>(&self, id: T) -> u64 {
self.get_arg(&Id::from(id)).map_or(0, |a| a.occurs)
}
pub fn index_of<T: Key>(&self, id: T) -> Option<usize> {
let arg = self.get_arg(&Id::from(id))?;
let i = arg.get_index(0)?;
Some(i)
}
pub fn indices_of<T: Key>(&self, id: T) -> Option<Indices<'_>> {
let arg = self.get_arg(&Id::from(id))?;
let i = Indices {
iter: arg.indices(),
};
Some(i)
}
#[inline]
pub fn subcommand(&self) -> Option<(&str, &ArgMatches)> {
self.subcommand.as_ref().map(|sc| (&*sc.name, &sc.matches))
}
pub fn subcommand_matches<T: Key>(&self, id: T) -> Option<&ArgMatches> {
self.get_subcommand(&id.into()).map(|sc| &sc.matches)
}
#[inline]
pub fn subcommand_name(&self) -> Option<&str> {
self.subcommand.as_ref().map(|sc| &*sc.name)
}
}
impl ArgMatches {
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
fn get_arg(&self, arg: &Id) -> Option<&MatchedArg> {
#[cfg(debug_assertions)]
{
if *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 a name 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 *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,
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct Values<'a> {
#[allow(clippy::type_complexity)]
iter: Map<Flatten<Iter<'a, Vec<OsString>>>, for<'r> fn(&'r OsString) -> &'r str>,
}
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<usize>) {
self.iter.size_hint()
}
}
impl<'a> DoubleEndedIterator for Values<'a> {
fn next_back(&mut self) -> Option<&'a str> {
self.iter.next_back()
}
}
impl<'a> ExactSizeIterator for Values<'a> {}
impl<'a> Default for Values<'a> {
fn default() -> Self {
static EMPTY: [Vec<OsString>; 0] = [];
Values {
iter: EMPTY[..].iter().flatten().map(|_| unreachable!()),
}
}
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct GroupedValues<'a> {
#[allow(clippy::type_complexity)]
iter: Map<Iter<'a, Vec<OsString>>, fn(&Vec<OsString>) -> Vec<&str>>,
}
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.iter.size_hint()
}
}
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<OsString>; 0] = [];
GroupedValues {
iter: EMPTY[..].iter().map(|_| unreachable!()),
}
}
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct OsValues<'a> {
#[allow(clippy::type_complexity)]
iter: Map<Flatten<Iter<'a, Vec<OsString>>>, fn(&OsString) -> &OsStr>,
}
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<usize>) {
self.iter.size_hint()
}
}
impl<'a> DoubleEndedIterator for OsValues<'a> {
fn next_back(&mut self) -> Option<&'a OsStr> {
self.iter.next_back()
}
}
impl<'a> ExactSizeIterator for OsValues<'a> {}
impl Default for OsValues<'_> {
fn default() -> Self {
static EMPTY: [Vec<OsString>; 0] = [];
OsValues {
iter: EMPTY[..].iter().flatten().map(|_| unreachable!()),
}
}
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct Indices<'a> {
iter: Cloned<Iter<'a, 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.iter.size_hint()
}
}
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(),
}
}
}
#[track_caller]
#[inline]
fn assert_utf8_validation(arg: &MatchedArg, id: &Id) {
debug_assert!(
matches!(arg.is_invalid_utf8_allowed(), None | Some(false)),
"Must use `_os` lookups with `Arg::allow_invalid_utf8` at `{:?}`",
id
);
}
#[track_caller]
#[inline]
fn assert_no_utf8_validation(arg: &MatchedArg, id: &Id) {
debug_assert!(
matches!(arg.is_invalid_utf8_allowed(), None | Some(true)),
"Must use `Arg::allow_invalid_utf8` with `_os` lookups at `{:?}`",
id
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_values() {
let mut values: Values = Values::default();
assert_eq!(values.next(), None);
}
#[test]
fn test_default_values_with_shorter_lifetime() {
let matches = ArgMatches::default();
let mut values = matches.values_of("").unwrap_or_default();
assert_eq!(values.next(), None);
}
#[test]
fn test_default_osvalues() {
let mut values: OsValues = OsValues::default();
assert_eq!(values.next(), None);
}
#[test]
fn test_default_osvalues_with_shorter_lifetime() {
let matches = ArgMatches::default();
let mut values = matches.values_of_os("").unwrap_or_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);
}
}