diff --git a/derive/src/attr.rs b/derive/src/attr.rs index ffc25e3..b43cac1 100644 --- a/derive/src/attr.rs +++ b/derive/src/attr.rs @@ -1,9 +1,12 @@ -use proc_macro2::Ident; -use syn::{Attribute, Field, LitStr, Result, Variant}; +use crate::private; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; +use syn::{Attribute, Field, LitStr, Path, Result, Variant}; -/// Find the value of a #[serde(rename = "...")] attribute. -fn attr_rename(attrs: &[Attribute]) -> Result> { +/// Find the value of a #[serde(rename = "...")], #[serde(default)], and #[serde(default = "...")] attributes. +fn attr_parse(attrs: &[Attribute]) -> Result<(Option, Option)> { let mut rename = None; + let mut default = None; for attr in attrs { if !attr.path().is_ident("serde") { @@ -18,27 +21,49 @@ fn attr_rename(attrs: &[Attribute]) -> Result> { } rename = Some(s.value()); Ok(()) + } else if meta.path.is_ident("default") { + if default.is_some() { + return Err(meta.error("duplicate default attribute")); + } + + if meta.input.is_empty() { + default = Some( + quote!(miniserde::#private::Some(::core::default::Default::default())), + ); + return Ok(()); + } + + let lit: LitStr = meta.value()?.parse()?; + let path: Path = lit.parse()?; + default = Some(quote!(miniserde::#private::Some(#path()))); + Ok(()) } else { Err(meta.error("unsupported attribute")) } })?; } - Ok(rename) + Ok((rename, default)) } /// Determine the name of a field, respecting a rename attribute. pub fn name_of_field(field: &Field) -> Result { - let rename = attr_rename(&field.attrs)?; + let (rename, _) = attr_parse(&field.attrs)?; Ok(rename.unwrap_or_else(|| unraw(field.ident.as_ref().unwrap()))) } /// Determine the name of a variant, respecting a rename attribute. pub fn name_of_variant(var: &Variant) -> Result { - let rename = attr_rename(&var.attrs)?; + let (rename, _) = attr_parse(&var.attrs)?; Ok(rename.unwrap_or_else(|| unraw(&var.ident))) } +/// Determine the default of a field, respecting the default attribute. +pub fn default_of_field(field: &Field) -> Result { + let (_, default) = attr_parse(&field.attrs)?; + Ok(default.unwrap_or_else(|| quote!(miniserde::Deserialize::default()))) +} + fn unraw(ident: &Ident) -> String { ident.to_string().trim_start_matches("r#").to_owned() } diff --git a/derive/src/de.rs b/derive/src/de.rs index 72bb515..f53f5c9 100644 --- a/derive/src/de.rs +++ b/derive/src/de.rs @@ -44,6 +44,11 @@ pub fn derive_struct(input: &DeriveInput, fields: &FieldsNamed) -> Result>>()?; + let fielddefault = fields + .named + .iter() + .map(attr::default_of_field) + .collect::>>()?; let wrapper_generics = bound::with_lifetime_bound(&input.generics, "'__a"); let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl(); @@ -71,7 +76,7 @@ pub fn derive_struct(input: &DeriveInput, fields: &FieldsNamed) -> Result miniserde::Result> { Ok(miniserde::#private::Box::new(__State { #( - #fieldname: miniserde::Deserialize::default(), + #fieldname: #fielddefault, )* __out: &mut self.__out, })) diff --git a/tests/test_derive.rs b/tests/test_derive.rs index 5648896..31161b6 100644 --- a/tests/test_derive.rs +++ b/tests/test_derive.rs @@ -18,6 +18,14 @@ struct Example { t2: Box, t3: [Tag; 1], r#struct: Box, + #[serde(default)] + m1: i32, + #[serde(default = "m2_default")] + m2: i32, +} + +fn m2_default() -> i32 { + 2 } #[derive(PartialEq, Debug, Serialize, Deserialize)] @@ -40,6 +48,8 @@ fn test_de() { y: Some(vec!["Y".to_owned(), "Y".to_owned()]), z: None, }), + m1: 0, + m2: 2, }; assert_eq!(actual, expected); } @@ -55,9 +65,10 @@ fn test_ser() { y: Some(vec!["Y".to_owned(), "Y".to_owned()]), z: None, }), + m1: 0, + m2: 2, }; let actual = json::to_string(&example); - let expected = - r#"{"x":"X","t1":"A","t2":"renamedB","t3":["enum"],"struct":{"y":["Y","Y"],"z":null}}"#; + let expected = r#"{"x":"X","t1":"A","t2":"renamedB","t3":["enum"],"struct":{"y":["Y","Y"],"z":null},"m1":0,"m2":2}"#; assert_eq!(actual, expected); }