Repository: Aleph-Alpha/ts-rs (Rust)

Issue:
Mismatch between `ts-rs`'s idea of `camelCase` and `serde`'s
I have a type like this:
```rs
#[derive(Debug, Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub struct ItemImageList {
    pub id: u32,
    pub crc32c_hash: u32,
}
```
The generated camelCase field name from `serde` for the field `crc32c_hash` is `crc32cHash`. `ts-rs`, on the other hand, decides to generate the following TS file:
```ts
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export interface ItemImageList { id: number, crc32CHash: number, }
```

This obviously leads to problems when trying to use the `ItemImageList` interface in TS to read JSON serialized by serde.

The solution for me for the time being, has been to slap `#[ts(rename = "crc32cHash")]` on the field. But the question is, should there actually be a discrepancy here?


Discussion:
Thanks for opening the issue!
No, this is definetely not intentional. We use the `inflector` crate for converting between the inflection. Unfortunately, it seems like it isn't 100% compatible with serde. I suppose we need to figure it if we can directly use serde's logic for this conversion, or if has to be re-implemented.
Apparently this isn't the only crate being "hit" by this mismatch. https://github.com/whatisinternet/Inflector/issues/85



Patch:
diff --git a/macros/src/attr/mod.rs b/macros/src/attr/mod.rs
index dc566ff1..b9575c10 100644
--- a/macros/src/attr/mod.rs
+++ b/macros/src/attr/mod.rs
@@ -32,9 +32,29 @@ impl Inflection {
         match self {
             Inflection::Lower => string.to_lowercase(),
             Inflection::Upper => string.to_uppercase(),
-            Inflection::Camel => string.to_camel_case(),
+            Inflection::Camel => {
+                let pascal = Inflection::apply(Inflection::Pascal, string);
+                pascal[..1].to_ascii_lowercase() + &pascal[1..]
+            }
             Inflection::Snake => string.to_snake_case(),
-            Inflection::Pascal => string.to_pascal_case(),
+            Inflection::Pascal => {
+                let mut s = String::with_capacity(string.len());
+
+                let mut capitalize = true;
+                for c in string.chars() {
+                    if c == '_' {
+                        capitalize = true;
+                        continue;
+                    } else if capitalize {
+                        s.push(c.to_ascii_uppercase());
+                        capitalize = false;
+                    } else {
+                        s.push(c.to_ascii_lowercase())
+                    }
+                }
+
+                s
+            }
             Inflection::ScreamingSnake => string.to_screaming_snake_case(),
             Inflection::Kebab => string.to_kebab_case(),
         }


Test patch:
diff --git a/ts-rs/tests/generics.rs b/ts-rs/tests/generics.rs
index c7ce9b6b..5dad9853 100644
--- a/ts-rs/tests/generics.rs
+++ b/ts-rs/tests/generics.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::box_collection)]
 #![allow(dead_code)]
 
 use std::{
diff --git a/ts-rs/tests/struct_rename.rs b/ts-rs/tests/struct_rename.rs
index 12039d05..e242c5b2 100644
--- a/ts-rs/tests/struct_rename.rs
+++ b/ts-rs/tests/struct_rename.rs
@@ -14,6 +14,30 @@ fn rename_all() {
     assert_eq!(Rename::inline(), "{ A: number, B: number, }");
 }
 
+#[test]
+fn rename_all_camel_case() {
+    #[derive(TS)]
+    #[ts(rename_all = "camelCase")]
+    struct Rename {
+        crc32c_hash: i32,
+        b: i32,
+    }
+
+    assert_eq!(Rename::inline(), "{ crc32cHash: number, b: number, }");
+}
+
+#[test]
+fn rename_all_pascal_case() {
+    #[derive(TS)]
+    #[ts(rename_all = "PascalCase")]
+    struct Rename {
+        crc32c_hash: i32,
+        b: i32,
+    }
+
+    assert_eq!(Rename::inline(), "{ Crc32cHash: number, B: number, }");
+}
+
 #[cfg(feature = "serde-compat")]
 #[test]
 fn serde_rename_special_char() {
diff --git a/ts-rs/tests/union_with_internal_tag.rs b/ts-rs/tests/union_with_internal_tag.rs
index e0fc0b74..e7382ee6 100644
--- a/ts-rs/tests/union_with_internal_tag.rs
+++ b/ts-rs/tests/union_with_internal_tag.rs
@@ -1,4 +1,4 @@
-#![allow(dead_code, clippy::blacklisted_name)]
+#![allow(dead_code, clippy::disallowed_names)]
 
 use serde::Serialize;
 use ts_rs::TS;