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 {
|
pub struct Item {
|
||||||
name: Name,
|
name: Name,
|
||||||
deny_unknown_fields: bool,
|
deny_unknown_fields: bool,
|
||||||
|
default: bool,
|
||||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
tag: EnumTag,
|
tag: EnumTag,
|
||||||
@ -133,6 +134,7 @@ impl Item {
|
|||||||
let mut ser_name = Attr::none(cx, "rename");
|
let mut ser_name = Attr::none(cx, "rename");
|
||||||
let mut de_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 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 ser_bound = Attr::none(cx, "bound");
|
||||||
let mut de_bound = Attr::none(cx, "bound");
|
let mut de_bound = Attr::none(cx, "bound");
|
||||||
let mut untagged = BoolAttr::none(cx, "untagged");
|
let mut untagged = BoolAttr::none(cx, "untagged");
|
||||||
@ -163,6 +165,11 @@ impl Item {
|
|||||||
deny_unknown_fields.set_true();
|
deny_unknown_fields.set_true();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse `#[serde(default)]`
|
||||||
|
MetaItem(Word(ref name)) if name == "default" => {
|
||||||
|
default.set_true();
|
||||||
|
}
|
||||||
|
|
||||||
// Parse `#[serde(bound="D: Serialize")]`
|
// Parse `#[serde(bound="D: Serialize")]`
|
||||||
MetaItem(NameValue(ref name, ref lit)) if name == "bound" => {
|
MetaItem(NameValue(ref name, ref lit)) if name == "bound" => {
|
||||||
if let Ok(where_predicates) =
|
if let Ok(where_predicates) =
|
||||||
@ -281,6 +288,7 @@ impl Item {
|
|||||||
deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()),
|
deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()),
|
||||||
},
|
},
|
||||||
deny_unknown_fields: deny_unknown_fields.get(),
|
deny_unknown_fields: deny_unknown_fields.get(),
|
||||||
|
default: default.get(),
|
||||||
ser_bound: ser_bound.get(),
|
ser_bound: ser_bound.get(),
|
||||||
de_bound: de_bound.get(),
|
de_bound: de_bound.get(),
|
||||||
tag: tag,
|
tag: tag,
|
||||||
@ -295,6 +303,10 @@ impl Item {
|
|||||||
self.deny_unknown_fields
|
self.deny_unknown_fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default(&self) -> bool {
|
||||||
|
self.default
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||||
self.ser_bound.as_ref().map(|vec| &vec[..])
|
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)
|
let let_values = vars.clone().zip(fields)
|
||||||
.map(|(var, field)| {
|
.map(|(var, field)| {
|
||||||
if field.attrs.skip_deserializing() {
|
if field.attrs.skip_deserializing() {
|
||||||
let default = expr_is_missing(&field.attrs);
|
let default = expr_is_missing(&field.attrs, false, "");
|
||||||
quote! {
|
quote! {
|
||||||
let #var = #default;
|
let #var = #default;
|
||||||
}
|
}
|
||||||
@ -1101,7 +1101,11 @@ fn deserialize_map(type_ident: &syn::Ident,
|
|||||||
let extract_values = fields_names.iter()
|
let extract_values = fields_names.iter()
|
||||||
.filter(|&&(field, _)| !field.attrs.skip_deserializing())
|
.filter(|&&(field, _)| !field.attrs.skip_deserializing())
|
||||||
.map(|&(field, ref name)| {
|
.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! {
|
quote! {
|
||||||
let #name = match #name {
|
let #name = match #name {
|
||||||
@ -1115,18 +1119,30 @@ fn deserialize_map(type_ident: &syn::Ident,
|
|||||||
.map(|&(field, ref name)| {
|
.map(|&(field, ref name)| {
|
||||||
let ident = field.ident.clone().expect("struct contains unnamed fields");
|
let ident = field.ident.clone().expect("struct contains unnamed fields");
|
||||||
let value = if field.attrs.skip_deserializing() {
|
let value = if field.attrs.skip_deserializing() {
|
||||||
expr_is_missing(&field.attrs)
|
expr_is_missing(&field.attrs, item_attrs.default(), ident.as_ref())
|
||||||
} else {
|
} else {
|
||||||
quote!(#name)
|
quote!(#name)
|
||||||
};
|
};
|
||||||
quote!(#ident: #value)
|
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! {
|
quote! {
|
||||||
#(#let_values)*
|
#(#let_values)*
|
||||||
|
|
||||||
#match_keys
|
#match_keys
|
||||||
|
|
||||||
|
#default
|
||||||
|
|
||||||
#(#extract_values)*
|
#(#extract_values)*
|
||||||
|
|
||||||
_serde::export::Ok(#struct_path { #(#result),* })
|
_serde::export::Ok(#struct_path { #(#result),* })
|
||||||
@ -1184,7 +1200,7 @@ fn wrap_deserialize_with(type_ident: &syn::Ident,
|
|||||||
wrapper_ty)
|
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() {
|
match *attrs.default() {
|
||||||
attr::FieldDefault::Default => {
|
attr::FieldDefault::Default => {
|
||||||
return quote!(_serde::export::Default::default());
|
return quote!(_serde::export::Default::default());
|
||||||
@ -1195,6 +1211,14 @@ fn expr_is_missing(attrs: &attr::Field) -> Tokens {
|
|||||||
attr::FieldDefault::None => { /* below */ }
|
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();
|
let name = attrs.name().deserialize_name();
|
||||||
match attrs.deserialize_with() {
|
match attrs.deserialize_with() {
|
||||||
None => {
|
None => {
|
||||||
|
@ -2,6 +2,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
|||||||
use std::net;
|
use std::net;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::default::Default;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
@ -40,6 +41,22 @@ struct StructDenyUnknown {
|
|||||||
b: i32,
|
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)]
|
#[derive(PartialEq, Debug, Deserialize)]
|
||||||
struct StructSkipAll {
|
struct StructSkipAll {
|
||||||
#[serde(skip_deserializing)]
|
#[serde(skip_deserializing)]
|
||||||
@ -728,6 +745,23 @@ declare_tests! {
|
|||||||
Token::StructEnd,
|
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 {
|
test_enum_unit {
|
||||||
Enum::Unit => &[
|
Enum::Unit => &[
|
||||||
Token::EnumUnit("Enum", "Unit"),
|
Token::EnumUnit("Enum", "Unit"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user