use std::{
borrow::Cow,
ffi::{OsStr, OsString},
fmt::{Debug, Display},
iter::{Cloned, Map},
slice::Iter,
str::FromStr,
};
use indexmap::IndexMap;
use crate::{
parse::MatchedArg,
util::{termcolor::ColorChoice, Id, Key},
{Error, INVALID_UTF8},
};
#[derive(Debug, Clone)]
pub(crate) struct SubCommand {
pub(crate) id: Id,
pub(crate) name: String,
pub(crate) matches: ArgMatches,
}
#[derive(Debug, Clone)]
pub struct ArgMatches {
pub(crate) args: IndexMap<Id, MatchedArg>,
pub(crate) subcommand: Option<Box<SubCommand>>,
}
impl Default for ArgMatches {
fn default() -> Self {
ArgMatches {
args: IndexMap::new(),
subcommand: None,
}
}
}
impl ArgMatches {
pub fn value_of<T: Key>(&self, id: T) -> Option<&str> {
if let Some(arg) = self.args.get(&Id::from(id)) {
if let Some(v) = arg.vals.get(0) {
return Some(v.to_str().expect(INVALID_UTF8));
}
}
None
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn value_of_lossy<T: Key>(&self, id: T) -> Option<Cow<'_, str>> {
if let Some(arg) = self.args.get(&Id::from(id)) {
if let Some(v) = arg.vals.get(0) {
return Some(v.to_string_lossy());
}
}
None
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn value_of_os<T: Key>(&self, id: T) -> Option<&OsStr> {
self.args
.get(&Id::from(id))
.and_then(|arg| arg.vals.get(0).map(OsString::as_os_str))
}
pub fn values_of<T: Key>(&self, id: T) -> Option<Values> {
self.args.get(&Id::from(id)).map(|arg| {
fn to_str_slice(o: &OsString) -> &str {
o.to_str().expect(INVALID_UTF8)
}
let to_str_slice: fn(&OsString) -> &str = to_str_slice;
Values {
iter: arg.vals.iter().map(to_str_slice),
}
})
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn values_of_lossy<T: Key>(&self, id: T) -> Option<Vec<String>> {
self.args.get(&Id::from(id)).map(|arg| {
arg.vals
.iter()
.map(|v| v.to_string_lossy().into_owned())
.collect()
})
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
pub fn values_of_os<'a, T: Key>(&'a self, id: T) -> Option<OsValues<'a>> {
fn to_str_slice(o: &OsString) -> &OsStr {
&*o
}
let to_str_slice: fn(&'a OsString) -> &'a OsStr = to_str_slice;
self.args.get(&Id::from(id)).map(|arg| OsValues {
iter: arg.vals.iter().map(to_str_slice),
})
}
pub fn value_of_t<R>(&self, name: &str) -> Result<R, Error>
where
R: FromStr,
<R as FromStr>::Err: Display,
{
if let Some(v) = self.value_of(name) {
v.parse::<R>().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, ColorChoice::Auto)
})
} else {
Err(Error::argument_not_found_auto(name.to_string()))
}
}
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,
{
if let Some(vals) = self.values_of(name) {
vals.map(|v| {
v.parse::<R>().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,
ColorChoice::Auto,
)
})
})
.collect()
} else {
Err(Error::argument_not_found_auto(name.to_string()))
}
}
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);
if let Some(ref sc) = self.subcommand {
if sc.id == id {
return true;
}
}
self.args.contains_key(&id)
}
pub fn occurrences_of<T: Key>(&self, id: T) -> u64 {
self.args.get(&Id::from(id)).map_or(0, |a| a.occurs)
}
pub fn index_of<T: Key>(&self, name: T) -> Option<usize> {
if let Some(arg) = self.args.get(&Id::from(name)) {
if let Some(i) = arg.indices.get(0) {
return Some(*i);
}
}
None
}
pub fn indices_of<T: Key>(&self, id: T) -> Option<Indices<'_>> {
self.args.get(&Id::from(id)).map(|arg| Indices {
iter: arg.indices.iter().cloned(),
})
}
pub fn subcommand_matches<T: Key>(&self, id: T) -> Option<&ArgMatches> {
if let Some(ref s) = self.subcommand {
if s.id == id.into() {
return Some(&s.matches);
}
}
None
}
#[inline]
pub fn subcommand_name(&self) -> Option<&str> {
self.subcommand.as_ref().map(|sc| &*sc.name)
}
#[inline]
pub fn subcommand(&self) -> Option<(&str, &ArgMatches)> {
self.subcommand.as_ref().map(|sc| (&*sc.name, &sc.matches))
}
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct Values<'a> {
iter: Map<Iter<'a, OsString>, fn(&'a OsString) -> &'a 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: [OsString; 0] = [];
fn to_str_slice(_: &OsString) -> &str {
unreachable!()
};
Values {
iter: EMPTY[..].iter().map(to_str_slice),
}
}
}
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct OsValues<'a> {
iter: Map<Iter<'a, OsString>, fn(&'a OsString) -> &'a 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: [OsString; 0] = [];
fn to_str_slice(_: &OsString) -> &OsStr {
unreachable!()
};
OsValues {
iter: EMPTY[..].iter().map(to_str_slice),
}
}
}
#[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(),
}
}
}
#[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);
}
}