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

Issue:
create skip_with_default attribute for Subcommands
### 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.17

### Describe your use case

The enum that I'm using with clap derive also serves as an external API so there are certain things it must have. It looks like this:

```rust
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Parser)]
#[serde(rename_all = "snake_case")]
pub enum Msg {
    #[command(subcommand)]
    Base(BaseMsg),
    #[clap(skip_with_default=my_default_function)] // This is the feature I would like to add
    Receive(ExternalStruct),
    AddAssets {
        #[arg(value_parser=serde_json::from_str::<[AssetInfo; 2]>)]
        assets: [AssetInfo; 2],
    },
```

Because Default is not implemented for ExternalStruct I cannot skip the variant regularly.

### Describe the solution you'd like

An attribute like 
```
#[clap(skip_with_default=my_default_function)]
```
Should work perfectly for me. I'm currently not aware of any other solution.

### Alternatives, if applicable

_No response_

### Additional Context

_No response_


Discussion / hints:
The thing that is missing for me is why `#[command(skip]` is insufficient.  Clap would have no reason to instantiate a skipped variant, so defaults do not matter.
> The thing that is missing for me is why `#[command(skip]` is insufficient. Clap would have no reason to instantiate a skipped variant, so defaults do not matter.

The compiler wants the trait bound FromArgMatches to be satisfied, I assumed it was related to not implementing Default? What is the issue then?

```
error[E0277]: the trait bound `ExternalStruct: FromArgMatches` is not satisfied
  --> packages/lending/src/market.rs:26:71
   |
26 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Parser)]
   |                                                                       ^^^^^^ the trait `FromArgMatches` is not implemented for `ExternalStruct`
```

Fix patch:
diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs
index b437e42c076..009684bfc2c 100644
--- a/clap_derive/src/derives/subcommand.rs
+++ b/clap_derive/src/derives/subcommand.rs
@@ -354,11 +354,15 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|(variant, item)| {
-            if let Kind::ExternalSubcommand = &*item.kind() {
-                ext_subcmd = true;
-                None
-            } else {
-                Some((variant, item))
+            let kind = item.kind();
+            match &*kind {
+                Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
+
+                Kind::ExternalSubcommand => {
+                    ext_subcmd = true;
+                    None
+                }
+                Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
             }
         })
         .partition(|(_, item)| {
@@ -414,52 +418,56 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|(variant, item)| {
-            if let Kind::ExternalSubcommand = &*item.kind() {
-                if ext_subcmd.is_some() {
-                    abort!(
-                        item.kind().span(),
-                        "Only one variant can be marked with `external_subcommand`, \
+            let kind = item.kind();
+            match &*kind {
+                Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
+
+                Kind::ExternalSubcommand => {
+                    if ext_subcmd.is_some() {
+                        abort!(
+                            item.kind().span(),
+                            "Only one variant can be marked with `external_subcommand`, \
                          this is the second"
-                    );
-                }
+                        );
+                    }
 
-                let ty = match variant.fields {
-                    Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
+                    let ty = match variant.fields {
+                        Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
 
-                    _ => abort!(
-                        variant,
-                        "The enum variant marked with `external_subcommand` must be \
+                        _ => abort!(
+                            variant,
+                            "The enum variant marked with `external_subcommand` must be \
                          a single-typed tuple, and the type must be either `Vec<String>` \
                          or `Vec<OsString>`."
-                    ),
-                };
-
-                let (span, str_ty) = match subty_if_name(ty, "Vec") {
-                    Some(subty) => {
-                        if is_simple_ty(subty, "String") {
-                            (subty.span(), quote!(::std::string::String))
-                        } else if is_simple_ty(subty, "OsString") {
-                            (subty.span(), quote!(::std::ffi::OsString))
-                        } else {
-                            abort!(
-                                ty.span(),
-                                "The type must be either `Vec<String>` or `Vec<OsString>` \
+                        ),
+                    };
+
+                    let (span, str_ty) = match subty_if_name(ty, "Vec") {
+                        Some(subty) => {
+                            if is_simple_ty(subty, "String") {
+                                (subty.span(), quote!(::std::string::String))
+                            } else if is_simple_ty(subty, "OsString") {
+                                (subty.span(), quote!(::std::ffi::OsString))
+                            } else {
+                                abort!(
+                                    ty.span(),
+                                    "The type must be either `Vec<String>` or `Vec<OsString>` \
                                  to be used with `external_subcommand`."
-                            );
+                                );
+                            }
                         }
-                    }
 
-                    None => abort!(
-                        ty.span(),
-                        "The type must be either `Vec<String>` or `Vec<OsString>` \
+                        None => abort!(
+                            ty.span(),
+                            "The type must be either `Vec<String>` or `Vec<OsString>` \
                          to be used with `external_subcommand`."
-                    ),
-                };
+                        ),
+                    };
 
-                ext_subcmd = Some((span, &variant.ident, str_ty));
-                None
-            } else {
-                Some((variant, item))
+                    ext_subcmd = Some((span, &variant.ident, str_ty));
+                    None
+                }
+                Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
             }
         })
         .partition(|(_, item)| {
@@ -563,10 +571,15 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
     let (flatten, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|(variant, item)| {
-            match &*item.kind() {
+            let kind = item.kind();
+            match &*kind {
                 // Fallback to `from_arg_matches_mut`
-                Kind::ExternalSubcommand => None,
-                _ => Some((variant, item)),
+                Kind::Skip(_, _)
+                | Kind::Arg(_)
+                | Kind::FromGlobal(_)
+                | Kind::Value
+                | Kind::ExternalSubcommand => None,
+                Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
             }
         })
         .partition(|(_, item)| {


Test patch:
diff --git a/tests/derive/subcommands.rs b/tests/derive/subcommands.rs
index ad42616a9be..2252456f082 100644
--- a/tests/derive/subcommands.rs
+++ b/tests/derive/subcommands.rs
@@ -532,8 +532,24 @@ fn skip_subcommand() {
         #[allow(dead_code)]
         #[command(skip)]
         Skip,
+
+        #[allow(dead_code)]
+        #[command(skip)]
+        Other(Other),
     }
 
+    #[allow(dead_code)]
+    #[derive(Debug, PartialEq, Eq)]
+    enum Other {
+        One,
+        Twp,
+    }
+
+    assert!(Subcommands::has_subcommand("add"));
+    assert!(Subcommands::has_subcommand("remove"));
+    assert!(!Subcommands::has_subcommand("skip"));
+    assert!(!Subcommands::has_subcommand("other"));
+
     assert_eq!(
         Opt::try_parse_from(&["test", "add"]).unwrap(),
         Opt {
@@ -553,6 +569,12 @@ fn skip_subcommand() {
         res.unwrap_err().kind(),
         clap::error::ErrorKind::InvalidSubcommand,
     );
+
+    let res = Opt::try_parse_from(&["test", "other"]);
+    assert_eq!(
+        res.unwrap_err().kind(),
+        clap::error::ErrorKind::InvalidSubcommand,
+    );
 }
 
 #[test]