From fb9478a9c0e08e68b3a9753acf7082f594d84591 Mon Sep 17 00:00:00 2001 From: Birh Burh Date: Sat, 17 Aug 2024 17:56:23 +0200 Subject: [PATCH 1/2] Use default value instead of None for all types when using skip attribute --- derive/src/serde_json.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/src/serde_json.rs b/derive/src/serde_json.rs index 46ba989..0bedcd4 100644 --- a/derive/src/serde_json.rs +++ b/derive/src/serde_json.rs @@ -175,7 +175,7 @@ pub fn derive_de_json_named(name: &str, defaults: bool, fields: &[Field]) -> Tok matches.push((json_fieldname.clone(), localvar.clone())); local_vars.push(localvar); } else { - unwraps.push(format!("None")); + unwraps.push(default_val.unwrap_or_else(|| String::from("Default::default()"))); } struct_field_names.push(struct_fieldname); From ddb4e28c909ab540bd717cb7f7003de5e771fa43 Mon Sep 17 00:00:00 2001 From: Birh Burh Date: Mon, 26 Aug 2024 20:09:46 +0200 Subject: [PATCH 2/2] serde_ron: skip attribute; serde_ron, serde_json: tests for skip, default Also tests and added attributes to feature table --- README.md | 47 ++++++++++++------------ derive/src/serde_ron.rs | 79 +++++++++++++++++++++++------------------ derive/src/shared.rs | 2 +- tests/json.rs | 65 +++++++++++++++++++++++++++++++++ tests/ron.rs | 55 ++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index b16a57b..3e2f164 100644 --- a/README.md +++ b/README.md @@ -33,36 +33,39 @@ For more examples take a look at [tests](/tests) ## Features support matrix: -| Feature | json | bin | ron | toml | -| ---------------------------------------------- | ------ | ----- | ------ | ----- | -| serialization | yes | yes | yes | no | -| deserialization | yes | yes | yes | no | -| container: Struct | yes | yes | yes | no | -| container: Tuple Struct | no | yes | yes | no | -| container: Enum | yes | yes | yes | no | -| field: `std::collections::HashMap` | yes | yes | yes | no | -| field: `std::vec::Vec` | yes | yes | yes | no | -| field: `Option` | yes | yes | yes | no | -| field: `i*`/`f*`/`String`/`T: De*/Ser*` | yes | yes | yes | no | -| field attribute: `#[nserde(default)]` | yes | no | yes | no | -| field attribute: `#[nserde(rename = "")]` | yes | yes | yes | no | -| field attribute: `#[nserde(proxy = "")]` | no | yes | no | no | -| container attribute: `#[nserde(default)]` | yes | no | yes | no | -| container attribute: `#[nserde(rename = "")]` | yes | yes | yes | no | -| container attribute: `#[nserde(proxy = "")]` | yes | yes | no | no | -| container attribute: `#[nserde(transparent)]` | yes | no | no | no | +| Feature | json | bin | ron | toml | +| --------------------------------------------------- | ------ | ----- | ------ | ----- | +| serialization | yes | yes | yes | no | +| deserialization | yes | yes | yes | no | +| container: Struct | yes | yes | yes | no | +| container: Tuple Struct | no | yes | yes | no | +| container: Enum | yes | yes | yes | no | +| field: `std::collections::HashMap` | yes | yes | yes | no | +| field: `std::vec::Vec` | yes | yes | yes | no | +| field: `Option` | yes | yes | yes | no | +| field: `i*`/`f*`/`String`/`T: De*/Ser*` | yes | yes | yes | no | +| field attribute: `#[nserde(default)]` | yes | no | yes | no | +| field attribute: `#[nserde(rename = "")]` | yes | yes | yes | no | +| field attribute: `#[nserde(proxy = "")]` | no | yes | no | no | +| container attribute: `#[nserde(default)]` | yes | no | yes | no | +| container attribute: `#[nserde(default = "")]` | yes | no | yes | no | +| container attribute: `#[nserde(default_with = "")]` | yes | no | yes | no | +| container attribute: `#[nserde(skip)]` | yes | no | yes | no | +| container attribute: `#[nserde(rename = "")]` | yes | yes | yes | no | +| container attribute: `#[nserde(proxy = "")]` | yes | yes | no | no | +| container attribute: `#[nserde(transparent)]` | yes | no | no | no | ## Crate features: -All features are enabled by default. To enable only specific formats, import nanoserde using +All features are enabled by default. To enable only specific formats, import nanoserde using ```toml nanoserde = { version = "*", default-features = false, features = ["std", "{format feature name}"] } ``` in your `Cargo.toml` and add one or more of the following crate features: -| Format | Feature Name | +| Format | Feature Name | | ----------| -------------- | -| Binary | `binary` | -| JSON | `json` | +| Binary | `binary` | +| JSON | `json` | | RON | `ron` | | TOML | `toml` | diff --git a/derive/src/serde_ron.rs b/derive/src/serde_ron.rs index 48004eb..01d87e0 100644 --- a/derive/src/serde_ron.rs +++ b/derive/src/serde_ron.rs @@ -43,6 +43,10 @@ pub fn derive_ser_ron_struct(struct_: &Struct) -> TokenStream { let struct_fieldname = field.field_name.clone().unwrap(); let ron_fieldname = shared::attrs_rename(&field.attributes).unwrap_or_else(|| struct_fieldname.clone()); + let skip = shared::attrs_skip(&field.attributes); + if skip { + continue; + } if field.ty.base() == "Option" { l!( s, @@ -156,42 +160,47 @@ pub fn derive_de_ron_named( }; let ron_fieldname = shared::attrs_rename(&field.attributes).unwrap_or(struct_fieldname.clone()); - - if field.ty.base() == "Option" { - unwraps.push(format!( - "{{ - if let Some(t) = {} {{ - t - }} else {{ - {} - }} - }}", - localvar, - default_val.unwrap_or_else(|| String::from("None")) - )); - } else if container_attr_default || default_val.is_some() { - unwraps.push(format!( - "{{ - if let Some(t) = {} {{ - t - }} else {{ - {} - }} - }}", - localvar, - default_val.unwrap_or_else(|| String::from("Default::default()")) - )); + let skip = crate::shared::attrs_skip(&field.attributes); + + if skip == false { + if field.ty.base() == "Option" { + unwraps.push(format!( + "{{ + if let Some(t) = {} {{ + t + }} else {{ + {} + }} + }}", + localvar, + default_val.unwrap_or_else(|| String::from("None")) + )); + } else if container_attr_default || default_val.is_some() { + unwraps.push(format!( + "{{ + if let Some(t) = {} {{ + t + }} else {{ + {} + }} + }}", + localvar, + default_val.unwrap_or_else(|| String::from("Default::default()")) + )); + } else { + unwraps.push(format!( + "{{ + if let Some(t) = {} {{ + t + }} else {{ + return Err(s.err_nf(\"{}\")) + }} + }}", + localvar, struct_fieldname + )); + } } else { - unwraps.push(format!( - "{{ - if let Some(t) = {} {{ - t - }} else {{ - return Err(s.err_nf(\"{}\")) - }} - }}", - localvar, struct_fieldname - )); + unwraps.push(default_val.unwrap_or_else(|| String::from("Default::default()"))); } struct_field_names.push(struct_fieldname); diff --git a/derive/src/shared.rs b/derive/src/shared.rs index 735bf75..b826a17 100644 --- a/derive/src/shared.rs +++ b/derive/src/shared.rs @@ -69,7 +69,7 @@ pub fn attrs_transparent(attributes: &[crate::parse::Attribute]) -> bool { .any(|attr| attr.tokens.len() == 1 && attr.tokens[0] == "transparent") } -#[cfg(feature = "json")] +#[cfg(any(feature = "json", feature = "ron"))] pub fn attrs_skip(attributes: &[crate::parse::Attribute]) -> bool { attributes .iter() diff --git a/tests/json.rs b/tests/json.rs index a9a79e4..d8b0bc8 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -326,6 +326,71 @@ fn de_field_default() { assert_eq!(test.foo2.x, 3); } +#[test] +fn de_ser_field_skip() { + #[derive(DeJson, SerJson)] + struct Foo { + x: i32, + } + impl Default for Foo { + fn default() -> Foo { + Foo { x: 23 } + } + } + + fn h_default() -> Option { + Some("h not empty".into()) + } + + #[derive(DeJson, SerJson)] + pub struct Test { + a: i32, + #[nserde(skip)] + foo: Foo, + foo2: Foo, + #[nserde(skip, default = "4.0")] + b: f32, + #[nserde(skip, default)] + c: f32, + #[nserde(skip)] + d: i32, + #[nserde(skip)] + e: String, + #[nserde(skip)] + f: Foo, + #[nserde(skip)] + g: Option, + #[nserde(skip, default_with = "h_default")] + h: Option, + } + + let json = r#"{ + "a": 1, + "c": 3.0, + "h": "h not empty", + "foo2": { "x": 3 } + }"#; + + let mut test: Test = DeJson::deserialize_json(json).unwrap(); + assert_eq!(test.a, 1); + assert_eq!(test.b, 4.0); + assert_eq!(test.c, 0.0); + assert_eq!(test.d, 0); + assert_eq!(test.e, ""); + assert_eq!(test.f.x, 23); + assert_eq!(test.g, None); + assert_eq!(test.h, Some("h not empty".into())); + assert_eq!(test.foo.x, 23); + assert_eq!(test.foo2.x, 3); + + test.e = "e not empty".into(); + test.g = Some(2); + + let ser_json = r#"{"a":1,"foo2":{"x":3}}"#; + let serialized = SerJson::serialize_json(&test); + assert_eq!(serialized, ser_json); +} + #[test] fn doctests() { /// This is test diff --git a/tests/ron.rs b/tests/ron.rs index 67a6e10..2706a95 100644 --- a/tests/ron.rs +++ b/tests/ron.rs @@ -91,6 +91,9 @@ fn de_field_default() { Foo { x: 23 } } } + fn foo3_default() -> Foo { + Foo { x: 15 } + } #[derive(DeRon)] pub struct Test { @@ -98,6 +101,8 @@ fn de_field_default() { #[nserde(default)] foo: Foo, foo2: Foo, + #[nserde(default_with = "foo3_default")] + foo3: Foo, b: f32, } @@ -112,6 +117,56 @@ fn de_field_default() { assert_eq!(test.b, 2.); assert_eq!(test.foo.x, 23); assert_eq!(test.foo2.x, 3); + assert_eq!(test.foo3.x, 15); +} + +#[test] +fn de_ser_field_skip() { + #[derive(DeRon, SerRon, PartialEq, Debug)] + struct Foo { + x: i32, + } + impl Default for Foo { + fn default() -> Foo { + Foo { x: 23 } + } + } + fn foo3_default() -> Foo { + Foo { x: 15 } + } + + #[derive(DeRon, SerRon, PartialEq, Debug)] + pub struct Test { + a: i32, + #[nserde(skip)] + foo: Foo, + foo2: Foo, + #[nserde(skip, default_with = "foo3_default")] + foo3: Foo, + b: f32, + #[nserde(skip)] + c: Option, + } + + let ron = r#"( + a: 1, + b: 2., + foo2: (x: 3) + )"#; + + let mut test: Test = DeRon::deserialize_ron(ron).unwrap(); + assert_eq!(test.a, 1); + assert_eq!(test.b, 2.); + assert_eq!(test.foo.x, 23); + assert_eq!(test.foo2.x, 3); + assert_eq!(test.foo3.x, 15); + + test.c = Some(2); + let serialized = SerRon::serialize_ron(&test); + + let test: Test = DeRon::deserialize_ron(ron).unwrap(); + let deserialized: Test = DeRon::deserialize_ron(&serialized).unwrap(); + assert_eq!(deserialized, test); } #[test]