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

Issue:
Can't use ValueParser to replace PossibleValues in v4
### 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

### Rust Version

1.64.0

### Clap Version

4.08

### Minimal reproducible code

```rust
static COLOR_RESOLUTIONS: [&str; 5] = ["16", "32", "64", "128", "256"];

#[arg(
    long,
    value_name = "VALUE",
    requires = "image",
    default_value_t = 16usize,
    // possible_values = ["16", "32", "64", "128", "256"], // Used to work before v4
    value_parser = PossibleValuesParser::new(COLOR_RESOLUTIONS) // fails
)]
pub color_resolution: usize,
```

```sh
$ cargo run

thread 'main' panicked at 'Mismatch between definition and access of `color_resolution`. Could not downcast to TypeId { t: 13834754221672687376 }, need to downcast to TypeId { t: 5005868189860995316 }

```



Discussion / hints:

Fix patch:
diff --git a/examples/typed-derive.md b/examples/typed-derive.md
index 4418fe5ba65..d6aa71012a7 100644
--- a/examples/typed-derive.md
+++ b/examples/typed-derive.md
@@ -6,19 +6,21 @@ $ typed-derive --help
 Usage: typed-derive[EXE] [OPTIONS]
 
 Options:
-  -O <OPTIMIZATION>      Implicitly using `std::str::FromStr`
-  -I <DIR>               Allow invalid UTF-8 paths
-      --bind <BIND>      Handle IP addresses
-      --sleep <SLEEP>    Allow human-readable durations
-  -D <DEFINES>           Hand-written parser for tuples
-  -h, --help             Print help information
+  -O <OPTIMIZATION>            Implicitly using `std::str::FromStr`
+  -I <DIR>                     Allow invalid UTF-8 paths
+      --bind <BIND>            Handle IP addresses
+      --sleep <SLEEP>          Allow human-readable durations
+  -D <DEFINES>                 Hand-written parser for tuples
+      --port <PORT>            Support for discrete numbers [default: 22] [possible values: 22, 80]
+      --log-level <LOG_LEVEL>  Support enums from a foreign crate that don't implement `ValueEnum` [default: info] [possible values: info, debug, info, warn, error]
+  -h, --help                   Print help information
 
 ```
 
 Optimization-level (number)
 ```console
 $ typed-derive -O 1
-Args { optimization: Some(1), include: None, bind: None, sleep: None, defines: [] }
+Args { optimization: Some(1), include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Info }
 
 $ typed-derive -O plaid
 ? failed
@@ -31,14 +33,14 @@ For more information try '--help'
 Include (path)
 ```console
 $ typed-derive -I../hello
-Args { optimization: None, include: Some("../hello"), bind: None, sleep: None, defines: [] }
+Args { optimization: None, include: Some("../hello"), bind: None, sleep: None, defines: [], port: 22, log_level: Info }
 
 ```
 
 IP Address
 ```console
 $ typed-derive --bind 192.0.0.1
-Args { optimization: None, include: None, bind: Some(192.0.0.1), sleep: None, defines: [] }
+Args { optimization: None, include: None, bind: Some(192.0.0.1), sleep: None, defines: [], port: 22, log_level: Info }
 
 $ typed-derive --bind localhost
 ? failed
@@ -51,7 +53,7 @@ For more information try '--help'
 Time
 ```console
 $ typed-derive --sleep 10s
-Args { optimization: None, include: None, bind: None, sleep: Some(Duration(10s)), defines: [] }
+Args { optimization: None, include: None, bind: None, sleep: Some(Duration(10s)), defines: [], port: 22, log_level: Info }
 
 $ typed-derive --sleep forever
 ? failed
@@ -64,7 +66,7 @@ For more information try '--help'
 Defines (key-value pairs)
 ```console
 $ typed-derive -D Foo=10 -D Alice=30
-Args { optimization: None, include: None, bind: None, sleep: None, defines: [("Foo", 10), ("Alice", 30)] }
+Args { optimization: None, include: None, bind: None, sleep: None, defines: [("Foo", 10), ("Alice", 30)], port: 22, log_level: Info }
 
 $ typed-derive -D Foo
 ? failed
@@ -79,3 +81,51 @@ error: Invalid value "Foo=Bar" for '-D <DEFINES>': invalid digit found in string
 For more information try '--help'
 
 ```
+
+Discrete numbers
+```console
+$ typed-derive --port 22
+Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Info }
+
+$ typed-derive --port 80
+Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 80, log_level: Info }
+
+$ typed-derive --port
+? failed
+error: The argument '--port <PORT>' requires a value but none was supplied
+  [possible values: 22, 80]
+
+For more information try '--help'
+
+$ typed-derive --port 3000
+? failed
+error: "3000" isn't a valid value for '--port <PORT>'
+  [possible values: 22, 80]
+
+For more information try '--help'
+
+```
+
+Enums from crates that can't implement `ValueEnum`
+```console
+$ typed-derive --log-level debug
+Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Debug }
+
+$ typed-derive --log-level error
+Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Error }
+
+$ typed-derive --log-level
+? failed
+error: The argument '--log-level <LOG_LEVEL>' requires a value but none was supplied
+  [possible values: info, debug, info, warn, error]
+
+For more information try '--help'
+
+$ typed-derive --log-level critical
+? failed
+error: "critical" isn't a valid value for '--log-level <LOG_LEVEL>'
+  [possible values: info, debug, info, warn, error]
+
+For more information try '--help'
+
+```
diff --git a/examples/typed-derive.rs b/examples/typed-derive.rs
index 1bd03fcf52c..bdf8a580911 100644
--- a/examples/typed-derive.rs
+++ b/examples/typed-derive.rs
@@ -1,7 +1,9 @@
+use clap::builder::TypedValueParser as _;
 use clap::Parser;
 use std::error::Error;
 
 #[derive(Parser, Debug)] // requires `derive` feature
+#[command(term_width = 0)] // Just to make testing across clap features easier
 struct Args {
     /// Implicitly using `std::str::FromStr`
     #[arg(short = 'O')]
@@ -22,6 +24,24 @@ struct Args {
     /// Hand-written parser for tuples
     #[arg(short = 'D', value_parser = parse_key_val::<String, i32>)]
     defines: Vec<(String, i32)>,
+
+    /// Support for discrete numbers
+    #[arg(
+        long,
+        default_value_t = 22,
+        value_parser = clap::builder::PossibleValuesParser::new(["22", "80"])
+            .map(|s| s.parse::<usize>().unwrap()),
+    )]
+    port: usize,
+
+    /// Support enums from a foreign crate that don't implement `ValueEnum`
+    #[arg(
+        long,
+        default_value_t = foreign_crate::LogLevel::Info,
+        value_parser = clap::builder::PossibleValuesParser::new(["info", "debug", "info", "warn", "error"])
+            .map(|s| s.parse::<foreign_crate::LogLevel>().unwrap()),
+    )]
+    log_level: foreign_crate::LogLevel,
 }
 
 /// Parse a single key-value pair
@@ -38,6 +58,44 @@ where
     Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
 }
 
+mod foreign_crate {
+    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
+    pub enum LogLevel {
+        Trace,
+        Debug,
+        Info,
+        Warn,
+        Error,
+    }
+
+    impl std::fmt::Display for LogLevel {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            let s = match self {
+                Self::Trace => "trace",
+                Self::Debug => "debug",
+                Self::Info => "info",
+                Self::Warn => "warn",
+                Self::Error => "error",
+            };
+            s.fmt(f)
+        }
+    }
+    impl std::str::FromStr for LogLevel {
+        type Err = String;
+
+        fn from_str(s: &str) -> Result<Self, Self::Err> {
+            match s {
+                "trace" => Ok(Self::Trace),
+                "debug" => Ok(Self::Debug),
+                "info" => Ok(Self::Info),
+                "warn" => Ok(Self::Warn),
+                "error" => Ok(Self::Error),
+                _ => Err(format!("Unknown log level: {s}")),
+            }
+        }
+    }
+}
+
 fn main() {
     let args = Args::parse();
     println!("{:?}", args);


Test patch: