Repository: clap-rs/clap
Pull request #3541 (2022-03-07 03:58:22)

fix: multiple default values combined with `ArgEnum` / `PossibleValues`
Fixes a regression when combining `ArgEnum` and a `default_value` with multiple values, e.g.:

```rust
#[derive(ArgEnum)]
enum Arg {
  Foo,
  Bar,
}

#[derive(Parser)]
struct Opts {
  #[clarp(arg_enum, default_value="foo,bar", value_delimiter=',')]
  arg: Vec<Arg>
}
```
This results in the following error and backtrace:

```
thread 'arg_enum::vec_type_default_value' panicked at 'Argument `arg`'s default_value=foo,bar doesn't match possible values', src/build/debug_asserts.rs:691:21
stack backtrace:
   0: rust_begin_unwind
             at /rustc/8769f4ef2fe1efddd1f072485f97f568e7328f79/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/8769f4ef2fe1efddd1f072485f97f568e7328f79/library/core/src/panicking.rs:143:14
   2: clap::build::debug_asserts::assert_defaults
             at ./src/build/debug_asserts.rs:691:21
   3: clap::build::debug_asserts::assert_arg
             at ./src/build/debug_asserts.rs:623:5
   4: clap::build::debug_asserts::assert_app
             at ./src/build/debug_asserts.rs:57:9
   5: clap::build::command::App::_build
             at ./src/build/command.rs:4101:13
   6: clap::build::command::App::_do_parse
             at ./src/build/command.rs:3965:9
   7: clap::build::command::App::try_get_matches_from_mut
             at ./src/build/command.rs:681:9
   8: clap::build::command::App::try_get_matches_from
             at ./src/build/command.rs:597:9
   9: clap::derive::Parser::try_parse_from
             at ./src/derive.rs:126:23
  10: derive::arg_enum::vec_type_default_value
             at ./tests/derive/arg_enum.rs:480:9
  11: derive::arg_enum::vec_type_default_value::{{closure}}
             at ./tests/derive/arg_enum.rs:462:1
  12: core::ops::function::FnOnce::call_once
             at /rustc/8769f4ef2fe1efddd1f072485f97f568e7328f79/library/core/src/ops/function.rs:227:5
  13: core::ops::function::FnOnce::call_once
             at /rustc/8769f4ef2fe1efddd1f072485f97f568e7328f79/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

This is essentially the same issue as https://github.com/clap-rs/clap/issues/3514, but with `PossibleValues` instead of `FromStr`.


Patch:
diff --git a/src/build/debug_asserts.rs b/src/build/debug_asserts.rs
index e811db01..5e6baf66 100644
--- a/src/build/debug_asserts.rs
+++ b/src/build/debug_asserts.rs
@@ -675,15 +675,29 @@ fn assert_defaults<'d>(
     for default_os in defaults {
         if let Some(default_s) = default_os.to_str() {
             if !arg.possible_vals.is_empty() {
-                assert!(
-                    arg.possible_vals.iter().any(|possible_val| {
-                        possible_val.matches(default_s, arg.is_ignore_case_set())
-                    }),
-                    "Argument `{}`'s {}={} doesn't match possible values",
-                    arg.name,
-                    field,
-                    default_s
-                );
+                if let Some(delim) = arg.get_value_delimiter() {
+                    for part in default_s.split(delim) {
+                        assert!(
+                            arg.possible_vals.iter().any(|possible_val| {
+                                possible_val.matches(part, arg.is_ignore_case_set())
+                            }),
+                            "Argument `{}`'s {}={} doesn't match possible values",
+                            arg.name,
+                            field,
+                            part
+                        )
+                    }
+                } else {
+                    assert!(
+                        arg.possible_vals.iter().any(|possible_val| {
+                            possible_val.matches(default_s, arg.is_ignore_case_set())
+                        }),
+                        "Argument `{}`'s {}={} doesn't match possible values",
+                        arg.name,
+                        field,
+                        default_s
+                    );
+                }
             }
 
             if let Some(validator) = arg.validator.as_ref() {


Test patch:
diff --git a/tests/derive/arg_enum.rs b/tests/derive/arg_enum.rs
index aaf3ccdc..957e870d 100644
--- a/tests/derive/arg_enum.rs
+++ b/tests/derive/arg_enum.rs
@@ -457,3 +457,39 @@ fn option_vec_type() {
     );
     assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
 }
+
+#[test]
+fn vec_type_default_value() {
+    #[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
+    enum ArgChoice {
+        Foo,
+        Bar,
+        Baz,
+    }
+
+    #[derive(Parser, PartialEq, Debug)]
+    struct Opt {
+        #[clap(
+            arg_enum,
+            short,
+            long,
+            default_value = "foo,bar",
+            value_delimiter = ','
+        )]
+        arg: Vec<ArgChoice>,
+    }
+
+    assert_eq!(
+        Opt {
+            arg: vec![ArgChoice::Foo, ArgChoice::Bar]
+        },
+        Opt::try_parse_from(&[""]).unwrap()
+    );
+
+    assert_eq!(
+        Opt {
+            arg: vec![ArgChoice::Foo, ArgChoice::Baz]
+        },
+        Opt::try_parse_from(&["", "-a", "foo,baz"]).unwrap()
+    );
+}