diff --git a/CHANGELOG.md b/CHANGELOG.md index 92b7f90..4e99548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## vNEXT + +* Add support for `#[serde(fields_rename_all)]` ([#35](https://github.com/dbeckwith/rust-typescript-type-def/issues/35)). + ## v0.5.12 * Export `Blob` newtype wrapper around `Vec` which has the Typescript type [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). diff --git a/derive/src/lib.rs b/derive/src/lib.rs index c361e2d..51a1889 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -141,6 +141,8 @@ struct TypeDefInput { #[darling(default)] rename_all: Option>, #[darling(default)] + rename_all_fields: Option>, + #[darling(default)] rename: Option>, #[darling(default)] #[allow(dead_code)] // doesn't affect JSON @@ -289,6 +291,7 @@ fn make_info_def( content, untagged, rename_all, + rename_all_fields, rename, .. }: &TypeDefInput, @@ -343,21 +346,35 @@ fn make_info_def( match style { ast::Style::Unit => type_expr_ident("null"), ast::Style::Tuple => fields_to_type_expr( - fields, false, rename_all, generics, None, + fields, + false, + rename_all.as_ref(), + generics, + None, ), ast::Style::Struct => { if fields.is_empty() { type_expr_object([], None) } else { fields_to_type_expr( - fields, true, rename_all, generics, None, + fields, + true, + rename_all.as_ref(), + generics, + None, ) } } } } ast::Data::Enum(variants) => variants_to_type_expr( - variants, tag, content, untagged, rename_all, generics, + variants, + tag, + content, + untagged, + rename_all, + rename_all_fields, + generics, ), }, generics @@ -383,7 +400,7 @@ fn make_info_def( fn fields_to_type_expr( fields: &[TypeDefField], named: bool, - rename_all: &Option>, + rename_all: Option<&SpannedValue>, generics: &Generics, docs: Option<&Expr>, ) -> Expr { @@ -490,6 +507,7 @@ fn variants_to_type_expr( content: &Option>, untagged: &SpannedValue, variant_rename_all: &Option>, + fields_rename_all: &Option>, generics: &Generics, ) -> Expr { type_expr_union( @@ -505,9 +523,11 @@ fn variants_to_type_expr( let variant_name = serde_rename_ident( variant_name, variant_rename, - variant_rename_all, + variant_rename_all.as_ref(), false, ); + let field_rename_all = + field_rename_all.as_ref().or(fields_rename_all.as_ref()); match (tag, content, ***untagged) { (None, None, false) => match style { ast::Style::Unit => type_expr_string( @@ -909,7 +929,7 @@ fn wrap_optional_docs(docs: Option<&Expr>) -> Expr { fn serde_rename_ident( ident: &Ident, rename: &Option>, - rename_all: &Option>, + rename_all: Option<&SpannedValue>, is_field: bool, ) -> LitStr { let span = ident.span(); diff --git a/src/lib.rs b/src/lib.rs index 868c201..775283c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -256,6 +256,7 @@ pub use crate::impls::Blob; /// |:-|:-:| /// | [`#[serde(rename = "name")]`](https://serde.rs/container-attrs.html#rename) | ✓ | /// | [`#[serde(rename_all = "...")]`](https://serde.rs/container-attrs.html#rename_all) | ✓ | +/// | [`#[serde(rename_all_fields = "...")]`](https://serde.rs/container-attrs.html#rename_all_fields) | ✓ | /// | [`#[serde(deny_unknown_fields)]`](https://serde.rs/container-attrs.html#deny_unknown_fields) | ? | /// | [`#[serde(tag = "type")]`](https://serde.rs/container-attrs.html#tag) | ✓ | /// | [`#[serde(tag = "t", content = "c")]`](https://serde.rs/container-attrs.html#tag--content) | ✓ | diff --git a/tests/test.rs b/tests/test.rs index 65f5e5e..76c6ea5 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -165,6 +165,7 @@ mod derive { f: Option, g: (), h: std::net::IpAddr, + i: Test11, }, C(Parent), D, @@ -172,6 +173,21 @@ mod derive { F(), } + #[derive(Serialize, TypeDef)] + #[serde(rename_all_fields = "kebab-case")] + enum Test11 { + A { + a_a: usize, + }, + B { + b_a: usize, + }, + #[serde(rename_all = "UPPERCASE")] + C { + c_a: usize, + }, + } + #[test] fn emit() { assert_eq_str!( @@ -221,6 +237,19 @@ export namespace types { export type TEST_8 = { }; export type Test9 = never; + export type Test11 = ({ + "A": { + "a-a": types.Usize; + }; + } | { + "B": { + "b-a": types.Usize; + }; + } | { + "C": { + "C_A": types.Usize; + }; + }); export type Test10 = ({ "type": "A"; "value": (types.Parent & { @@ -238,6 +267,7 @@ export namespace types { "F": (types.Test9 | null); "G": null; "H": string; + "I": types.Test11; }; } | { "type": "C"; @@ -285,9 +315,10 @@ export namespace types { f: None, g: (), h: std::net::IpAddr::from_str("::1").unwrap(), + i: Test11::A { a_a: 0 }, }) .unwrap(), - r#"{"type":"B","value":{"A":[{"FOO_BAR":123,"a":"foo","b":null,"c":[true,false],"FFF":"f","g":{"Ok":"test"},"h":{"Err":1234},"i":["test"]},4,"bar"],"B":"cool-beans","C":{"B":[42,"baz"]},"D":null,"E":{},"F":null,"G":null,"H":"::1"}}"# + r#"{"type":"B","value":{"A":[{"FOO_BAR":123,"a":"foo","b":null,"c":[true,false],"FFF":"f","g":{"Ok":"test"},"h":{"Err":1234},"i":["test"]},4,"bar"],"B":"cool-beans","C":{"B":[42,"baz"]},"D":null,"E":{},"F":null,"G":null,"H":"::1","I":{"A":{"a-a":0}}}}"# ); }