Repository: not-fl3/nanoserde
Pull request #78 (2023-08-31 22:53:11)

json: Allow proxies for optional types


Patch:
diff --git a/derive/src/serde_json.rs b/derive/src/serde_json.rs
index de71f91..6854e31 100644
--- a/derive/src/serde_json.rs
+++ b/derive/src/serde_json.rs
@@ -25,6 +25,20 @@ pub fn derive_ser_json_proxy(proxy_type: &str, type_: &str) -> TokenStream {
     .unwrap()
 }
 
+fn ser_proxy_guard(fieldname: &str, field: &Field) -> String {
+    if let Some(proxy) = crate::shared::attrs_proxy(&field.attributes) {
+        if field.ty.base() == "Option" {
+            format!(
+                "{{{fieldname}.as_ref().map(|f| {{let proxy: {proxy} = Into::into(f);proxy}})}}"
+            )
+        } else {
+            format!("{{let proxy: {proxy} = Into::into(&{fieldname});proxy}}",)
+        }
+    } else {
+        format!("{fieldname}")
+    }
+}
+
 pub fn derive_ser_json_struct(struct_: &Struct) -> TokenStream {
     let mut s = String::new();
     let (generic_w_bounds, generic_no_bounds) = struct_bounds_strings(struct_, "SerJson");
@@ -40,14 +54,7 @@ pub fn derive_ser_json_struct(struct_: &Struct) -> TokenStream {
             if skip {
                 continue;
             }
-            let proxied_field = if let Some(proxy) = crate::shared::attrs_proxy(&field.attributes) {
-                format!(
-                    "{{let proxy: {} = Into::into(&self.{});proxy}}",
-                    proxy, struct_fieldname
-                )
-            } else {
-                format!("self.{}", struct_fieldname)
-            };
+            let proxied_field = ser_proxy_guard(&format!("self.{struct_fieldname}"), field);
 
             if field.ty.base() == "Option" {
                 l!(
@@ -135,7 +142,11 @@ pub fn derive_de_json_named(name: &str, defaults: bool, fields: &[Field]) -> Tok
         let skip = crate::shared::attrs_skip(&field.attributes);
 
         let proxified_t = if let Some(proxy) = proxy {
-            format!("From::<&{}>::from(&t)", proxy)
+            if field.ty.base() == "Option" {
+                format!("Some(From::<&{proxy}>::from(&t))")
+            } else {
+                format!("From::<&{proxy}>::from(&t)")
+            }
         } else {
             format!("t")
         };
@@ -276,13 +287,7 @@ pub fn derive_ser_json_enum(enum_: &Enum) -> TokenStream {
                 let last = contents.fields.len() - 1;
                 for (index, field) in contents.fields.iter().enumerate() {
                     if let Some(name) = &&field.field_name {
-                        let proxied_field =
-                            if let Some(proxy) = crate::shared::attrs_proxy(&variant.attributes) {
-                                format!("{{let proxy: {} = Into::into(&{});proxy}}", proxy, name)
-                            } else {
-                                format!("{}", name)
-                            };
-
+                        let proxied_field = ser_proxy_guard(name, field);
                         if index == last {
                             if field.ty.base() == "Option" {
                                 l!(


Test patch:
diff --git a/tests/json.rs b/tests/json.rs
index ae821c7..421296b 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -626,6 +626,81 @@ fn field_proxy() {
     assert!(test == test_deserialized);
 }
 
+#[test]
+fn field_option_proxy() {
+    #[derive(PartialEq, Clone, Debug)]
+    #[repr(u32)]
+    enum SomeEnum {
+        One,
+        Two,
+        Three,
+    }
+
+    #[derive(PartialEq, Debug, DeJson, SerJson)]
+    #[nserde(transparent)]
+    pub struct U32(u32);
+
+    impl From<&SomeEnum> for U32 {
+        fn from(e: &SomeEnum) -> U32 {
+            U32(e.clone() as u32)
+        }
+    }
+    impl From<&U32> for SomeEnum {
+        fn from(n: &U32) -> SomeEnum {
+            match n.0 {
+                0 => SomeEnum::One,
+                1 => SomeEnum::Two,
+                2 => SomeEnum::Three,
+                _ => panic!(),
+            }
+        }
+    }
+
+    #[derive(DeJson, SerJson, PartialEq, Debug)]
+    pub struct Test {
+        #[nserde(proxy = "U32")]
+        foo: Option<SomeEnum>,
+        #[nserde(proxy = "U32")]
+        bar: SomeEnum,
+    }
+
+    let test = Test {
+        foo: Some(SomeEnum::Three),
+        bar: SomeEnum::Two,
+    };
+    let bytes = SerJson::serialize_json(&test);
+    let test_deserialized = DeJson::deserialize_json(&bytes).unwrap();
+    assert!(test == test_deserialized);
+    let test = Test {
+        foo: None,
+        bar: SomeEnum::One,
+    };
+    let bytes = SerJson::serialize_json(&test);
+    let test_deserialized = DeJson::deserialize_json(&bytes).unwrap();
+    assert!(test == test_deserialized);
+
+    #[derive(DeJson, SerJson, PartialEq, Debug)]
+    enum Test2 {
+        //A(#[nserde(proxy = "U32")] Option<SomeEnum>),
+        B {
+            #[nserde(proxy = "U32")]
+            bar: Option<SomeEnum>,
+        },
+    }
+
+    // TODO
+    // let test = Test2::A(Some(SomeEnum::Three));
+    // let bytes = SerJson::serialize_json(&test);
+    // let test_deserialized = DeJson::deserialize_json(&bytes).unwrap();
+    // assert!(test == test_deserialized);
+    let test = Test2::B {
+        bar: Some(SomeEnum::One),
+    };
+    let bytes = SerJson::serialize_json(&test);
+    let test_deserialized = DeJson::deserialize_json(&bytes).unwrap();
+    assert!(test == test_deserialized);
+}
+
 #[test]
 fn tuple_struct() {
     #[derive(DeJson, SerJson, PartialEq)]