From b6fc68c0304c5916f27afaf7e4c944dab4c77069 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 20 Feb 2015 12:43:37 -0500 Subject: [PATCH 1/2] Add serde default attribute for fields When annotated with #[serde(default)], a field gets a default value using std:default::Default, and will not error when decoding if the field is missing from the input. Addresses #9 --- serde2/serde2_macros/src/lib.rs | 84 ++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/serde2/serde2_macros/src/lib.rs b/serde2/serde2_macros/src/lib.rs index e0dd752b..90145520 100644 --- a/serde2/serde2_macros/src/lib.rs +++ b/serde2/serde2_macros/src/lib.rs @@ -9,6 +9,8 @@ use syntax::ast::{ Item, Expr, MutMutable, + StructDef, + EnumDef, }; use syntax::ast; use syntax::codemap::{Span, respan}; @@ -57,8 +59,8 @@ fn expand_derive_serialize( sp: Span, mitem: &MetaItem, item: &Item, - mut push: Box)> -) { + push: &mut FnMut(P)) +{ let inline = cx.meta_word(sp, token::InternedString::new("inline")); let attrs = vec!(cx.attribute(sp, inline)); @@ -462,8 +464,8 @@ pub fn expand_derive_deserialize( sp: Span, mitem: &MetaItem, item: &Item, - mut push: Box)> -) { + push: &mut FnMut(P)) +{ let inline = cx.meta_word(sp, token::InternedString::new("inline")); let attrs = vec!(cx.attribute(sp, inline)); @@ -518,7 +520,7 @@ fn deserialize_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> let state = substr.nonself_args[0].clone(); match *substr.fields { - StaticStruct(_, ref fields) => { + StaticStruct(ref struct_def, ref fields) => { deserialize_struct( cx, span, @@ -526,15 +528,17 @@ fn deserialize_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> substr.type_ident, cx.path(span, vec![substr.type_ident]), fields, - state) + state, + struct_def) } - StaticEnum(_, ref fields) => { + StaticEnum(ref enum_def, ref fields) => { deserialize_enum( cx, span, substr.type_ident, &fields, - state) + state, + enum_def) } _ => cx.bug("expected StaticEnum or StaticStruct in derive(Deserialize)") } @@ -548,6 +552,7 @@ fn deserialize_struct( struct_path: ast::Path, fields: &StaticFields, state: P, + struct_def: &StructDef ) -> P { match *fields { Unnamed(ref fields) => { @@ -555,16 +560,16 @@ fn deserialize_struct( deserialize_struct_empty_fields( cx, span, - type_ident, - struct_ident, + type_ident, + struct_ident, struct_path, state) } else { deserialize_struct_unnamed_fields( cx, span, - type_ident, - struct_ident, + type_ident, + struct_ident, struct_path, &fields[], state) @@ -574,11 +579,12 @@ fn deserialize_struct( deserialize_struct_named_fields( cx, span, - type_ident, - struct_ident, + type_ident, + struct_ident, struct_path, &fields[], - state) + state, + struct_def) } } } @@ -714,6 +720,7 @@ fn deserialize_struct_named_fields( struct_path: ast::Path, fields: &[(Ident, Span)], state: P, + struct_def: &StructDef, ) -> P { let struct_name = cx.expr_str(span, token::get_ident(struct_ident)); @@ -735,6 +742,7 @@ fn deserialize_struct_named_fields( struct_path, &field_names[], fields, + struct_def ); quote_expr!(cx, { @@ -842,18 +850,50 @@ fn declare_map_field_deserializer( ] } + +fn default_value(field: &ast::StructField) -> bool { + field.node.attrs.iter() + .any(|sa| + match &sa.node.value.node { + &ast::MetaItem_::MetaList(ref n, ref vals) => { + if n == &"serde" { + vals.iter() + .map(|mi| + match &mi.node { + &ast::MetaItem_::MetaWord(ref n) => { + n == &"default" + }, + _ => false + }) + .any(|x| x) + } else { + false + } + }, + _ => false + } ) +} + fn declare_visit_map( cx: &ExtCtxt, span: Span, struct_path: ast::Path, field_names: &[Ident], fields: &[(Ident, Span)], + struct_def: &StructDef, ) -> P { // Declare each field. let let_values: Vec> = field_names.iter() - .map(|field| { - quote_stmt!(cx, let mut $field = None;) + .zip(struct_def.fields.iter()) + .map(|(field, sf)| { + if default_value(sf) { + quote_stmt!( + cx, + let mut $field = Some(::std::default::Default::default());) + } else { + quote_stmt!(cx, let mut $field = None;) + } }) .collect(); @@ -912,12 +952,14 @@ fn deserialize_enum( type_ident: Ident, fields: &[(Ident, Span, StaticFields)], state: P, + enum_def: &EnumDef, ) -> P { let type_name = cx.expr_str(span, token::get_ident(type_ident)); // Match arms to extract a variant from a string let variant_arms: Vec = fields.iter() - .map(|&(name, span, ref fields)| { + .zip(enum_def.variants.iter()) + .map(|(&(name, span, ref fields), variant_ptr)| { let value = deserialize_enum_variant( cx, span, @@ -925,6 +967,7 @@ fn deserialize_enum( name, fields, cx.expr_ident(span, cx.ident_of("visitor")), + variant_ptr, ); let s = cx.expr_str(span, token::get_ident(name)); @@ -969,6 +1012,7 @@ fn deserialize_enum_variant( variant_ident: Ident, fields: &StaticFields, state: P, + variant_ptr: &P, ) -> P { let variant_path = cx.path(span, vec![type_ident, variant_ident]); @@ -1030,6 +1074,10 @@ fn deserialize_enum_variant( variant_path, &field_names[], fields, + match variant_ptr.node.kind { + ast::VariantKind::StructVariantKind(ref sd) => &*sd, + _ => panic!("Mismatched enum types") + }, ); quote_expr!(cx, { From c5a35e388ca2ab34937b654c09129b05d91982dc Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 20 Feb 2015 13:59:14 -0500 Subject: [PATCH 2/2] Refactor default_value to use if-let --- serde2/serde2_macros/src/lib.rs | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/serde2/serde2_macros/src/lib.rs b/serde2/serde2_macros/src/lib.rs index 90145520..e0c8caf7 100644 --- a/serde2/serde2_macros/src/lib.rs +++ b/serde2/serde2_macros/src/lib.rs @@ -6,6 +6,7 @@ extern crate rustc; use syntax::ast::{ Ident, MetaItem, + MetaItem_, Item, Expr, MutMutable, @@ -854,24 +855,23 @@ fn declare_map_field_deserializer( fn default_value(field: &ast::StructField) -> bool { field.node.attrs.iter() .any(|sa| - match &sa.node.value.node { - &ast::MetaItem_::MetaList(ref n, ref vals) => { - if n == &"serde" { - vals.iter() - .map(|mi| - match &mi.node { - &ast::MetaItem_::MetaWord(ref n) => { - n == &"default" - }, - _ => false - }) - .any(|x| x) - } else { - false - } - }, - _ => false - } ) + if let MetaItem_::MetaList(ref n, ref vals) = sa.node.value.node { + if n == &"serde" { + vals.iter() + .map(|mi| + if let MetaItem_::MetaWord(ref n) = mi.node { + n == &"default" + } else { + false + }) + .any(|x| x) + } else { + false + } + } + else { + false + }) } fn declare_visit_map(