Repository: clap-rs/clap
Pull request #4049 (2022-08-09 18:50:36)

fix(derive): Add "id" attribute
Previously the Arg id was set with the "name" attribute. This allows use of an "id" attribute to match the underlying struct.

A side effect of this is that the "id" attribute may also be used on Commands. This isn't desired, but given the current architecture of the attribute parser, it's hard to avoid.

Fixes: #3785


danielparks: I think the lint check failure is unrelated to my changes. The commit subject is slightly longer than 50 characters, though. I can fix that if you like.
epage: > I think the lint check failure is unrelated to my changes. The commit subject is slightly longer than 50 characters, though. I can fix that if you like.

The lint failure is

>  96c57b0: error Subject should be capitalized but found `allow`

I need to go back and do some fixes to committed since its still not all working with the git security fix *sigh*
epage: Note: it also says to not fuss too much over the commit messages.  Its preferred but not required to have them be clean but there isn't a good way to do that with github actions

Patch:
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs
index b7fc1f39..0c6824f1 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/attrs.rs
@@ -400,7 +400,7 @@ impl Attrs {
     }
 
     fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
-        if name == "name" {
+        if name == "name" || name == "id" {
             self.name = Name::Assigned(quote!(#arg));
         } else if name == "value_parser" {
             self.value_parser = Some(ValueParser::Explicit(Method::new(name, quote!(#arg))));
diff --git a/src/_derive/mod.rs b/src/_derive/mod.rs
index 3219c183..f271e01f 100644
--- a/src/_derive/mod.rs
+++ b/src/_derive/mod.rs
@@ -181,7 +181,7 @@
 //! - e.g. `#[clap(max_values(3))]` would translate to `arg.max_values(3)`
 //!
 //! **Magic attributes**:
-//! - `name = <expr>`: [`Arg::id`][crate::Arg::id]
+//! - `id = <expr>`: [`Arg::id`][crate::Arg::id]
 //!   - When not present: case-converted field name is used
 //! - `value_parser [= <expr>]`: [`Arg::value_parser`][crate::Arg::value_parser]
 //!   - When not present: will auto-select an implementation based on the field type using


Test patch:
diff --git a/tests/derive/naming.rs b/tests/derive/naming.rs
index e3018a02..23df16b3 100644
--- a/tests/derive/naming.rs
+++ b/tests/derive/naming.rs
@@ -57,6 +57,34 @@ fn test_standalone_long_ignores_afterwards_defined_custom_name() {
     );
 }
 
+#[test]
+fn test_standalone_long_uses_previous_defined_custom_id() {
+    #[derive(Parser, Debug, PartialEq)]
+    struct Opt {
+        #[clap(id = "foo", long)]
+        foo_option: bool,
+    }
+
+    assert_eq!(
+        Opt { foo_option: true },
+        Opt::try_parse_from(&["test", "--foo"]).unwrap()
+    );
+}
+
+#[test]
+fn test_standalone_long_ignores_afterwards_defined_custom_id() {
+    #[derive(Parser, Debug, PartialEq)]
+    struct Opt {
+        #[clap(long, id = "foo")]
+        foo_option: bool,
+    }
+
+    assert_eq!(
+        Opt { foo_option: true },
+        Opt::try_parse_from(&["test", "--foo-option"]).unwrap()
+    );
+}
+
 #[test]
 fn test_standalone_short_generates_kebab_case() {
     #[derive(Parser, Debug, PartialEq)]
@@ -114,6 +142,34 @@ fn test_standalone_short_ignores_afterwards_defined_custom_name() {
     );
 }
 
+#[test]
+fn test_standalone_short_uses_previous_defined_custom_id() {
+    #[derive(Parser, Debug, PartialEq)]
+    struct Opt {
+        #[clap(id = "option", short)]
+        foo_option: bool,
+    }
+
+    assert_eq!(
+        Opt { foo_option: true },
+        Opt::try_parse_from(&["test", "-o"]).unwrap()
+    );
+}
+
+#[test]
+fn test_standalone_short_ignores_afterwards_defined_custom_id() {
+    #[derive(Parser, Debug, PartialEq)]
+    struct Opt {
+        #[clap(short, id = "option")]
+        foo_option: bool,
+    }
+
+    assert_eq!(
+        Opt { foo_option: true },
+        Opt::try_parse_from(&["test", "-f"]).unwrap()
+    );
+}
+
 #[test]
 fn test_standalone_long_uses_previous_defined_casing() {
     #[derive(Parser, Debug, PartialEq)]