Repository: clap-rs/clap (language: rust)

Issue:
Bring back `possible_values` (because `ValueEnum` does not fit everything)
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Clap Version

4.0.26

### Describe your use case

There are cases where I want to only limit user choices between strings. The behavior of the program remain exactly the same (i.e. no branches related to the user choice). For example: Choosing a weekday only to be printed out, choosing a profile between "release" and "debug" only to infer the binary path, etc.

In such cases, an enum (that derives `ValueEnum`) would be inconvenient since it requires me to write `match` to convert every single enum variant to corresponding strings.

### Describe the solution you'd like

Bring back `possible_values` or something similar.

### Alternatives, if applicable

Derive [strum](https://crates.io/crates/strum) on the value enum. However, this method is not optimal as the resulting program still parse a string into a value enum only to convert it back to the original string.

Use `value_parser = [PossibleValue::new("..."), ...]` but this is unwieldy and cumbersome, especially when written in derive code.

### Additional Context

I exclusively use `#[derive(clap::Parser)]`. I've never use the args builder directly.


Discussion / hints:
> Use `value_parser = [PossibleValue::new("..."), ...]` but this is unwieldy and cumbersome, especially when written in derive code.

You can also do `value_parser = ["always", "auto", "never"])` as shown in the (builder) example for [`Arg::value_parser`](https://docs.rs/clap/latest/clap/builder/struct.Arg.html#method.value_parser)
@epage  I apologize, the fault lies with me for not reading the documentation carefully. I searched for the term "possible" on the docs.rs page for clap and find only the `PossibleValue` struct. The example for `PossibleValue` does not includes this convenient shorthand. May I request adding this shorthand to the code examples for `PossibleValue`?
@epage 

`.value_parser(["always", "auto", "never"])` only seems to accept a list as array.

It doesn't seem to be possible to pass a runtime slice or `Vec` holding strings into there, or am I missing something?

Fix patch:
diff --git a/src/builder/possible_value.rs b/src/builder/possible_value.rs
index 03964fe118b..7a313ad2585 100644
--- a/src/builder/possible_value.rs
+++ b/src/builder/possible_value.rs
@@ -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
 ///
@@ -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()
diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs
index 1b2b51dba13..4681857acc1 100644
--- a/src/builder/value_parser.rs
+++ b/src/builder/value_parser.rs
@@ -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 {
@@ -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(
@@ -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
 ///
@@ -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");
@@ -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)");
 /// ```
diff --git a/src/lib.rs b/src/lib.rs
index 62b092929fd..393e1f0b9a7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
diff --git a/src/util/color.rs b/src/util/color.rs
index c13d20d3ea8..7c7468eafd7 100644
--- a/src/util/color.rs
+++ b/src/util/color.rs
@@ -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]


Test patch: