Support custom paths in container attribute serde(default="...")

This commit is contained in:
David Tolnay
2017-02-20 17:03:38 -08:00
parent ad480d2b04
commit 3d6e086d3e
4 changed files with 131 additions and 55 deletions
+31 -15
View File
@@ -90,7 +90,7 @@ impl Name {
pub struct Item { pub struct Item {
name: Name, name: Name,
deny_unknown_fields: bool, deny_unknown_fields: bool,
default: bool, default: Default,
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,
@@ -134,7 +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 default = Attr::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");
@@ -168,11 +168,27 @@ impl Item {
// Parse `#[serde(default)]` // Parse `#[serde(default)]`
MetaItem(Word(ref name)) if name == "default" => { MetaItem(Word(ref name)) if name == "default" => {
match item.body { match item.body {
syn::Body::Struct(_) => { syn::Body::Struct(syn::VariantData::Struct(_)) => {
default.set_true(); default.set(Default::Default);
} }
_ => { _ => {
cx.error("#[serde(default)] can only be used on structs") cx.error("#[serde(default)] can only be used on structs \
with named fields")
}
}
}
// Parse `#[serde(default="...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "default" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
match item.body {
syn::Body::Struct(syn::VariantData::Struct(_)) => {
default.set(Default::Path(path));
}
_ => {
cx.error("#[serde(default = \"...\")] can only be used \
on structs with named fields")
}
} }
} }
} }
@@ -295,7 +311,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(), default: default.get().unwrap_or(Default::None),
ser_bound: ser_bound.get(), ser_bound: ser_bound.get(),
de_bound: de_bound.get(), de_bound: de_bound.get(),
tag: tag, tag: tag,
@@ -310,8 +326,8 @@ impl Item {
self.deny_unknown_fields self.deny_unknown_fields
} }
pub fn default(&self) -> bool { pub fn default(&self) -> &Default {
self.default &self.default
} }
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
@@ -410,7 +426,7 @@ pub struct Field {
skip_serializing: bool, skip_serializing: bool,
skip_deserializing: bool, skip_deserializing: bool,
skip_serializing_if: Option<syn::Path>, skip_serializing_if: Option<syn::Path>,
default: FieldDefault, default: Default,
serialize_with: Option<syn::Path>, serialize_with: Option<syn::Path>,
deserialize_with: Option<syn::Path>, deserialize_with: Option<syn::Path>,
ser_bound: Option<Vec<syn::WherePredicate>>, ser_bound: Option<Vec<syn::WherePredicate>>,
@@ -419,7 +435,7 @@ pub struct Field {
/// Represents the default to use for a field when deserializing. /// Represents the default to use for a field when deserializing.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum FieldDefault { pub enum Default {
/// Field must always be specified because it does not have a default. /// Field must always be specified because it does not have a default.
None, None,
/// The default is given by `std::default::Default::default()`. /// The default is given by `std::default::Default::default()`.
@@ -468,13 +484,13 @@ impl Field {
// Parse `#[serde(default)]` // Parse `#[serde(default)]`
MetaItem(Word(ref name)) if name == "default" => { MetaItem(Word(ref name)) if name == "default" => {
default.set(FieldDefault::Default); default.set(Default::Default);
} }
// Parse `#[serde(default="...")]` // Parse `#[serde(default="...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "default" => { MetaItem(NameValue(ref name, ref lit)) if name == "default" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) { if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
default.set(FieldDefault::Path(path)); default.set(Default::Path(path));
} }
} }
@@ -552,7 +568,7 @@ impl Field {
// Is skip_deserializing, initialize the field to Default::default() // Is skip_deserializing, initialize the field to Default::default()
// unless a different default is specified by `#[serde(default="...")]` // unless a different default is specified by `#[serde(default="...")]`
if skip_deserializing.0.value.is_some() { if skip_deserializing.0.value.is_some() {
default.set_if_none(FieldDefault::Default); default.set_if_none(Default::Default);
} }
Field { Field {
@@ -563,7 +579,7 @@ impl Field {
skip_serializing: skip_serializing.get(), skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(), skip_deserializing: skip_deserializing.get(),
skip_serializing_if: skip_serializing_if.get(), skip_serializing_if: skip_serializing_if.get(),
default: default.get().unwrap_or(FieldDefault::None), default: default.get().unwrap_or(Default::None),
serialize_with: serialize_with.get(), serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(), deserialize_with: deserialize_with.get(),
ser_bound: ser_bound.get(), ser_bound: ser_bound.get(),
@@ -587,7 +603,7 @@ impl Field {
self.skip_serializing_if.as_ref() self.skip_serializing_if.as_ref()
} }
pub fn default(&self) -> &FieldDefault { pub fn default(&self) -> &Default {
&self.default &self.default
} }
+47
View File
@@ -157,6 +157,30 @@ pub fn with_bound<F>(item: &Item,
generics generics
} }
pub fn with_self_bound(item: &Item,
generics: &syn::Generics,
bound: &syn::Path)
-> syn::Generics
{
let mut generics = generics.clone();
generics.where_clause.predicates.push(
syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
bound_lifetimes: Vec::new(),
// the type that is being bounded e.g. MyStruct<'a, T>
bounded_ty: type_of_item(item),
// the bound e.g. Default
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: Vec::new(),
trait_ref: bound.clone(),
},
syn::TraitBoundModifier::None
)],
})
);
generics
}
pub fn with_lifetime_bound(generics: &syn::Generics, pub fn with_lifetime_bound(generics: &syn::Generics,
lifetime: &str) lifetime: &str)
-> syn::Generics { -> syn::Generics {
@@ -179,3 +203,26 @@ pub fn with_lifetime_bound(generics: &syn::Generics,
generics generics
} }
fn type_of_item(item: &Item) -> syn::Ty {
syn::Ty::Path(None, syn::Path {
global: false,
segments: vec![
syn::PathSegment {
ident: item.ident.clone(),
parameters: syn::PathParameters::AngleBracketed(syn::AngleBracketedParameterData {
lifetimes: item.generics
.lifetimes
.iter()
.map(|def| def.lifetime.clone())
.collect(),
types: item.generics
.ty_params
.iter()
.map(|param| syn::Ty::Path(None, param.ident.clone().into()))
.collect(),
bindings: Vec::new(),
}),
}
]
})
}
+44 -31
View File
@@ -46,11 +46,19 @@ fn build_generics(item: &Item) -> syn::Generics {
match item.attrs.de_bound() { match item.attrs.de_bound() {
Some(predicates) => bound::with_where_predicates(&generics, predicates), Some(predicates) => bound::with_where_predicates(&generics, predicates),
None => { None => {
let generics = match *item.attrs.default() {
attr::Default::Default => {
bound::with_self_bound(item, &generics, &path!(_serde::export::Default))
}
attr::Default::None | attr::Default::Path(_) => generics,
};
let generics = let generics =
bound::with_bound(item, bound::with_bound(item,
&generics, &generics,
needs_deserialize_bound, needs_deserialize_bound,
&path!(_serde::Deserialize)); &path!(_serde::Deserialize));
bound::with_bound(item, bound::with_bound(item,
&generics, &generics,
requires_default, requires_default,
@@ -70,7 +78,7 @@ fn needs_deserialize_bound(attrs: &attr::Field) -> bool {
// Fields with a `default` attribute (not `default=...`), and fields with a // Fields with a `default` attribute (not `default=...`), and fields with a
// `skip_deserializing` attribute that do not also have `default=...`. // `skip_deserializing` attribute that do not also have `default=...`.
fn requires_default(attrs: &attr::Field) -> bool { fn requires_default(attrs: &attr::Field) -> bool {
attrs.default() == &attr::FieldDefault::Default attrs.default() == &attr::Default::Default
} }
fn deserialize_body(item: &Item, generics: &syn::Generics) -> Tokens { fn deserialize_body(item: &Item, generics: &syn::Generics) -> Tokens {
@@ -168,7 +176,7 @@ fn deserialize_tuple(ident: &syn::Ident,
None None
}; };
let visit_seq = deserialize_seq(ident, &type_path, generics, fields, false); let visit_seq = deserialize_seq(ident, &type_path, generics, fields, false, item_attrs);
let visitor_expr = quote! { let visitor_expr = quote! {
__Visitor { marker: _serde::export::PhantomData::<#ident #ty_generics> } __Visitor { marker: _serde::export::PhantomData::<#ident #ty_generics> }
@@ -222,7 +230,8 @@ fn deserialize_seq(ident: &syn::Ident,
type_path: &Tokens, type_path: &Tokens,
generics: &syn::Generics, generics: &syn::Generics,
fields: &[Field], fields: &[Field],
is_struct: bool) is_struct: bool,
item_attrs: &attr::Item)
-> Tokens { -> Tokens {
let vars = (0..fields.len()).map(field_i as fn(_) -> _); let vars = (0..fields.len()).map(field_i as fn(_) -> _);
@@ -235,7 +244,7 @@ fn deserialize_seq(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, false, ""); let default = expr_is_missing(&field, item_attrs);
quote! { quote! {
let #var = #default; let #var = #default;
} }
@@ -337,7 +346,7 @@ fn deserialize_struct(ident: &syn::Ident,
None => format!("struct {}", ident), None => format!("struct {}", ident),
}; };
let visit_seq = deserialize_seq(ident, &type_path, generics, fields, true); let visit_seq = deserialize_seq(ident, &type_path, generics, fields, true, item_attrs);
let (field_visitor, fields_stmt, visit_map) = let (field_visitor, fields_stmt, visit_map) =
deserialize_struct_visitor(ident, type_path, generics, fields, item_attrs); deserialize_struct_visitor(ident, type_path, generics, fields, item_attrs);
@@ -1012,11 +1021,7 @@ fn deserialize_map(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)| {
// Use the ident as field name, since the user can rename the field let missing_expr = expr_is_missing(&field, item_attrs);
// 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 {
@@ -1030,21 +1035,29 @@ fn deserialize_map(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, item_attrs.default(), ident.as_ref()) expr_is_missing(&field, item_attrs)
} else { } else {
quote!(#name) quote!(#name)
}; };
quote!(#ident: #value) quote!(#ident: #value)
}); });
let default = if item_attrs.default() { let let_default = match *item_attrs.default() {
quote!( attr::Default::Default => {
let default: #struct_path = _serde::export::Default::default(); Some(quote!(
) let __default: Self::Value = _serde::export::Default::default();
} else { ))
}
attr::Default::Path(ref path) => {
Some(quote!(
let __default: Self::Value = #path();
))
}
attr::Default::None => {
// We don't need the default value, to prevent an unused variable warning // We don't need the default value, to prevent an unused variable warning
// we'll leave the line empty. // we'll leave the line empty.
quote!() None
}
}; };
quote! { quote! {
@@ -1052,7 +1065,7 @@ fn deserialize_map(ident: &syn::Ident,
#match_keys #match_keys
#default #let_default
#(#extract_values)* #(#extract_values)*
@@ -1097,27 +1110,27 @@ fn wrap_deserialize_with(ident: &syn::Ident,
(wrapper, wrapper_ty) (wrapper, wrapper_ty)
} }
fn expr_is_missing(attrs: &attr::Field, use_default: bool, field_name: &str) -> Tokens { fn expr_is_missing(field: &Field, item_attrs: &attr::Item) -> Tokens {
match *attrs.default() { match *field.attrs.default() {
attr::FieldDefault::Default => { attr::Default::Default => {
return quote!(_serde::export::Default::default()); return quote!(_serde::export::Default::default());
} }
attr::FieldDefault::Path(ref path) => { attr::Default::Path(ref path) => {
return quote!(#path()); return quote!(#path());
} }
attr::FieldDefault::None => { /* below */ } attr::Default::None => { /* below */ }
} }
if use_default { match *item_attrs.default() {
// Field name without the qoutes. attr::Default::Default | attr::Default::Path(_) => {
let field_name = quote::Ident::new(field_name); let ident = &field.ident;
return quote!( return quote!(__default.#ident);
default.#field_name }
) attr::Default::None => { /* below */ }
} }
let name = attrs.name().deserialize_name(); let name = field.attrs.name().deserialize_name();
match attrs.deserialize_with() { match field.attrs.deserialize_with() {
None => { None => {
quote! { quote! {
try!(_serde::de::private::missing_field(#name)) try!(_serde::de::private::missing_field(#name))
+4 -4
View File
@@ -43,13 +43,13 @@ struct StructDenyUnknown {
#[derive(PartialEq, Debug, Deserialize)] #[derive(PartialEq, Debug, Deserialize)]
#[serde(default)] #[serde(default)]
struct StructDefault { struct StructDefault<T> {
a: i32, a: i32,
b: String, b: T,
} }
impl Default for StructDefault { impl Default for StructDefault<String> {
fn default() -> StructDefault { fn default() -> Self {
StructDefault { StructDefault {
a: 100, a: 100,
b: "default".to_string(), b: "default".to_string(),