diff --git a/serde2/serde2_macros/src/lib.rs b/serde2/serde2_macros/src/lib.rs index 4a433dbf..dde45369 100644 --- a/serde2/serde2_macros/src/lib.rs +++ b/serde2/serde2_macros/src/lib.rs @@ -6,9 +6,12 @@ extern crate rustc; use syntax::ast::{ Ident, MetaItem, + MetaItem_, Item, Expr, MutMutable, + StructDef, + EnumDef, }; use syntax::ast; use syntax::ast_util; @@ -122,11 +125,12 @@ fn serialize_substructure(cx: &ExtCtxt, let visitor = substr.nonself_args[0].clone(); match (&item.node, &*substr.fields) { - (&ast::ItemStruct(..), &Struct(ref fields)) => { + (&ast::ItemStruct(ref struct_def, _), &Struct(ref fields)) => { if fields.is_empty() { serialize_tuple_struct(cx) } else { - serialize_struct(cx, span, visitor, substr.type_ident, fields) + serialize_struct( + cx, span, visitor, substr.type_ident, fields, struct_def) } } @@ -153,15 +157,21 @@ fn serialize_struct(cx: &ExtCtxt, span: Span, visitor: P, type_ident: Ident, - fields: &[FieldInfo]) -> P { + fields: &[FieldInfo], + struct_def: &StructDef) -> P { let type_name = cx.expr_str( span, token::get_ident(type_ident)); let len = fields.len(); + let aliases : Vec> = struct_def.fields.iter() + .map(field_alias) + .collect(); + let arms: Vec = fields.iter() + .zip(aliases.iter()) .enumerate() - .map(|(i, &FieldInfo { name, span, .. })| { + .map(|(i, (&FieldInfo { name, span, .. }, alias_lit ))| { let first = if i == 0 { quote_expr!(cx, true) } else { @@ -169,7 +179,13 @@ fn serialize_struct(cx: &ExtCtxt, }; let name = name.unwrap(); - let expr = cx.expr_str(span, token::get_ident(name)); + let expr = match alias_lit { + &Some(lit) => { + let lit = (*lit).clone(); + cx.expr_lit(lit.span, lit.node) + }, + &None => cx.expr_str(span, token::get_ident(name)), + }; let i = i as u32; @@ -607,7 +623,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, @@ -615,15 +631,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)") } @@ -637,6 +655,7 @@ fn deserialize_struct( struct_path: ast::Path, fields: &StaticFields, state: P, + struct_def: &StructDef ) -> P { match *fields { Unnamed(ref fields) => { @@ -644,16 +663,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) @@ -663,11 +682,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) } } } @@ -803,6 +823,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)); @@ -816,6 +837,7 @@ fn deserialize_struct_named_fields( span, &field_names[], fields, + struct_def, ); let visit_map_expr = declare_visit_map( @@ -824,6 +846,7 @@ fn deserialize_struct_named_fields( struct_path, &field_names[], fields, + struct_def ); quote_expr!(cx, { @@ -857,11 +880,39 @@ fn deserialize_struct_named_fields( }) } +fn field_alias(field: &ast::StructField) -> Option<&ast::Lit> { + field.node.attrs.iter() + .find(|sa| + if let MetaItem_::MetaList(ref n, _) = sa.node.value.node { + n == &"serde" + } else { + false + }) + .and_then(|sa| + if let MetaItem_::MetaList(_, ref vals) = sa.node.value.node { + vals.iter() + .fold(None, + |v, mi| + if let MetaItem_::MetaNameValue(ref n, ref lit) = mi.node { + if n == &"alias" { + Some(lit) + } else { + v + } + } else { + v + }) + } else { + None + }) +} + fn declare_map_field_deserializer( cx: &ExtCtxt, span: Span, field_names: &[ast::Ident], fields: &[(Ident, Span)], + struct_def: &StructDef, ) -> Vec> { // Create the field names for the fields. let field_variants: Vec> = field_names.iter() @@ -884,13 +935,24 @@ fn declare_map_field_deserializer( token::str_to_ident("__Field"), ast::EnumDef { variants: field_variants }); + // Get aliases + let aliases : Vec> = struct_def.fields.iter() + .map(field_alias) + .collect(); + // Match arms to extract a field from a string let field_arms: Vec = fields.iter() .zip(field_names.iter()) - .map(|(&(name, span), field)| { - let s = cx.expr_str(span, token::get_ident(name)); - quote_arm!(cx, $s => Ok(__Field::$field),) - }) + .zip(aliases.iter()) + .map(|((&(name, span), field), alias_lit)| { + let s = match alias_lit { + &None => cx.expr_str(span, token::get_ident(name)) , + &Some(lit) =>{ + let lit = (*lit).clone(); + cx.expr_lit(lit.span, lit.node) + }, + }; + quote_arm!(cx, $s => Ok(__Field::$field),)}) .collect(); vec![ @@ -931,18 +993,49 @@ fn declare_map_field_deserializer( ] } + +fn default_value(field: &ast::StructField) -> bool { + field.node.attrs.iter() + .any(|sa| + 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( 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(); @@ -1001,12 +1094,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, @@ -1014,6 +1109,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)); @@ -1058,6 +1154,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]); @@ -1111,6 +1208,10 @@ fn deserialize_enum_variant( span, &field_names[], fields, + match variant_ptr.node.kind { + ast::VariantKind::StructVariantKind(ref sd) => &*sd, + _ => panic!("Mismatched enum types") + }, ); let visit_map_expr = declare_visit_map( @@ -1119,6 +1220,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, {