Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve ValueParser experience #4509

Merged
merged 5 commits into from Nov 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/builder/possible_value.rs
Expand Up @@ -7,8 +7,11 @@ use crate::util::eq_ignore_case;
///
/// This is used for specifying [possible values] of [Args].
///
/// **NOTE:** This struct is likely not needed for most usecases as it is only required to
/// [hide] single values from help messages and shell completions or to attach [help] to possible values.
/// See also [`PossibleValuesParser`][crate::builder::PossibleValuesParser]
///
/// **NOTE:** Most likely you can use strings, rather than `PossibleValue` as it is only required
/// to [hide] single values from help messages and shell completions or to attach [help] to
/// possible values.
///
/// # Examples
///
Expand All @@ -23,6 +26,7 @@ use crate::util::eq_ignore_case;
/// PossibleValue::new("secret speed").hide(true)
/// ]);
/// ```
///
/// [Args]: crate::Arg
/// [possible values]: crate::builder::ValueParser::possible_values
/// [hide]: PossibleValue::hide()
Expand Down
82 changes: 40 additions & 42 deletions src/builder/value_parser.rs
Expand Up @@ -508,6 +508,41 @@ where
}
}

/// Create a [`ValueParser`] with [`PossibleValuesParser`]
///
/// See [`PossibleValuesParser`] for more flexibility in creating the
/// [`PossibleValue`][crate::builder::PossibleValue]s.
///
/// # Examples
///
/// ```rust
/// let possible = vec!["always", "auto", "never"];
/// let mut cmd = clap::Command::new("raw")
/// .arg(
/// clap::Arg::new("color")
/// .long("color")
/// .value_parser(possible)
/// .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<P> From<Vec<P>> for ValueParser
where
P: Into<super::PossibleValue>,
{
fn from(values: Vec<P>) -> 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 {
Expand Down Expand Up @@ -957,31 +992,11 @@ impl Default for PathBufValueParser {
///
/// ```rust
/// # use std::ffi::OsStr;
/// # use clap::ColorChoice;
/// # 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<clap::builder::PossibleValue> {
/// match self {
/// Self::Always => Some(clap::builder::PossibleValue::new("always")),
/// Self::Auto => Some(clap::builder::PossibleValue::new("auto")),
/// Self::Never => Some(clap::builder::PossibleValue::new("never")),
/// }
/// }
/// }
///
/// // Usage
/// let mut cmd = clap::Command::new("raw")
/// .arg(
Expand Down Expand Up @@ -1086,8 +1101,9 @@ impl<E: crate::ValueEnum + Clone + Send + Sync + 'static> Default for EnumValueP
/// Verify the value is from an enumerated set of [`PossibleValue`][crate::builder::PossibleValue].
///
/// See also:
/// - [`EnumValueParser`] for directly supporting `enum`s
/// - [`TypedValueParser::map`] for adapting values to a more specialized type
/// - [`EnumValueParser`] for directly supporting [`ValueEnum`][crate::ValueEnum] types
/// - [`TypedValueParser::map`] for adapting values to a more specialized type, like an external
/// enums that can't implement [`ValueEnum`][crate::ValueEnum]
///
/// # Example
///
Expand Down Expand Up @@ -2327,6 +2343,7 @@ pub mod via_prelude {
///
/// Example mappings:
/// ```rust
/// # use clap::ColorChoice;
/// // Built-in types
/// let parser = clap::value_parser!(String);
/// assert_eq!(format!("{:?}", parser), "ValueParser::string");
Expand All @@ -2344,25 +2361,6 @@ pub mod via_prelude {
/// 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<clap::builder::PossibleValue> {
/// # match self {
/// # Self::Always => Some(clap::builder::PossibleValue::new("always")),
/// # Self::Auto => Some(clap::builder::PossibleValue::new("auto")),
/// # Self::Never => Some(clap::builder::PossibleValue::new("never")),
/// # }
/// # }
/// }
/// let parser = clap::value_parser!(ColorChoice);
/// assert_eq!(format!("{:?}", parser), "EnumValueParser(PhantomData)");
/// ```
Expand Down
4 changes: 0 additions & 4 deletions src/lib.rs
Expand Up @@ -102,11 +102,7 @@ pub use crate::builder::Command;
pub use crate::builder::ValueHint;
pub use crate::builder::{Arg, ArgGroup};
pub use crate::parser::ArgMatches;
#[cfg(feature = "color")]
pub use crate::util::color::ColorChoice;
#[cfg(not(feature = "color"))]
#[allow(unused_imports)]
pub(crate) use crate::util::color::ColorChoice;
pub use crate::util::Id;

/// Command Line Argument Parser Error
Expand Down
22 changes: 22 additions & 0 deletions src/util/color.rs
Expand Up @@ -64,6 +64,28 @@ impl Default for ColorChoice {
}
}

impl std::fmt::Display for ColorChoice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}

impl std::str::FromStr for ColorChoice {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("Invalid variant: {}", s))
}
}

impl ValueEnum for ColorChoice {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Auto, Self::Always, Self::Never]
Expand Down