use std::convert::TryInto; use std::ops::RangeBounds; use crate::parser::AnyValue; use crate::parser::AnyValueId; /// Parse/validate argument values /// /// Specified with [`Arg::value_parser`][crate::Arg::value_parser]. /// /// `ValueParser` defines how to convert a raw argument value into a validated and typed value for /// use within an application. /// /// See /// - [`value_parser!`] for automatically selecting an implementation for a given type /// - [`ValueParser::new`] for additional [`TypedValueParser`] that can be used /// /// # Example /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("color") /// .long("color") /// .value_parser(["always", "auto", "never"]) /// .default_value("auto") /// ) /// .arg( /// clap::Arg::new("hostname") /// .long("hostname") /// .value_parser(clap::builder::NonEmptyStringValueParser::new()) /// .takes_value(true) /// .required(true) /// ) /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(clap::value_parser!(u16).range(3000..)) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut( /// ["cmd", "--hostname", "rust-lang.org", "--port", "3001"] /// ).unwrap(); /// /// let color: &String = m.get_one("color") /// .expect("default"); /// assert_eq!(color, "auto"); /// /// let hostname: &String = m.get_one("hostname") /// .expect("required"); /// assert_eq!(hostname, "rust-lang.org"); /// /// let port: u16 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` pub struct ValueParser(ValueParserInner); enum ValueParserInner { // Common enough to optimize and for possible values Bool, // Common enough to optimize String, // Common enough to optimize OsString, // Common enough to optimize PathBuf, Other(Box), } impl ValueParser { /// Custom parser for argument values /// /// To create a custom parser, see [`TypedValueParser`] /// /// Pre-existing implementations include: /// - [`EnumValueParser`] and [`PossibleValuesParser`] for static enumerated values /// - [`BoolishValueParser`] and [`FalseyValueParser`] for alternative `bool` implementations /// - [`RangedI64ValueParser`] and [`RangedU64ValueParser`] /// - [`NonEmptyStringValueParser`] /// /// # Example /// /// ```rust /// type EnvVar = (String, Option); /// fn parse_env_var(env: &str) -> Result { /// if let Some((var, value)) = env.split_once('=') { /// Ok((var.to_owned(), Some(value.to_owned()))) /// } else { /// Ok((env.to_owned(), None)) /// } /// } /// /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("env") /// .value_parser(clap::builder::ValueParser::new(parse_env_var)) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "key=value"]).unwrap(); /// let port: &EnvVar = m.get_one("env") /// .expect("required"); /// assert_eq!(*port, ("key".into(), Some("value".into()))); /// ``` pub fn new

(other: P) -> Self where P: TypedValueParser, P::Value: Send + Sync + Clone, { Self(ValueParserInner::Other(Box::new(other))) } /// [`bool`] parser for argument values /// /// See also: /// - [`BoolishValueParser`] for different human readable bool representations /// - [`FalseyValueParser`] for assuming non-false is true /// /// # Example /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("download") /// .value_parser(clap::value_parser!(bool)) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "true"]).unwrap(); /// let port: bool = *m.get_one("download") /// .expect("required"); /// assert_eq!(port, true); /// /// assert!(cmd.try_get_matches_from_mut(["cmd", "forever"]).is_err()); /// ``` pub const fn bool() -> Self { Self(ValueParserInner::Bool) } /// [`String`] parser for argument values /// /// See also: /// - [`NonEmptyStringValueParser`] /// /// # Example /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .value_parser(clap::value_parser!(String)) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "80"]).unwrap(); /// let port: &String = m.get_one("port") /// .expect("required"); /// assert_eq!(port, "80"); /// ``` pub const fn string() -> Self { Self(ValueParserInner::String) } /// [`OsString`][std::ffi::OsString] parser for argument values /// /// # Example /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```rust")] /// # use clap::{Command, Arg, builder::ValueParser}; /// use std::ffi::OsString; /// use std::os::unix::ffi::{OsStrExt,OsStringExt}; /// let r = Command::new("myprog") /// .arg( /// Arg::new("arg") /// .required(true) /// .value_parser(ValueParser::os_string()) /// ) /// .try_get_matches_from(vec![ /// OsString::from("myprog"), /// OsString::from_vec(vec![0xe9]) /// ]); /// /// assert!(r.is_ok()); /// let m = r.unwrap(); /// let arg: &OsString = m.get_one("arg") /// .expect("required"); /// assert_eq!(arg.as_bytes(), &[0xe9]); /// ``` pub const fn os_string() -> Self { Self(ValueParserInner::OsString) } /// [`PathBuf`][std::path::PathBuf] parser for argument values /// /// # Example /// /// ```rust /// # use std::path::PathBuf; /// # use std::path::Path; /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("output") /// .value_parser(clap::value_parser!(PathBuf)) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "hello.txt"]).unwrap(); /// let port: &PathBuf = m.get_one("output") /// .expect("required"); /// assert_eq!(port, Path::new("hello.txt")); /// /// assert!(cmd.try_get_matches_from_mut(["cmd", ""]).is_err()); /// ``` pub const fn path_buf() -> Self { Self(ValueParserInner::PathBuf) } } impl ValueParser { /// Parse into a `AnyValue` /// /// When `arg` is `None`, an external subcommand value is being parsed. pub(crate) fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { self.any_value_parser().parse_ref(cmd, arg, value) } /// Describes the content of `AnyValue` pub fn type_id(&self) -> AnyValueId { self.any_value_parser().type_id() } /// Reflect on enumerated value properties /// /// Error checking should not be done with this; it is mostly targeted at user-facing /// applications like errors and completion. pub fn possible_values( &self, ) -> Option> + '_>> { self.any_value_parser().possible_values() } fn any_value_parser(&self) -> &dyn AnyValueParser { match &self.0 { ValueParserInner::Bool => &BoolValueParser {}, ValueParserInner::String => &StringValueParser {}, ValueParserInner::OsString => &OsStringValueParser {}, ValueParserInner::PathBuf => &PathBufValueParser {}, ValueParserInner::Other(o) => o.as_ref(), } } } /// Convert a [`TypedValueParser`] to [`ValueParser`] /// /// # Example /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("hostname") /// .long("hostname") /// .value_parser(clap::builder::NonEmptyStringValueParser::new()) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut( /// ["cmd", "--hostname", "rust-lang.org"] /// ).unwrap(); /// /// let hostname: &String = m.get_one("hostname") /// .expect("required"); /// assert_eq!(hostname, "rust-lang.org"); /// ``` impl

From

for ValueParser where P: TypedValueParser + Send + Sync + 'static, P::Value: Send + Sync + Clone, { fn from(p: P) -> Self { Self::new(p) } } impl From<_AnonymousValueParser> for ValueParser { fn from(p: _AnonymousValueParser) -> Self { p.0 } } /// Create an `i64` [`ValueParser`] from a `N..M` range /// /// See [`RangedI64ValueParser`] for more control over the output type. /// /// See also [`RangedU64ValueParser`] /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(3000..4000) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); /// let port: i64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` impl From> for ValueParser { fn from(value: std::ops::Range) -> Self { let inner = RangedI64ValueParser::::new().range(value.start..value.end); Self::from(inner) } } /// Create an `i64` [`ValueParser`] from a `N..=M` range /// /// See [`RangedI64ValueParser`] for more control over the output type. /// /// See also [`RangedU64ValueParser`] /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(3000..=4000) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); /// let port: i64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` impl From> for ValueParser { fn from(value: std::ops::RangeInclusive) -> Self { let inner = RangedI64ValueParser::::new().range(value.start()..=value.end()); Self::from(inner) } } /// Create an `i64` [`ValueParser`] from a `N..` range /// /// See [`RangedI64ValueParser`] for more control over the output type. /// /// See also [`RangedU64ValueParser`] /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(3000..) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); /// let port: i64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` impl From> for ValueParser { fn from(value: std::ops::RangeFrom) -> Self { let inner = RangedI64ValueParser::::new().range(value.start..); Self::from(inner) } } /// Create an `i64` [`ValueParser`] from a `..M` range /// /// See [`RangedI64ValueParser`] for more control over the output type. /// /// See also [`RangedU64ValueParser`] /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(..3000) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "80"]).unwrap(); /// let port: i64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 80); /// ``` impl From> for ValueParser { fn from(value: std::ops::RangeTo) -> Self { let inner = RangedI64ValueParser::::new().range(..value.end); Self::from(inner) } } /// Create an `i64` [`ValueParser`] from a `..=M` range /// /// See [`RangedI64ValueParser`] for more control over the output type. /// /// See also [`RangedU64ValueParser`] /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(..=3000) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "80"]).unwrap(); /// let port: i64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 80); /// ``` impl From> for ValueParser { fn from(value: std::ops::RangeToInclusive) -> Self { let inner = RangedI64ValueParser::::new().range(..=value.end); Self::from(inner) } } /// Create an `i64` [`ValueParser`] from a `..` range /// /// See [`RangedI64ValueParser`] for more control over the output type. /// /// See also [`RangedU64ValueParser`] /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(..) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); /// let port: i64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` impl From for ValueParser { fn from(value: std::ops::RangeFull) -> Self { let inner = RangedI64ValueParser::::new().range(value); Self::from(inner) } } /// Create a [`ValueParser`] with [`PossibleValuesParser`] /// /// See [`PossibleValuesParser`] for more flexibility in creating the /// [`PossibleValue`][crate::PossibleValue]s. /// /// # Examples /// /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("color") /// .long("color") /// .value_parser(["always", "auto", "never"]) /// .default_value("auto") /// ); /// /// let m = cmd.try_get_matches_from_mut( /// ["cmd", "--color", "never"] /// ).unwrap(); /// /// let color: &String = m.get_one("color") /// .expect("default"); /// assert_eq!(color, "never"); /// ``` impl From<[P; C]> for ValueParser where P: Into>, { fn from(values: [P; C]) -> Self { let inner = PossibleValuesParser::from(values); Self::from(inner) } } impl std::fmt::Debug for ValueParser { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { match &self.0 { ValueParserInner::Bool => f.debug_struct("ValueParser::bool").finish(), 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_id()), } } } impl Clone for ValueParser { fn clone(&self) -> Self { Self(match &self.0 { ValueParserInner::Bool => ValueParserInner::Bool, ValueParserInner::String => ValueParserInner::String, ValueParserInner::OsString => ValueParserInner::OsString, ValueParserInner::PathBuf => ValueParserInner::PathBuf, ValueParserInner::Other(o) => ValueParserInner::Other(o.clone_any()), }) } } /// A type-erased wrapper for [`TypedValueParser`]. trait AnyValueParser: Send + Sync + 'static { fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result; fn parse( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: std::ffi::OsString, ) -> Result; /// Describes the content of `AnyValue` fn type_id(&self) -> AnyValueId; fn possible_values( &self, ) -> Option> + '_>>; fn clone_any(&self) -> Box; } impl AnyValueParser for P where T: std::any::Any + Clone + Send + Sync + 'static, P: TypedValueParser, { fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { let value = TypedValueParser::parse_ref(self, cmd, arg, value)?; Ok(AnyValue::new(value)) } fn parse( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: std::ffi::OsString, ) -> Result { let value = TypedValueParser::parse(self, cmd, arg, value)?; Ok(AnyValue::new(value)) } fn type_id(&self) -> AnyValueId { AnyValueId::of::() } fn possible_values( &self, ) -> Option> + '_>> { P::possible_values(self) } fn clone_any(&self) -> Box { Box::new(self.clone()) } } /// Parse/validate argument values pub trait TypedValueParser: Clone + Send + Sync + 'static { /// 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; /// 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.parse_ref(cmd, arg, &value) } /// Reflect on enumerated value properties /// /// Error checking should not be done with this; it is mostly targeted at user-facing /// applications like errors and completion. fn possible_values( &self, ) -> Option> + '_>> { None } } impl TypedValueParser for F where F: Fn(&str) -> Result + Clone + Send + Sync + 'static, E: Into>, { type Value = T; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { 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) } } /// Implementation for [`ValueParser::string`] /// /// Useful for composing new [`TypedValueParser`]s #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct StringValueParser {} impl StringValueParser { /// Implementation for [`ValueParser::string`] pub fn new() -> Self { Self {} } } impl TypedValueParser for StringValueParser { type Value = String; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { TypedValueParser::parse(self, cmd, arg, value.to_owned()) } fn parse( &self, cmd: &crate::Command, _arg: Option<&crate::Arg>, value: std::ffi::OsString, ) -> Result { let value = value.into_string().map_err(|_| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; Ok(value) } } impl Default for StringValueParser { fn default() -> Self { Self::new() } } /// Implementation for [`ValueParser::os_string`] /// /// Useful for composing new [`TypedValueParser`]s #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct OsStringValueParser {} impl OsStringValueParser { /// Implementation for [`ValueParser::os_string`] pub fn new() -> Self { Self {} } } impl TypedValueParser for OsStringValueParser { type Value = std::ffi::OsString; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { TypedValueParser::parse(self, cmd, arg, value.to_owned()) } fn parse( &self, _cmd: &crate::Command, _arg: Option<&crate::Arg>, value: std::ffi::OsString, ) -> Result { Ok(value) } } impl Default for OsStringValueParser { fn default() -> Self { Self::new() } } /// Implementation for [`ValueParser::path_buf`] /// /// Useful for composing new [`TypedValueParser`]s #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct PathBufValueParser {} impl PathBufValueParser { /// Implementation for [`ValueParser::path_buf`] pub fn new() -> Self { Self {} } } impl TypedValueParser for PathBufValueParser { type Value = std::path::PathBuf; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { TypedValueParser::parse(self, cmd, arg, value.to_owned()) } fn parse( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: std::ffi::OsString, ) -> Result { if value.is_empty() { return Err(crate::Error::empty_value( cmd, &[], arg.map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()), )); } Ok(Self::Value::from(value)) } } impl Default for PathBufValueParser { fn default() -> Self { Self::new() } } /// Parse an [`ValueEnum`][crate::ValueEnum] value. /// /// See also: /// - [`PossibleValuesParser`] /// /// # Example /// /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// /// #[derive(Copy, Clone, Debug, PartialEq, Eq)] /// enum ColorChoice { /// Always, /// Auto, /// Never, /// } /// /// impl clap::ValueEnum for ColorChoice { /// fn value_variants<'a>() -> &'a [Self] { /// &[Self::Always, Self::Auto, Self::Never] /// } /// /// fn to_possible_value<'a>(&self) -> Option> { /// match self { /// Self::Always => Some(clap::PossibleValue::new("always")), /// Self::Auto => Some(clap::PossibleValue::new("auto")), /// Self::Never => Some(clap::PossibleValue::new("never")), /// } /// } /// } /// /// // Usage /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("color") /// .value_parser(clap::builder::EnumValueParser::::new()) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "always"]).unwrap(); /// let port: ColorChoice = *m.get_one("color") /// .expect("required"); /// assert_eq!(port, ColorChoice::Always); /// /// // Semantics /// let value_parser = clap::builder::EnumValueParser::::new(); /// // or /// let value_parser = clap::value_parser!(ColorChoice); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("always")).unwrap(), ColorChoice::Always); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("auto")).unwrap(), ColorChoice::Auto); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("never")).unwrap(), ColorChoice::Never); /// ``` #[derive(Clone, Debug)] pub struct EnumValueParser( std::marker::PhantomData, ); impl EnumValueParser { /// Parse an [`ValueEnum`][crate::ValueEnum] pub fn new() -> Self { let phantom: std::marker::PhantomData = Default::default(); Self(phantom) } } impl TypedValueParser for EnumValueParser { type Value = E; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { let ignore_case = arg.map(|a| a.is_ignore_case_set()).unwrap_or(false); let possible_vals = || { E::value_variants() .iter() .filter_map(|v| v.to_possible_value()) .filter(|v| !v.is_hide_set()) .map(|v| v.get_name()) .collect::>() }; let value = value.to_str().ok_or_else(|| { crate::Error::invalid_value( cmd, value.to_string_lossy().into_owned(), &possible_vals(), arg.map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()), ) })?; let value = E::value_variants() .iter() .find(|v| { v.to_possible_value() .expect("ValueEnum::value_variants contains only values with a corresponding ValueEnum::to_possible_value") .matches(value, ignore_case) }) .ok_or_else(|| { crate::Error::invalid_value( cmd, value.to_owned(), &possible_vals(), arg.map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()), ) })? .clone(); Ok(value) } fn possible_values( &self, ) -> Option> + '_>> { Some(Box::new( E::value_variants() .iter() .filter_map(|v| v.to_possible_value()), )) } } impl Default for EnumValueParser { fn default() -> Self { Self::new() } } /// Verify the value is from an enumerated set of [`PossibleValue`][crate::PossibleValue]. /// /// See also: /// - [`EnumValueParser`] /// /// # Example /// /// Usage: /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("color") /// .value_parser(clap::builder::PossibleValuesParser::new(["always", "auto", "never"])) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "always"]).unwrap(); /// let port: &String = m.get_one("color") /// .expect("required"); /// assert_eq!(port, "always"); /// ``` /// /// Semantics: /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// let value_parser = clap::builder::PossibleValuesParser::new(["always", "auto", "never"]); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("always")).unwrap(), "always"); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("auto")).unwrap(), "auto"); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("never")).unwrap(), "never"); /// ``` #[derive(Clone, Debug)] pub struct PossibleValuesParser(Vec>); impl PossibleValuesParser { /// Verify the value is from an enumerated set pf [`PossibleValue`][crate::PossibleValue]. pub fn new(values: impl Into) -> Self { values.into() } } impl TypedValueParser for PossibleValuesParser { type Value = String; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { TypedValueParser::parse(self, cmd, arg, value.to_owned()) } fn parse( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: std::ffi::OsString, ) -> Result { let value = value.into_string().map_err(|_| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; let ignore_case = arg.map(|a| a.is_ignore_case_set()).unwrap_or(false); if self.0.iter().any(|v| v.matches(&value, ignore_case)) { Ok(value) } else { let possible_vals = self .0 .iter() .filter(|v| !v.is_hide_set()) .map(crate::builder::PossibleValue::get_name) .collect::>(); Err(crate::Error::invalid_value( cmd, value, &possible_vals, arg.map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()), )) } } fn possible_values( &self, ) -> Option> + '_>> { Some(Box::new(self.0.iter().cloned())) } } impl From for PossibleValuesParser where I: IntoIterator, T: Into>, { fn from(values: I) -> Self { Self(values.into_iter().map(|t| t.into()).collect()) } } /// Parse number that fall within a range of values /// /// # Example /// /// Usage: /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(clap::value_parser!(u16).range(3000..)) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); /// let port: u16 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` /// /// Semantics: /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// let value_parser = clap::builder::RangedI64ValueParser::::new().range(-1..200); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-200")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("300")).is_err()); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("-1")).unwrap(), -1); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("0")).unwrap(), 0); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("50")).unwrap(), 50); /// ``` #[derive(Copy, Clone, Debug)] pub struct RangedI64ValueParser + Clone + Send + Sync = i64> { bounds: (std::ops::Bound, std::ops::Bound), target: std::marker::PhantomData, } impl + Clone + Send + Sync> RangedI64ValueParser { /// Select full range of `i64` pub fn new() -> Self { Self::from(..) } /// Narrow the supported range pub fn range>(mut self, range: B) -> Self { // Consideration: when the user does `value_parser!(u8).range()` // - Avoid programming mistakes by accidentally expanding the range // - Make it convenient to limit the range like with `..10` let start = match range.start_bound() { l @ std::ops::Bound::Included(i) => { debug_assert!( self.bounds.contains(i), "{} must be in {:?}", i, self.bounds ); l.cloned() } l @ std::ops::Bound::Excluded(i) => { debug_assert!( self.bounds.contains(&i.saturating_add(1)), "{} must be in {:?}", i, self.bounds ); l.cloned() } std::ops::Bound::Unbounded => self.bounds.start_bound().cloned(), }; let end = match range.end_bound() { l @ std::ops::Bound::Included(i) => { debug_assert!( self.bounds.contains(i), "{} must be in {:?}", i, self.bounds ); l.cloned() } l @ std::ops::Bound::Excluded(i) => { debug_assert!( self.bounds.contains(&i.saturating_sub(1)), "{} must be in {:?}", i, self.bounds ); l.cloned() } std::ops::Bound::Unbounded => self.bounds.end_bound().cloned(), }; self.bounds = (start, end); self } fn format_bounds(&self) -> String { let mut result = match self.bounds.0 { std::ops::Bound::Included(i) => i.to_string(), std::ops::Bound::Excluded(i) => i.saturating_add(1).to_string(), std::ops::Bound::Unbounded => i64::MIN.to_string(), }; result.push_str(".."); match self.bounds.1 { std::ops::Bound::Included(i) => { result.push('='); result.push_str(&i.to_string()); } std::ops::Bound::Excluded(i) => { result.push_str(&i.to_string()); } std::ops::Bound::Unbounded => { result.push_str(&i64::MAX.to_string()); } } result } } impl + Clone + Send + Sync + 'static> TypedValueParser for RangedI64ValueParser where >::Error: Send + Sync + 'static + std::error::Error + ToString, { type Value = T; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, raw_value: &std::ffi::OsStr, ) -> Result { let value = raw_value.to_str().ok_or_else(|| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; let value = value.parse::().map_err(|err| { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); crate::Error::value_validation( arg, raw_value.to_string_lossy().into_owned(), err.into(), ) .with_cmd(cmd) })?; if !self.bounds.contains(&value) { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); return Err(crate::Error::value_validation( arg, raw_value.to_string_lossy().into_owned(), format!("{} is not in {}", value, self.format_bounds()).into(), ) .with_cmd(cmd)); } let value: Result = value.try_into(); let value = value.map_err(|err| { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); crate::Error::value_validation( arg, raw_value.to_string_lossy().into_owned(), err.into(), ) .with_cmd(cmd) })?; Ok(value) } } impl + Clone + Send + Sync, B: RangeBounds> From for RangedI64ValueParser { fn from(range: B) -> Self { Self { bounds: (range.start_bound().cloned(), range.end_bound().cloned()), target: Default::default(), } } } impl + Clone + Send + Sync> Default for RangedI64ValueParser { fn default() -> Self { Self::new() } } /// Parse number that fall within a range of values /// /// # Example /// /// Usage: /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("port") /// .long("port") /// .value_parser(clap::value_parser!(u64).range(3000..)) /// .takes_value(true) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); /// let port: u64 = *m.get_one("port") /// .expect("required"); /// assert_eq!(port, 3001); /// ``` /// /// Semantics: /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// let value_parser = clap::builder::RangedU64ValueParser::::new().range(0..200); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-200")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("300")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-1")).is_err()); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("0")).unwrap(), 0); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("50")).unwrap(), 50); /// ``` #[derive(Copy, Clone, Debug)] pub struct RangedU64ValueParser = u64> { bounds: (std::ops::Bound, std::ops::Bound), target: std::marker::PhantomData, } impl> RangedU64ValueParser { /// Select full range of `u64` pub fn new() -> Self { Self::from(..) } /// Narrow the supported range pub fn range>(mut self, range: B) -> Self { // Consideration: when the user does `value_parser!(u8).range()` // - Avoid programming mistakes by accidentally expanding the range // - Make it convenient to limit the range like with `..10` let start = match range.start_bound() { l @ std::ops::Bound::Included(i) => { debug_assert!( self.bounds.contains(i), "{} must be in {:?}", i, self.bounds ); l.cloned() } l @ std::ops::Bound::Excluded(i) => { debug_assert!( self.bounds.contains(&i.saturating_add(1)), "{} must be in {:?}", i, self.bounds ); l.cloned() } std::ops::Bound::Unbounded => self.bounds.start_bound().cloned(), }; let end = match range.end_bound() { l @ std::ops::Bound::Included(i) => { debug_assert!( self.bounds.contains(i), "{} must be in {:?}", i, self.bounds ); l.cloned() } l @ std::ops::Bound::Excluded(i) => { debug_assert!( self.bounds.contains(&i.saturating_sub(1)), "{} must be in {:?}", i, self.bounds ); l.cloned() } std::ops::Bound::Unbounded => self.bounds.end_bound().cloned(), }; self.bounds = (start, end); self } fn format_bounds(&self) -> String { let mut result = match self.bounds.0 { std::ops::Bound::Included(i) => i.to_string(), std::ops::Bound::Excluded(i) => i.saturating_add(1).to_string(), std::ops::Bound::Unbounded => u64::MIN.to_string(), }; result.push_str(".."); match self.bounds.1 { std::ops::Bound::Included(i) => { result.push('='); result.push_str(&i.to_string()); } std::ops::Bound::Excluded(i) => { result.push_str(&i.to_string()); } std::ops::Bound::Unbounded => { result.push_str(&u64::MAX.to_string()); } } result } } impl + Clone + Send + Sync + 'static> TypedValueParser for RangedU64ValueParser where >::Error: Send + Sync + 'static + std::error::Error + ToString, { type Value = T; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, raw_value: &std::ffi::OsStr, ) -> Result { let value = raw_value.to_str().ok_or_else(|| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; let value = value.parse::().map_err(|err| { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); crate::Error::value_validation( arg, raw_value.to_string_lossy().into_owned(), err.into(), ) .with_cmd(cmd) })?; if !self.bounds.contains(&value) { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); return Err(crate::Error::value_validation( arg, raw_value.to_string_lossy().into_owned(), format!("{} is not in {}", value, self.format_bounds()).into(), ) .with_cmd(cmd)); } let value: Result = value.try_into(); let value = value.map_err(|err| { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); crate::Error::value_validation( arg, raw_value.to_string_lossy().into_owned(), err.into(), ) .with_cmd(cmd) })?; Ok(value) } } impl, B: RangeBounds> From for RangedU64ValueParser { fn from(range: B) -> Self { Self { bounds: (range.start_bound().cloned(), range.end_bound().cloned()), target: Default::default(), } } } impl> Default for RangedU64ValueParser { fn default() -> Self { Self::new() } } /// Implementation for [`ValueParser::bool`] /// /// Useful for composing new [`TypedValueParser`]s #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct BoolValueParser {} impl BoolValueParser { /// Implementation for [`ValueParser::bool`] pub fn new() -> Self { Self {} } fn possible_values() -> impl Iterator> { ["true", "false"] .iter() .copied() .map(crate::PossibleValue::new) } } impl TypedValueParser for BoolValueParser { type Value = bool; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { let value = if value == std::ffi::OsStr::new("true") { true } else if value == std::ffi::OsStr::new("false") { false } else { // Intentionally showing hidden as we hide all of them let possible_vals = Self::possible_values() .map(|v| v.get_name()) .collect::>(); return Err(crate::Error::invalid_value( cmd, value.to_string_lossy().into_owned(), &possible_vals, arg.map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()), )); }; Ok(value) } fn possible_values( &self, ) -> Option> + '_>> { Some(Box::new(Self::possible_values())) } } impl Default for BoolValueParser { fn default() -> Self { Self::new() } } /// Parse false-like string values, everything else is `true` /// /// See also: /// - [`ValueParser::bool`] for assuming non-false is true /// - [`BoolishValueParser`] for different human readable bool representations /// /// # Example /// /// Usage: /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("append") /// .value_parser(clap::builder::FalseyValueParser::new()) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "true"]).unwrap(); /// let port: bool = *m.get_one("append") /// .expect("required"); /// assert_eq!(port, true); /// ``` /// /// Semantics: /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// let value_parser = clap::builder::FalseyValueParser::new(); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).unwrap(), true); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("100")).unwrap(), true); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("false")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("No")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("oFF")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("0")).unwrap(), false); /// ``` #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct FalseyValueParser {} impl FalseyValueParser { /// Parse false-like string values, everything else is `true` pub fn new() -> Self { Self {} } fn possible_values() -> impl Iterator> { crate::util::TRUE_LITERALS .iter() .chain(crate::util::FALSE_LITERALS.iter()) .copied() .map(|l| crate::PossibleValue::new(l).hide(l != "true" && l != "false")) } } impl TypedValueParser for FalseyValueParser { type Value = bool; fn parse_ref( &self, cmd: &crate::Command, _arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { let value = value.to_str().ok_or_else(|| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; let value = if value.is_empty() { false } else { crate::util::str_to_bool(value).unwrap_or(true) }; Ok(value) } fn possible_values( &self, ) -> Option> + '_>> { Some(Box::new(Self::possible_values())) } } impl Default for FalseyValueParser { fn default() -> Self { Self::new() } } /// Parse bool-like string values, everything else is `true` /// /// See also: /// - [`ValueParser::bool`] for different human readable bool representations /// - [`FalseyValueParser`] for assuming non-false is true /// /// # Example /// /// Usage: /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("append") /// .value_parser(clap::builder::BoolishValueParser::new()) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "true"]).unwrap(); /// let port: bool = *m.get_one("append") /// .expect("required"); /// assert_eq!(port, true); /// ``` /// /// Semantics: /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// let value_parser = clap::builder::BoolishValueParser::new(); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("100")).is_err()); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("true")).unwrap(), true); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("Yes")).unwrap(), true); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("oN")).unwrap(), true); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("1")).unwrap(), true); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("false")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("No")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("oFF")).unwrap(), false); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("0")).unwrap(), false); /// ``` #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct BoolishValueParser {} impl BoolishValueParser { /// Parse bool-like string values, everything else is `true` pub fn new() -> Self { Self {} } fn possible_values() -> impl Iterator> { crate::util::TRUE_LITERALS .iter() .chain(crate::util::FALSE_LITERALS.iter()) .copied() .map(|l| crate::PossibleValue::new(l).hide(l != "true" && l != "false")) } } impl TypedValueParser for BoolishValueParser { type Value = bool; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { let value = value.to_str().ok_or_else(|| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; let value = crate::util::str_to_bool(value).ok_or_else(|| { let arg = arg .map(|a| a.to_string()) .unwrap_or_else(|| "...".to_owned()); crate::Error::value_validation(arg, value.to_owned(), "value was not a boolean".into()) .with_cmd(cmd) })?; Ok(value) } fn possible_values( &self, ) -> Option> + '_>> { Some(Box::new(Self::possible_values())) } } impl Default for BoolishValueParser { fn default() -> Self { Self::new() } } /// Parse non-empty string values /// /// See also: /// - [`ValueParser::string`] /// /// # Example /// /// Usage: /// ```rust /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("append") /// .value_parser(clap::builder::NonEmptyStringValueParser::new()) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "true"]).unwrap(); /// let port: &String = m.get_one("append") /// .expect("required"); /// assert_eq!(port, "true"); /// ``` /// /// Semantics: /// ```rust /// # use std::ffi::OsStr; /// # use clap::builder::TypedValueParser; /// # let cmd = clap::Command::new("test"); /// # let arg = None; /// let value_parser = clap::builder::NonEmptyStringValueParser::new(); /// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).unwrap(), "random"); /// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); /// ``` #[derive(Copy, Clone, Debug)] #[non_exhaustive] pub struct NonEmptyStringValueParser {} impl NonEmptyStringValueParser { /// Parse non-empty string values pub fn new() -> Self { Self {} } } impl TypedValueParser for NonEmptyStringValueParser { type Value = String; fn parse_ref( &self, cmd: &crate::Command, arg: Option<&crate::Arg>, value: &std::ffi::OsStr, ) -> Result { if value.is_empty() { return Err(crate::Error::empty_value( cmd, &[], arg.map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()), )); } let value = value.to_str().ok_or_else(|| { crate::Error::invalid_utf8( cmd, crate::output::Usage::new(cmd).create_usage_with_title(&[]), ) })?; Ok(value.to_owned()) } } impl Default for NonEmptyStringValueParser { fn default() -> Self { Self::new() } } /// Register a type with [value_parser!][crate::value_parser!] /// /// # Example /// /// ```rust /// #[derive(Copy, Clone, Debug)] /// pub struct Custom(u32); /// /// impl clap::builder::ValueParserFactory for Custom { /// type Parser = CustomValueParser; /// fn value_parser() -> Self::Parser { /// CustomValueParser /// } /// } /// /// #[derive(Clone, Debug)] /// pub struct CustomValueParser; /// impl clap::builder::TypedValueParser for CustomValueParser { /// type Value = Custom; /// /// fn parse_ref( /// &self, /// cmd: &clap::Command, /// arg: Option<&clap::Arg>, /// value: &std::ffi::OsStr, /// ) -> Result { /// let inner = clap::value_parser!(u32); /// let val = inner.parse_ref(cmd, arg, value)?; /// Ok(Custom(val)) /// } /// } /// /// let parser: CustomValueParser = clap::value_parser!(Custom); /// ``` pub trait ValueParserFactory { /// Generated parser, usually [`ValueParser`]. /// /// It should at least be a type that supports `Into`. A non-`ValueParser` type /// allows the caller to do further initialization on the parser. type Parser; /// Create the specified [`Self::Parser`] fn value_parser() -> Self::Parser; } impl ValueParserFactory for String { type Parser = ValueParser; fn value_parser() -> Self::Parser { ValueParser::string() } } impl ValueParserFactory for std::ffi::OsString { type Parser = ValueParser; fn value_parser() -> Self::Parser { ValueParser::os_string() } } impl ValueParserFactory for std::path::PathBuf { type Parser = ValueParser; fn value_parser() -> Self::Parser { ValueParser::path_buf() } } impl ValueParserFactory for bool { type Parser = ValueParser; fn value_parser() -> Self::Parser { ValueParser::bool() } } impl ValueParserFactory for u8 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { let start: i64 = u8::MIN.into(); let end: i64 = u8::MAX.into(); RangedI64ValueParser::new().range(start..=end) } } impl ValueParserFactory for i8 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { let start: i64 = i8::MIN.into(); let end: i64 = i8::MAX.into(); RangedI64ValueParser::new().range(start..=end) } } impl ValueParserFactory for u16 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { let start: i64 = u16::MIN.into(); let end: i64 = u16::MAX.into(); RangedI64ValueParser::new().range(start..=end) } } impl ValueParserFactory for i16 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { let start: i64 = i16::MIN.into(); let end: i64 = i16::MAX.into(); RangedI64ValueParser::new().range(start..=end) } } impl ValueParserFactory for u32 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { let start: i64 = u32::MIN.into(); let end: i64 = u32::MAX.into(); RangedI64ValueParser::new().range(start..=end) } } impl ValueParserFactory for i32 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { let start: i64 = i32::MIN.into(); let end: i64 = i32::MAX.into(); RangedI64ValueParser::new().range(start..=end) } } impl ValueParserFactory for i64 { type Parser = RangedI64ValueParser; fn value_parser() -> Self::Parser { RangedI64ValueParser::new() } } impl ValueParserFactory for u64 { type Parser = RangedU64ValueParser; fn value_parser() -> Self::Parser { RangedU64ValueParser::new() } } #[doc(hidden)] #[derive(Debug)] pub struct _AutoValueParser(std::marker::PhantomData); impl _AutoValueParser { #[doc(hidden)] #[allow(clippy::new_without_default)] pub fn new() -> Self { Self(Default::default()) } } /// Unstable [`ValueParser`] /// /// Implementation may change to more specific instance in the future #[doc(hidden)] #[derive(Debug)] pub struct _AnonymousValueParser(ValueParser); #[doc(hidden)] pub mod via_prelude { use super::*; #[doc(hidden)] pub trait _ValueParserViaFactory: private::_ValueParserViaFactorySealed { type Parser; fn value_parser(&self) -> Self::Parser; } impl _ValueParserViaFactory for &&_AutoValueParser

{ type Parser = P::Parser; fn value_parser(&self) -> Self::Parser { P::value_parser() } } #[doc(hidden)] pub trait _ValueParserViaValueEnum: private::_ValueParserViaValueEnumSealed { type Output; fn value_parser(&self) -> Self::Output; } impl _ValueParserViaValueEnum for &_AutoValueParser { type Output = EnumValueParser; fn value_parser(&self) -> Self::Output { EnumValueParser::::new() } } #[doc(hidden)] pub trait _ValueParserViaFromStr: private::_ValueParserViaFromStrSealed { fn value_parser(&self) -> _AnonymousValueParser; } impl _ValueParserViaFromStr for _AutoValueParser where FromStr: std::str::FromStr + std::any::Any + Clone + Send + Sync + 'static, ::Err: Into>, { fn value_parser(&self) -> _AnonymousValueParser { let func: fn(&str) -> Result::Err> = FromStr::from_str; _AnonymousValueParser(ValueParser::new(func)) } } } /// Select a [`ValueParser`] implementation from the intended type /// /// To register a custom type with this macro, implement [`ValueParserFactory`]. /// /// # Example /// /// Usage: /// ```rust /// # use std::path::PathBuf; /// # use std::path::Path; /// let mut cmd = clap::Command::new("raw") /// .arg( /// clap::Arg::new("output") /// .value_parser(clap::value_parser!(PathBuf)) /// .required(true) /// ); /// /// let m = cmd.try_get_matches_from_mut(["cmd", "file.txt"]).unwrap(); /// let port: &PathBuf = m.get_one("output") /// .expect("required"); /// assert_eq!(port, Path::new("file.txt")); /// ``` /// /// Supported types: /// ```rust /// // Built-in types /// 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!(u16).range(3000..); /// assert_eq!(format!("{:?}", parser), "RangedI64ValueParser { bounds: (Included(3000), Included(65535)), target: PhantomData }"); /// let parser = clap::value_parser!(u64).range(3000..); /// assert_eq!(format!("{:?}", parser), "RangedU64ValueParser { bounds: (Included(3000), Unbounded), target: PhantomData }"); /// /// // FromStr types /// let parser = clap::value_parser!(usize); /// assert_eq!(format!("{:?}", parser), "_AnonymousValueParser(ValueParser::other(usize))"); /// /// // ValueEnum types /// #[derive(Copy, Clone, Debug, PartialEq, Eq)] /// enum ColorChoice { /// Always, /// Auto, /// Never, /// } /// impl clap::ValueEnum for ColorChoice { /// // ... /// # fn value_variants<'a>() -> &'a [Self] { /// # &[Self::Always, Self::Auto, Self::Never] /// # } /// # fn to_possible_value<'a>(&self) -> Option> { /// # match self { /// # Self::Always => Some(clap::PossibleValue::new("always")), /// # Self::Auto => Some(clap::PossibleValue::new("auto")), /// # Self::Never => Some(clap::PossibleValue::new("never")), /// # } /// # } /// } /// let parser = clap::value_parser!(ColorChoice); /// assert_eq!(format!("{:?}", parser), "EnumValueParser(PhantomData)"); /// ``` #[macro_export] macro_rules! value_parser { ($name:ty) => {{ use $crate::builder::via_prelude::*; let auto = $crate::builder::_AutoValueParser::<$name>::new(); (&&&auto).value_parser() }}; } mod private { use super::*; pub trait _ValueParserViaSelfSealed {} impl> _ValueParserViaSelfSealed for &&&_AutoValueParser

{} pub trait _ValueParserViaFactorySealed {} impl _ValueParserViaFactorySealed for &&_AutoValueParser

{} pub trait _ValueParserViaValueEnumSealed {} impl _ValueParserViaValueEnumSealed for &_AutoValueParser {} pub trait _ValueParserViaFromStrSealed {} impl _ValueParserViaFromStrSealed for _AutoValueParser where FromStr: std::str::FromStr + std::any::Any + Send + Sync + 'static, ::Err: Into>, { } } #[cfg(test)] mod test { use super::*; #[test] fn ensure_typed_applies_to_parse() { fn parse(_: &str) -> Result { Ok(10) } let cmd = crate::Command::new("cmd"); let arg = None; assert_eq!( TypedValueParser::parse_ref(&parse, &cmd, arg, std::ffi::OsStr::new("foo")).unwrap(), 10 ); } }