Add support for #[serde(default)]
on structs
This allows structs to use the default value for each field defined in the struct’s `std::default::Default` implementation, rather then the default value for the field’s type. ``` struct StructDefault { a: i32, b: String, } impl Default for StructDefault { fn default() -> StructDefault { StructDefault{ a: 100, b: "default".to_string(), } } } ``` The code above will now return `100` for field `a` and `”default”` for `b`, rather then `0` and `””` respectively.
This commit is contained in:
parent
535ab1e04b
commit
9444db5f19
@ -90,6 +90,7 @@ impl Name {
|
||||
pub struct Item {
|
||||
name: Name,
|
||||
deny_unknown_fields: bool,
|
||||
default: bool,
|
||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||
tag: EnumTag,
|
||||
@ -133,6 +134,7 @@ impl Item {
|
||||
let mut ser_name = Attr::none(cx, "rename");
|
||||
let mut de_name = Attr::none(cx, "rename");
|
||||
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
||||
let mut default = BoolAttr::none(cx, "default");
|
||||
let mut ser_bound = Attr::none(cx, "bound");
|
||||
let mut de_bound = Attr::none(cx, "bound");
|
||||
let mut untagged = BoolAttr::none(cx, "untagged");
|
||||
@ -163,6 +165,11 @@ impl Item {
|
||||
deny_unknown_fields.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(default)]`
|
||||
MetaItem(Word(ref name)) if name == "default" => {
|
||||
default.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(bound="D: Serialize")]`
|
||||
MetaItem(NameValue(ref name, ref lit)) if name == "bound" => {
|
||||
if let Ok(where_predicates) =
|
||||
@ -281,6 +288,7 @@ impl Item {
|
||||
deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()),
|
||||
},
|
||||
deny_unknown_fields: deny_unknown_fields.get(),
|
||||
default: default.get(),
|
||||
ser_bound: ser_bound.get(),
|
||||
de_bound: de_bound.get(),
|
||||
tag: tag,
|
||||
@ -295,6 +303,10 @@ impl Item {
|
||||
self.deny_unknown_fields
|
||||
}
|
||||
|
||||
pub fn default(&self) -> bool {
|
||||
self.default
|
||||
}
|
||||
|
||||
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||
self.ser_bound.as_ref().map(|vec| &vec[..])
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ fn deserialize_seq(type_ident: &syn::Ident,
|
||||
let let_values = vars.clone().zip(fields)
|
||||
.map(|(var, field)| {
|
||||
if field.attrs.skip_deserializing() {
|
||||
let default = expr_is_missing(&field.attrs);
|
||||
let default = expr_is_missing(&field.attrs, false, "");
|
||||
quote! {
|
||||
let #var = #default;
|
||||
}
|
||||
@ -1101,7 +1101,11 @@ fn deserialize_map(type_ident: &syn::Ident,
|
||||
let extract_values = fields_names.iter()
|
||||
.filter(|&&(field, _)| !field.attrs.skip_deserializing())
|
||||
.map(|&(field, ref name)| {
|
||||
let missing_expr = expr_is_missing(&field.attrs);
|
||||
// Use the ident as field name, since the user can rename the field
|
||||
// in the attributes using `#[serde(rename = "name")]`, but we need
|
||||
// the original (in code) name of the field.
|
||||
let ident = field.ident.clone().expect("struct contains unnamed fields");
|
||||
let missing_expr = expr_is_missing(&field.attrs, item_attrs.default(), ident.as_ref());
|
||||
|
||||
quote! {
|
||||
let #name = match #name {
|
||||
@ -1115,18 +1119,30 @@ fn deserialize_map(type_ident: &syn::Ident,
|
||||
.map(|&(field, ref name)| {
|
||||
let ident = field.ident.clone().expect("struct contains unnamed fields");
|
||||
let value = if field.attrs.skip_deserializing() {
|
||||
expr_is_missing(&field.attrs)
|
||||
expr_is_missing(&field.attrs, item_attrs.default(), ident.as_ref())
|
||||
} else {
|
||||
quote!(#name)
|
||||
};
|
||||
quote!(#ident: #value)
|
||||
});
|
||||
|
||||
let default = if item_attrs.default() {
|
||||
quote!(
|
||||
let default: #struct_path = _serde::export::Default::default();
|
||||
)
|
||||
} else {
|
||||
// We don't need the default value, to prevent an unused variable warning
|
||||
// we'll leave the line empty.
|
||||
quote!()
|
||||
};
|
||||
|
||||
quote! {
|
||||
#(#let_values)*
|
||||
|
||||
#match_keys
|
||||
|
||||
#default
|
||||
|
||||
#(#extract_values)*
|
||||
|
||||
_serde::export::Ok(#struct_path { #(#result),* })
|
||||
@ -1184,7 +1200,7 @@ fn wrap_deserialize_with(type_ident: &syn::Ident,
|
||||
wrapper_ty)
|
||||
}
|
||||
|
||||
fn expr_is_missing(attrs: &attr::Field) -> Tokens {
|
||||
fn expr_is_missing(attrs: &attr::Field, use_default: bool, field_name: &str) -> Tokens {
|
||||
match *attrs.default() {
|
||||
attr::FieldDefault::Default => {
|
||||
return quote!(_serde::export::Default::default());
|
||||
@ -1195,6 +1211,14 @@ fn expr_is_missing(attrs: &attr::Field) -> Tokens {
|
||||
attr::FieldDefault::None => { /* below */ }
|
||||
}
|
||||
|
||||
if use_default {
|
||||
// Field name without the qoutes.
|
||||
let field_name = quote::Ident::new(field_name);
|
||||
return quote!(
|
||||
default.#field_name
|
||||
)
|
||||
}
|
||||
|
||||
let name = attrs.name().deserialize_name();
|
||||
match attrs.deserialize_with() {
|
||||
None => {
|
||||
|
@ -2,6 +2,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::net;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use std::default::Default;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
@ -40,6 +41,22 @@ struct StructDenyUnknown {
|
||||
b: i32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
struct StructDefault {
|
||||
a: i32,
|
||||
b: String,
|
||||
}
|
||||
|
||||
impl Default for StructDefault {
|
||||
fn default() -> StructDefault {
|
||||
StructDefault{
|
||||
a: 100,
|
||||
b: "default".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Deserialize)]
|
||||
struct StructSkipAll {
|
||||
#[serde(skip_deserializing)]
|
||||
@ -728,6 +745,23 @@ declare_tests! {
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_struct_default {
|
||||
StructDefault{ a: 50, b: "overwritten".to_string() } => &[
|
||||
Token::StructStart("StructDefault", 1),
|
||||
Token::StructSep,
|
||||
Token::Str("a"),
|
||||
Token::I32(50),
|
||||
|
||||
Token::StructSep,
|
||||
Token::Str("b"),
|
||||
Token::String("overwritten".to_string()),
|
||||
Token::StructEnd,
|
||||
],
|
||||
StructDefault{ a: 100, b: "default".to_string() } => &[
|
||||
Token::StructStart("StructDefault", 0),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_enum_unit {
|
||||
Enum::Unit => &[
|
||||
Token::EnumUnit("Enum", "Unit"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user