mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
feat(parser): Add type information to arg values
To set the type, we offer - `ValueParser::<type>` short cuts for natively supported types - `TypedValueParser` for fn pointers and custom implementations - `value_parser!(T)` for specialized lookup of an implementation (inspired by #2298) The main motivation for `value_parser!` is to help with `clap_derive`s implementation but it can also be convinient for end-users. When reading, this replaces nearly all of our current `ArgMatches` getters with: - `get_one`: like `value_of_t` - `get_many`: like `values_of_t` It also adds a `get_raw` that allows accessing the `OsStr`s without panicing. The naming is to invoke the idea of a more general container which I want to move this to. The return type is a bit complicated so that - Users choose whether to panic on invalid types so they can do their own querying, like `get_raw` - Users can choose how to handle not present easily (#2505) We had to defer setting the `value_parser` on external subcommands, for consistency sake, because `Command` requires `PartialEq` and `ValueParser` does not impl that trait. It'll have to wait until a breaking change. Fixes #2505
This commit is contained in:
parent
dcf69d1c87
commit
200f6626db
5 changed files with 526 additions and 4 deletions
|
@ -1001,6 +1001,27 @@ impl<'help> Arg<'help> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Specify the type of the argument.
|
||||
///
|
||||
/// This allows parsing and validating a value before storing it into
|
||||
/// [`ArgMatches`][crate::ArgMatches].
|
||||
///
|
||||
/// ```rust
|
||||
/// let cmd = clap::Command::new("raw")
|
||||
/// .arg(
|
||||
/// clap::Arg::new("port")
|
||||
/// .value_parser(clap::value_parser!(usize))
|
||||
/// );
|
||||
/// let value_parser = cmd.get_arguments()
|
||||
/// .find(|a| a.get_id() == "port").unwrap()
|
||||
/// .get_value_parser();
|
||||
/// println!("{:?}", value_parser);
|
||||
/// ```
|
||||
pub fn value_parser(mut self, parser: impl Into<super::ValueParser>) -> Self {
|
||||
self.value_parser = Some(parser.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies that the argument may have an unknown number of values
|
||||
///
|
||||
/// Without any other settings, this argument may appear only *once*.
|
||||
|
@ -4739,7 +4760,21 @@ impl<'help> Arg<'help> {
|
|||
}
|
||||
|
||||
/// Configured parser for argument values
|
||||
pub(crate) fn get_value_parser(&self) -> &super::ValueParser {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let cmd = clap::Command::new("raw")
|
||||
/// .arg(
|
||||
/// clap::Arg::new("port")
|
||||
/// .value_parser(clap::value_parser!(usize))
|
||||
/// );
|
||||
/// let value_parser = cmd.get_arguments()
|
||||
/// .find(|a| a.get_id() == "port").unwrap()
|
||||
/// .get_value_parser();
|
||||
/// println!("{:?}", value_parser);
|
||||
/// ```
|
||||
pub fn get_value_parser(&self) -> &super::ValueParser {
|
||||
if let Some(value_parser) = self.value_parser.as_ref() {
|
||||
value_parser
|
||||
} else if self.is_allow_invalid_utf8_set() {
|
||||
|
|
|
@ -3679,7 +3679,17 @@ impl<'help> App<'help> {
|
|||
}
|
||||
|
||||
/// Configured parser for values passed to an external subcommand
|
||||
pub(crate) fn get_external_subcommand_value_parser(&self) -> Option<&super::ValueParser> {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let cmd = clap::Command::new("raw")
|
||||
/// .allow_external_subcommands(true)
|
||||
/// .allow_invalid_utf8_for_external_subcommands(true);
|
||||
/// let value_parser = cmd.get_external_subcommand_value_parser();
|
||||
/// println!("{:?}", value_parser);
|
||||
/// ```
|
||||
pub fn get_external_subcommand_value_parser(&self) -> Option<&super::ValueParser> {
|
||||
if !self.is_allow_external_subcommands_set() {
|
||||
None
|
||||
} else if self.is_allow_invalid_utf8_for_external_subcommands_set() {
|
||||
|
|
|
@ -30,7 +30,12 @@ pub use arg_settings::{ArgFlags, ArgSettings};
|
|||
pub use command::Command;
|
||||
pub use possible_value::PossibleValue;
|
||||
pub use value_hint::ValueHint;
|
||||
pub(crate) use value_parser::ValueParser;
|
||||
pub use value_parser::AnyValueParser;
|
||||
pub use value_parser::AutoValueParser;
|
||||
pub use value_parser::TypedValueParser;
|
||||
pub use value_parser::ValueParser;
|
||||
pub use value_parser::ValueParserViaBuiltIn;
|
||||
pub use value_parser::ValueParserViaFromStr;
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub use command::App;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::TypeId;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::parser::AnyValue;
|
||||
|
@ -10,9 +11,16 @@ pub struct ValueParser(pub(crate) ValueParserInner);
|
|||
pub(crate) enum ValueParserInner {
|
||||
String,
|
||||
OsString,
|
||||
PathBuf,
|
||||
Other(Arc<dyn AnyValueParser + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl ValueParser {
|
||||
/// Custom parser for argument values
|
||||
pub fn new(other: impl AnyValueParser + Send + Sync + 'static) -> Self {
|
||||
Self(ValueParserInner::Other(Arc::new(other)))
|
||||
}
|
||||
|
||||
/// `String` parser for argument values
|
||||
pub const fn string() -> Self {
|
||||
Self(ValueParserInner::String)
|
||||
|
@ -22,6 +30,11 @@ impl ValueParser {
|
|||
pub const fn os_string() -> Self {
|
||||
Self(ValueParserInner::OsString)
|
||||
}
|
||||
|
||||
/// `PathBuf` parser for argument values
|
||||
pub const fn path_buf() -> Self {
|
||||
Self(ValueParserInner::PathBuf)
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueParser {
|
||||
|
@ -31,7 +44,7 @@ impl ValueParser {
|
|||
pub fn parse_ref(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
_arg: Option<&crate::Arg>,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<AnyValue, crate::Error> {
|
||||
match &self.0 {
|
||||
|
@ -45,8 +58,61 @@ impl ValueParser {
|
|||
Ok(Arc::new(value.to_owned()))
|
||||
}
|
||||
ValueParserInner::OsString => Ok(Arc::new(value.to_owned())),
|
||||
ValueParserInner::PathBuf => Ok(Arc::new(std::path::PathBuf::from(value))),
|
||||
ValueParserInner::Other(o) => o.parse_ref(cmd, arg, value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse into a `Arc<Any>`
|
||||
///
|
||||
/// When `arg` is `None`, an external subcommand value is being parsed.
|
||||
pub fn parse(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: std::ffi::OsString,
|
||||
) -> Result<AnyValue, crate::Error> {
|
||||
match &self.0 {
|
||||
ValueParserInner::String => {
|
||||
let value = value.into_string().map_err(|_| {
|
||||
crate::Error::invalid_utf8(
|
||||
cmd,
|
||||
crate::output::Usage::new(cmd).create_usage_with_title(&[]),
|
||||
)
|
||||
})?;
|
||||
Ok(Arc::new(value))
|
||||
}
|
||||
ValueParserInner::OsString => Ok(Arc::new(value)),
|
||||
ValueParserInner::PathBuf => Ok(Arc::new(std::path::PathBuf::from(value))),
|
||||
ValueParserInner::Other(o) => o.parse(cmd, arg, value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the content of `Arc<Any>`
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
match &self.0 {
|
||||
ValueParserInner::String => TypeId::of::<String>(),
|
||||
ValueParserInner::OsString => TypeId::of::<std::ffi::OsString>(),
|
||||
ValueParserInner::PathBuf => TypeId::of::<std::path::PathBuf>(),
|
||||
ValueParserInner::Other(o) => o.type_id(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the content of `Arc<Any>`
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match &self.0 {
|
||||
ValueParserInner::String => std::any::type_name::<String>(),
|
||||
ValueParserInner::OsString => std::any::type_name::<std::ffi::OsString>(),
|
||||
ValueParserInner::PathBuf => std::any::type_name::<std::path::PathBuf>(),
|
||||
ValueParserInner::Other(o) => o.type_name(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AnyValueParser + Send + Sync + 'static> From<P> for ValueParser {
|
||||
fn from(p: P) -> Self {
|
||||
ValueParser(ValueParserInner::Other(Arc::new(p)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'help> std::fmt::Debug for ValueParser {
|
||||
|
@ -54,6 +120,250 @@ impl<'help> std::fmt::Debug for ValueParser {
|
|||
match &self.0 {
|
||||
ValueParserInner::String => f.debug_struct("ValueParser::string").finish(),
|
||||
ValueParserInner::OsString => f.debug_struct("ValueParser::os_string").finish(),
|
||||
ValueParserInner::PathBuf => f.debug_struct("ValueParser::path_buf").finish(),
|
||||
ValueParserInner::Other(o) => write!(f, "ValueParser::other({})", o.type_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Require people to implement `TypedValueParser` rather than `AnyValueParser`:
|
||||
// - Make implementing the user-facing trait easier
|
||||
// - Enforce in the type-system that a given `AnyValueParser::parse` always returns the same type
|
||||
// on each call and that it matches `type_id` / `type_name`
|
||||
/// Parse/validate argument values into a `Arc<Any>`
|
||||
pub trait AnyValueParser: private::AnyValueParserSealed {
|
||||
/// Parse into a `Arc<Any>`
|
||||
///
|
||||
/// When `arg` is `None`, an external subcommand value is being parsed.
|
||||
fn parse_ref(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<AnyValue, crate::Error>;
|
||||
|
||||
/// Parse into a `Arc<Any>`
|
||||
///
|
||||
/// When `arg` is `None`, an external subcommand value is being parsed.
|
||||
fn parse(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: std::ffi::OsString,
|
||||
) -> Result<AnyValue, crate::Error>;
|
||||
|
||||
/// Describes the content of `Arc<Any>`
|
||||
fn type_id(&self) -> TypeId;
|
||||
|
||||
/// Describes the content of `Arc<Any>`
|
||||
fn type_name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl<T, P> AnyValueParser for P
|
||||
where
|
||||
T: std::any::Any + Send + Sync + 'static,
|
||||
P: TypedValueParser<Value = T>,
|
||||
{
|
||||
fn parse_ref(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<AnyValue, crate::Error> {
|
||||
let value = TypedValueParser::parse_ref(self, cmd, arg, value)?;
|
||||
Ok(Arc::new(value))
|
||||
}
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: std::ffi::OsString,
|
||||
) -> Result<AnyValue, crate::Error> {
|
||||
let value = TypedValueParser::parse(self, cmd, arg, value)?;
|
||||
Ok(Arc::new(value))
|
||||
}
|
||||
|
||||
fn type_id(&self) -> TypeId {
|
||||
TypeId::of::<T>()
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse/validate argument values
|
||||
pub trait TypedValueParser {
|
||||
/// Argument's value type
|
||||
type Value;
|
||||
|
||||
/// Parse the argument value
|
||||
///
|
||||
/// When `arg` is `None`, an external subcommand value is being parsed.
|
||||
fn parse_ref(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<Self::Value, crate::Error>;
|
||||
|
||||
/// Parse the argument value
|
||||
///
|
||||
/// When `arg` is `None`, an external subcommand value is being parsed.
|
||||
fn parse(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: std::ffi::OsString,
|
||||
) -> Result<Self::Value, crate::Error> {
|
||||
self.parse_ref(cmd, arg, &value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> TypedValueParser for fn(&str) -> Result<T, E>
|
||||
where
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<T, crate::Error> {
|
||||
let value = value.to_str().ok_or_else(|| {
|
||||
crate::Error::invalid_utf8(
|
||||
cmd,
|
||||
crate::output::Usage::new(cmd).create_usage_with_title(&[]),
|
||||
)
|
||||
})?;
|
||||
let value = (self)(value).map_err(|e| {
|
||||
let arg = arg
|
||||
.map(|a| a.to_string())
|
||||
.unwrap_or_else(|| "...".to_owned());
|
||||
crate::Error::value_validation(arg, value.to_owned(), e.into()).with_cmd(cmd)
|
||||
})?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> TypedValueParser for fn(&std::ffi::OsStr) -> Result<T, E>
|
||||
where
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
cmd: &crate::Command,
|
||||
arg: Option<&crate::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<T, crate::Error> {
|
||||
let value = (self)(value).map_err(|e| {
|
||||
let arg = arg
|
||||
.map(|a| a.to_string())
|
||||
.unwrap_or_else(|| "...".to_owned());
|
||||
crate::Error::value_validation(arg, value.to_string_lossy().into_owned(), e.into())
|
||||
.with_cmd(cmd)
|
||||
})?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct AutoValueParser<T>(std::marker::PhantomData<T>);
|
||||
|
||||
impl<T> AutoValueParser<T> {
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait ValueParserViaBuiltIn: private::ValueParserViaBuiltInSealed {
|
||||
fn value_parser(&self) -> ValueParser;
|
||||
}
|
||||
impl ValueParserViaBuiltIn for &AutoValueParser<String> {
|
||||
fn value_parser(&self) -> ValueParser {
|
||||
ValueParser::string()
|
||||
}
|
||||
}
|
||||
impl ValueParserViaBuiltIn for &AutoValueParser<std::ffi::OsString> {
|
||||
fn value_parser(&self) -> ValueParser {
|
||||
ValueParser::os_string()
|
||||
}
|
||||
}
|
||||
impl ValueParserViaBuiltIn for &AutoValueParser<std::path::PathBuf> {
|
||||
fn value_parser(&self) -> ValueParser {
|
||||
ValueParser::path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait ValueParserViaFromStr: private::ValueParserViaFromStrSealed {
|
||||
fn value_parser(&self) -> ValueParser;
|
||||
}
|
||||
impl<FromStr> ValueParserViaFromStr for AutoValueParser<FromStr>
|
||||
where
|
||||
FromStr: std::str::FromStr + std::any::Any + Send + Sync + 'static,
|
||||
<FromStr as std::str::FromStr>::Err: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
|
||||
{
|
||||
fn value_parser(&self) -> ValueParser {
|
||||
let func: fn(&str) -> Result<FromStr, <FromStr as std::str::FromStr>::Err> =
|
||||
FromStr::from_str;
|
||||
ValueParser::new(func)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse/validate argument values
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let parser = clap::value_parser!(String);
|
||||
/// assert_eq!(format!("{:?}", parser), "ValueParser::string");
|
||||
/// let parser = clap::value_parser!(std::ffi::OsString);
|
||||
/// assert_eq!(format!("{:?}", parser), "ValueParser::os_string");
|
||||
/// let parser = clap::value_parser!(std::path::PathBuf);
|
||||
/// assert_eq!(format!("{:?}", parser), "ValueParser::path_buf");
|
||||
/// let parser = clap::value_parser!(usize);
|
||||
/// assert_eq!(format!("{:?}", parser), "ValueParser::other(usize)");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! value_parser {
|
||||
($name:ty) => {{
|
||||
use $crate::builder::ValueParserViaBuiltIn;
|
||||
use $crate::builder::ValueParserViaFromStr;
|
||||
let auto = $crate::builder::AutoValueParser::<$name>::new();
|
||||
(&&auto).value_parser()
|
||||
}};
|
||||
}
|
||||
|
||||
mod private {
|
||||
pub trait AnyValueParserSealed {}
|
||||
impl<T, P> AnyValueParserSealed for P
|
||||
where
|
||||
T: std::any::Any + Send + Sync + 'static,
|
||||
P: super::TypedValueParser<Value = T>,
|
||||
{
|
||||
}
|
||||
|
||||
pub trait ValueParserViaBuiltInSealed {}
|
||||
impl ValueParserViaBuiltInSealed for &super::AutoValueParser<String> {}
|
||||
impl ValueParserViaBuiltInSealed for &super::AutoValueParser<std::ffi::OsString> {}
|
||||
impl ValueParserViaBuiltInSealed for &super::AutoValueParser<std::path::PathBuf> {}
|
||||
|
||||
pub trait ValueParserViaFromStrSealed {}
|
||||
impl<FromStr> ValueParserViaFromStrSealed for super::AutoValueParser<FromStr>
|
||||
where
|
||||
FromStr: std::str::FromStr + std::any::Any + Send + Sync + 'static,
|
||||
<FromStr as std::str::FromStr>::Err:
|
||||
Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,168 @@ pub struct ArgMatches {
|
|||
}
|
||||
|
||||
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.
|
||||
/// [`occurrences_of`] can be used to check if a value is present at runtime.
|
||||
///
|
||||
/// # Panics
|
||||
/// If `id` is is not a valid argument or group name.
|
||||
///
|
||||
/// # 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 a `usize`")
|
||||
/// .expect("`port`is required");
|
||||
/// assert_eq!(port, 2020);
|
||||
/// ```
|
||||
/// [option]: crate::Arg::takes_value()
|
||||
/// [positional]: crate::Arg::index()
|
||||
/// [`ArgMatches::values_of`]: ArgMatches::values_of()
|
||||
/// [`default_value`]: crate::Arg::default_value()
|
||||
/// [`occurrences_of`]: crate::ArgMatches::occurrences_of()
|
||||
pub fn get_one<T: 'static>(&self, name: &str) -> Result<Option<&T>, Error> {
|
||||
let id = Id::from(name);
|
||||
let value = match self.get_arg(&id).and_then(|a| a.first()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
value.downcast_ref::<T>().map(Some).ok_or_else(|| {
|
||||
Error::raw(
|
||||
crate::error::ErrorKind::ValueValidation,
|
||||
format!(
|
||||
"The argument `{}` is not of type `{}`",
|
||||
name,
|
||||
std::any::type_name::<T>()
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `id` is is not a valid argument or group name.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{Command, Arg, value_parser};
|
||||
/// let m = Command::new("myprog")
|
||||
/// .arg(Arg::new("ports")
|
||||
/// .multiple_occurrences(true)
|
||||
/// .value_parser(value_parser!(usize))
|
||||
/// .short('p')
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "myprog", "-p", "22", "-p", "80", "-p", "2020"
|
||||
/// ]);
|
||||
/// let vals: Vec<usize> = m.get_many("ports")
|
||||
/// .expect("`port` is a `usize`")
|
||||
/// .expect("`port`is required")
|
||||
/// .copied()
|
||||
/// .collect();
|
||||
/// assert_eq!(vals, [22, 80, 2020]);
|
||||
/// ```
|
||||
/// [values]: Values
|
||||
pub fn get_many<T: 'static>(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Result<Option<impl Iterator<Item = &T>>, Error> {
|
||||
let id = Id::from(name);
|
||||
let values = match self.get_arg(&id) {
|
||||
Some(values) => values.vals_flatten(),
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
// HACK: Track the type id and report errors even when there are no values
|
||||
let values: Result<Vec<&T>, Error> = values
|
||||
.map(|v| {
|
||||
v.downcast_ref::<T>().ok_or_else(|| {
|
||||
Error::raw(
|
||||
crate::error::ErrorKind::ValueValidation,
|
||||
format!(
|
||||
"The argument `{}` is not of type `{}`",
|
||||
name,
|
||||
std::any::type_name::<T>()
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Ok(Some(values?.into_iter()))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `id` is is not a valid argument or group name.
|
||||
///
|
||||
/// # 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!(<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").unwrap().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
|
||||
pub fn get_raw<T: Key>(&self, id: T) -> Option<impl Iterator<Item = &OsStr>> {
|
||||
let id = Id::from(id);
|
||||
let arg = self.get_arg(&id)?;
|
||||
Some(arg.raw_vals_flatten().map(|v| v.as_os_str()))
|
||||
}
|
||||
|
||||
/// Check if any args were present on the command line
|
||||
///
|
||||
/// # Examples
|
||||
|
|
Loading…
Reference in a new issue