Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions derive/src/attr.rs
Original file line number Diff line number Diff line change
@@ -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<Option<String>> {
/// Find the value of a #[serde(rename = "...")], #[serde(default)], and #[serde(default = "...")] attributes.
fn attr_parse(attrs: &[Attribute]) -> Result<(Option<String>, Option<TokenStream>)> {
let mut rename = None;
let mut default = None;

for attr in attrs {
if !attr.path().is_ident("serde") {
Expand All @@ -18,27 +21,49 @@ fn attr_rename(attrs: &[Attribute]) -> Result<Option<String>> {
}
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<String> {
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<String> {
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<TokenStream> {
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()
}
7 changes: 6 additions & 1 deletion derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub fn derive_struct(input: &DeriveInput, fields: &FieldsNamed) -> Result<TokenS
.iter()
.map(attr::name_of_field)
.collect::<Result<Vec<_>>>()?;
let fielddefault = fields
.named
.iter()
.map(attr::default_of_field)
.collect::<Result<Vec<_>>>()?;

let wrapper_generics = bound::with_lifetime_bound(&input.generics, "'__a");
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
Expand Down Expand Up @@ -71,7 +76,7 @@ pub fn derive_struct(input: &DeriveInput, fields: &FieldsNamed) -> Result<TokenS
fn map(&mut self) -> miniserde::Result<miniserde::#private::Box<dyn miniserde::de::Map + '_>> {
Ok(miniserde::#private::Box::new(__State {
#(
#fieldname: miniserde::Deserialize::default(),
#fieldname: #fielddefault,
)*
__out: &mut self.__out,
}))
Expand Down
15 changes: 13 additions & 2 deletions tests/test_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ struct Example {
t2: Box<Tag>,
t3: [Tag; 1],
r#struct: Box<Nested>,
#[serde(default)]
m1: i32,
#[serde(default = "m2_default")]
m2: i32,
}

fn m2_default() -> i32 {
2
}

#[derive(PartialEq, Debug, Serialize, Deserialize)]
Expand All @@ -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);
}
Expand All @@ -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);
}