Repository: mozilla/cbindgen (language: rust)

Issue:
Add rename-all=prefix:
This adds a new rename-all annotation which allows setting a specific prefix to all fields. For example:

```rust
/// cbindgen:rename-all=prefix:ERR_
#[repr(i32)]
pub enum Error {
   BAD = 0,
   VERY_BAD = 1
}
```

While in principle this can be added to the config file, it's primarily useful for overrides on a case-by-case basis.


Discussion / hints:

Fix patch:
diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs
index d6ec313a..8927b8be 100644
--- a/src/bindgen/ir/enumeration.rs
+++ b/src/bindgen/ir/enumeration.rs
@@ -152,9 +152,10 @@ impl EnumVariant {
             annotations.add_default("derive-ostream", AnnotationValue::Bool(b));
         }
 
-        let body_rule = enum_annotations
-            .parse_atom::<RenameRule>("rename-variant-name-fields")
-            .unwrap_or(config.enumeration.rename_variant_name_fields);
+        let body_rule = enum_annotations.parse_atom::<RenameRule>("rename-variant-name-fields");
+        let body_rule = body_rule
+            .as_ref()
+            .unwrap_or(&config.enumeration.rename_variant_name_fields);
 
         let body = match variant.fields {
             syn::Fields::Unit => VariantBody::Empty(annotations),
@@ -555,10 +556,10 @@ impl Item for Enum {
             }
         }
 
-        let rules = self
-            .annotations
-            .parse_atom::<RenameRule>("rename-all")
-            .unwrap_or(config.enumeration.rename_variants);
+        let rules = self.annotations.parse_atom::<RenameRule>("rename-all");
+        let rules = rules
+            .as_ref()
+            .unwrap_or(&config.enumeration.rename_variants);
 
         if let Some(r) = rules.not_none() {
             self.variants = self
diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs
index 79adfce9..f25d2f88 100644
--- a/src/bindgen/ir/function.rs
+++ b/src/bindgen/ir/function.rs
@@ -156,10 +156,8 @@ impl Function {
         self.ret.rename_for_config(config, &generic_params);
 
         // Apply rename rules to argument names
-        let rules = self
-            .annotations
-            .parse_atom::<RenameRule>("rename-all")
-            .unwrap_or(config.function.rename_args);
+        let rules = self.annotations.parse_atom::<RenameRule>("rename-all");
+        let rules = rules.as_ref().unwrap_or(&config.function.rename_args);
 
         if let Some(r) = rules.not_none() {
             let args = std::mem::take(&mut self.args);
diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs
index 9b33a15c..d805d69c 100644
--- a/src/bindgen/ir/structure.rs
+++ b/src/bindgen/ir/structure.rs
@@ -339,10 +339,10 @@ impl Item for Struct {
         {
             let names = self.fields.iter_mut().map(|field| &mut field.name);
 
-            let field_rules = self
-                .annotations
-                .parse_atom::<RenameRule>("rename-all")
-                .unwrap_or(config.structure.rename_fields);
+            let field_rules = self.annotations.parse_atom::<RenameRule>("rename-all");
+            let field_rules = field_rules
+                .as_ref()
+                .unwrap_or(&config.structure.rename_fields);
 
             if let Some(o) = self.annotations.list("field-names") {
                 for (dest, src) in names.zip(o) {
diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs
index 410e21a2..9c425837 100644
--- a/src/bindgen/ir/union.rs
+++ b/src/bindgen/ir/union.rs
@@ -168,10 +168,8 @@ impl Item for Union {
             field.ty.rename_for_config(config, &self.generic_params);
         }
 
-        let rules = self
-            .annotations
-            .parse_atom::<RenameRule>("rename-all")
-            .unwrap_or(config.structure.rename_fields);
+        let rules = self.annotations.parse_atom::<RenameRule>("rename-all");
+        let rules = rules.as_ref().unwrap_or(&config.structure.rename_fields);
 
         if let Some(o) = self.annotations.list("field-names") {
             let mut overriden_fields = Vec::new();
diff --git a/src/bindgen/rename.rs b/src/bindgen/rename.rs
index f594939e..6f4928ef 100644
--- a/src/bindgen/rename.rs
+++ b/src/bindgen/rename.rs
@@ -28,7 +28,7 @@ impl<'a> IdentifierType<'a> {
 }
 
 /// A rule to apply to an identifier when generating bindings.
-#[derive(Debug, Clone, Copy, Default)]
+#[derive(Debug, Clone, Default)]
 pub enum RenameRule {
     /// Do not apply any renaming. The default.
     #[default]
@@ -50,10 +50,12 @@ pub enum RenameRule {
     /// Converts the identifier to SCREAMING_SNAKE_CASE and prefixes enum variants
     /// with the enum name.
     QualifiedScreamingSnakeCase,
+    /// Adds a given prefix
+    Prefix(String),
 }
 
 impl RenameRule {
-    pub(crate) fn not_none(self) -> Option<Self> {
+    pub(crate) fn not_none(&self) -> Option<&Self> {
         match self {
             RenameRule::None => None,
             other => Some(other),
@@ -61,7 +63,7 @@ impl RenameRule {
     }
 
     /// Applies the rename rule to a string
-    pub fn apply<'a>(self, text: &'a str, context: IdentifierType) -> Cow<'a, str> {
+    pub fn apply<'a>(&self, text: &'a str, context: IdentifierType) -> Cow<'a, str> {
         use heck::*;
 
         if text.is_empty() {
@@ -90,6 +92,7 @@ impl RenameRule {
                 result.push_str(&RenameRule::ScreamingSnakeCase.apply(text, context));
                 result
             }
+            RenameRule::Prefix(prefix) => prefix.to_owned() + text,
         })
     }
 }
@@ -98,6 +101,9 @@ impl FromStr for RenameRule {
     type Err = String;
 
     fn from_str(s: &str) -> Result<RenameRule, Self::Err> {
+        const PREFIX: &str = "prefix:";
+        const PREFIX_LEN: usize = PREFIX.len();
+
         match s {
             "none" => Ok(RenameRule::None),
             "None" => Ok(RenameRule::None),
@@ -132,6 +138,8 @@ impl FromStr for RenameRule {
             "QualifiedScreamingSnakeCase" => Ok(RenameRule::QualifiedScreamingSnakeCase),
             "qualified_screaming_snake_case" => Ok(RenameRule::QualifiedScreamingSnakeCase),
 
+            s if s.starts_with(PREFIX) => Ok(RenameRule::Prefix(s[PREFIX_LEN..].to_string())),
+
             _ => Err(format!("Unrecognized RenameRule: '{}'.", s)),
         }
     }


Test patch:
diff --git a/tests/expectations/rename_case.c b/tests/expectations/rename_case.c
index c5748231..02764566 100644
--- a/tests/expectations/rename_case.c
+++ b/tests/expectations/rename_case.c
@@ -12,3 +12,5 @@ void test_snake_case(int32_t foo_bar);
 void test_screaming_snake_case(int32_t FOO_BAR);
 
 void test_gecko_case(int32_t aFooBar);
+
+void test_prefix(int32_t prefix_foo_bar);
diff --git a/tests/expectations/rename_case.compat.c b/tests/expectations/rename_case.compat.c
index 1d2cb1c5..ba66eeee 100644
--- a/tests/expectations/rename_case.compat.c
+++ b/tests/expectations/rename_case.compat.c
@@ -17,6 +17,8 @@ void test_screaming_snake_case(int32_t FOO_BAR);
 
 void test_gecko_case(int32_t aFooBar);
 
+void test_prefix(int32_t prefix_foo_bar);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/tests/expectations/rename_case.cpp b/tests/expectations/rename_case.cpp
index 0d5a6d94..c87cfbf7 100644
--- a/tests/expectations/rename_case.cpp
+++ b/tests/expectations/rename_case.cpp
@@ -16,4 +16,6 @@ void test_screaming_snake_case(int32_t FOO_BAR);
 
 void test_gecko_case(int32_t aFooBar);
 
+void test_prefix(int32_t prefix_foo_bar);
+
 }  // extern "C"
diff --git a/tests/expectations/rename_case.pyx b/tests/expectations/rename_case.pyx
index 5fd35fc4..a7817823 100644
--- a/tests/expectations/rename_case.pyx
+++ b/tests/expectations/rename_case.pyx
@@ -15,3 +15,5 @@ cdef extern from *:
   void test_screaming_snake_case(int32_t FOO_BAR);
 
   void test_gecko_case(int32_t aFooBar);
+
+  void test_prefix(int32_t prefix_foo_bar);
diff --git a/tests/rust/rename_case.rs b/tests/rust/rename_case.rs
index e83227d0..d01b71e6 100644
--- a/tests/rust/rename_case.rs
+++ b/tests/rust/rename_case.rs
@@ -17,3 +17,7 @@ pub extern "C" fn test_screaming_snake_case(foo_bar: i32) {}
 /// cbindgen:rename-all=GeckoCase
 #[no_mangle]
 pub extern "C" fn test_gecko_case(foo_bar: i32) {}
+
+/// cbindgen:rename-all=prefix:prefix_
+#[no_mangle]
+pub extern "C" fn test_prefix(foo_bar: i32) {}