diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs new file mode 100644 index 00000000..7d55ee57 --- /dev/null +++ b/serde_macros/src/de.rs @@ -0,0 +1,900 @@ +use syntax::ast::{ + Ident, + MetaItem, + MetaItem_, + Item, + Expr, + MutMutable, + StructDef, + EnumDef, +}; +use syntax::ast; +use syntax::codemap::{Span, respan}; +use syntax::ext::base::{ExtCtxt, ItemDecorator}; +use syntax::ext::build::AstBuilder; +use syntax::ext::deriving::generic::{ + MethodDef, + Named, + StaticFields, + StaticStruct, + StaticEnum, + Substructure, + TraitDef, + Unnamed, + combine_substructure, +}; +use syntax::ext::deriving::generic::ty::{ + Borrowed, + LifetimeBounds, + Ty, + Path, +}; +use syntax::parse::token; +use syntax::ptr::P; + +use aster; + +use field::field_alias; + +pub fn expand_derive_deserialize( + cx: &mut ExtCtxt, + sp: Span, + mitem: &MetaItem, + item: &Item, + push: &mut FnMut(P) +) { + let inline = cx.meta_word(sp, token::InternedString::new("inline")); + let attrs = vec!(cx.attribute(sp, inline)); + + let trait_def = TraitDef { + span: sp, + attributes: Vec::new(), + path: Path::new(vec!["serde", "de", "Deserialize"]), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + associated_types: vec![], + methods: vec!( + MethodDef { + name: "deserialize", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec![ + ("__D", vec![Path::new(vec!["serde", "de", "Deserializer"])]), + ], + }, + explicit_self: None, + args: vec![ + Ty::Ptr( + Box::new(Ty::Literal(Path::new_local("__D"))), + Borrowed(None, MutMutable) + ), + ], + ret_ty: Ty::Literal( + Path::new_( + vec!["std", "result", "Result"], + None, + vec![ + Box::new(Ty::Self_), + Box::new(Ty::Literal(Path::new_(vec!["__D", "Error"], + None, + vec![], + false))), + ], + true + ) + ), + attributes: attrs, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + deserialize_substructure(a, b, c, item) + })), + }) + }; + + trait_def.expand(cx, mitem, item, |item| push(item)) +} + +fn deserialize_substructure( + cx: &ExtCtxt, + span: Span, + substr: &Substructure, + item: &Item, +) -> P { + let builder = aster::AstBuilder::new().span(span); + + let state = substr.nonself_args[0].clone(); + + let generics = match item.node { + ast::ItemStruct(_, ref generics) => generics, + ast::ItemEnum(_, ref generics) => generics, + _ => cx.bug("expected ItemStruct or ItemEnum in derive(Deserialize)") + }; + + let trait_generics = builder.from_generics(generics.clone()) + .add_ty_param_bound( + builder.path().global().ids(&["serde", "de", "Deserialize"]).build() + ) + .build(); + + let type_generics = builder.from_generics(trait_generics.clone()) + .strip_bounds() + .build(); + + let visitor_ty = builder.ty().path() + .segment("__Visitor").with_generics(trait_generics.clone()).build() + .build(); + + // Build `__Visitor(PhantomData, PhantomData, ...)` + let (visitor_item, visitor_expr) = deserialize_visitor( + &builder, + &trait_generics + ); + + let value_ty = builder.ty().path() + .segment(substr.type_ident).with_generics(trait_generics.clone()).build() + .build(); + + match *substr.fields { + StaticStruct(ref struct_def, ref fields) => { + deserialize_struct( + cx, + span, + &builder, + substr.type_ident, + substr.type_ident, + builder.path().id(substr.type_ident).build(), + fields, + state, + struct_def, + &trait_generics, + visitor_item, + visitor_ty, + visitor_expr, + value_ty, + ) + } + StaticEnum(ref enum_def, ref fields) => { + deserialize_enum( + cx, + &builder, + substr.type_ident, + &fields, + state, + enum_def, + &trait_generics, + &type_generics, + value_ty, + ) + } + _ => cx.bug("expected StaticEnum or StaticStruct in derive(Deserialize)") + } +} + +// Build `__Visitor(PhantomData, PhantomData, ...)` +fn deserialize_visitor( + builder: &aster::AstBuilder, + trait_generics: &ast::Generics, +) -> (P, P) { + if trait_generics.ty_params.is_empty() { + ( + builder.item().tuple_struct("__Visitor") + .build(), + builder.expr().id("__Visitor"), + ) + } else { + ( + builder.item().tuple_struct("__Visitor") + .generics().with(trait_generics.clone()).build() + .with_tys( + trait_generics.ty_params.iter().map(|ty_param| { + builder.ty().phantom_data().id(ty_param.ident) + }) + ) + .build(), + builder.expr().call().id("__Visitor") + .with_args( + trait_generics.ty_params.iter().map(|_| { + builder.expr().phantom_data() + }) + ) + .build(), + ) + } +} + +fn deserialize_struct( + cx: &ExtCtxt, + span: Span, + builder: &aster::AstBuilder, + type_ident: Ident, + struct_ident: Ident, + struct_path: ast::Path, + fields: &StaticFields, + state: P, + struct_def: &StructDef, + trait_generics: &ast::Generics, + visitor_item: P, + visitor_ty: P, + visitor_expr: P, + value_ty: P, +) -> P { + match *fields { + Unnamed(ref fields) => { + if fields.is_empty() { + deserialize_struct_empty_fields( + cx, + builder, + type_ident, + struct_ident, + struct_path, + state) + } else { + deserialize_struct_unnamed_fields( + cx, + builder, + struct_ident, + struct_path, + &fields, + state, + trait_generics, + visitor_item, + visitor_ty, + visitor_expr, + value_ty, + ) + } + } + Named(ref fields) => { + deserialize_struct_named_fields( + cx, + span, + builder, + struct_ident, + struct_path, + &fields, + state, + struct_def, + trait_generics, + visitor_item, + visitor_ty, + visitor_expr, + value_ty, + ) + } + } +} + +fn deserialize_struct_empty_fields( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + type_ident: Ident, + struct_ident: Ident, + struct_path: ast::Path, + state: P, +) -> P { + let struct_name = builder.expr().str(struct_ident); + let result = builder.expr().build_path(struct_path); + + quote_expr!(cx, { + struct __Visitor; + + impl ::serde::de::Visitor for __Visitor { + type Value = $type_ident; + + #[inline] + fn visit_unit(&mut self) -> Result<$type_ident, E> + where E: ::serde::de::Error, + { + Ok($result) + } + + #[inline] + fn visit_named_unit< + E: ::serde::de::Error, + >(&mut self, name: &str) -> Result<$type_ident, E> { + if name == $struct_name { + self.visit_unit() + } else { + Err(::serde::de::Error::syntax_error()) + } + } + + + #[inline] + fn visit_seq(&mut self, mut visitor: V) -> Result<$type_ident, V::Error> + where V: ::serde::de::SeqVisitor, + { + try!(visitor.end()); + self.visit_unit() + } + } + + $state.visit(__Visitor) + }) +} + +fn deserialize_struct_unnamed_fields( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + struct_ident: Ident, + struct_path: ast::Path, + fields: &[Span], + state: P, + trait_generics: &ast::Generics, + visitor_item: P, + visitor_ty: P, + visitor_expr: P, + value_ty: P, +) -> P { + let where_clause = &trait_generics.where_clause; + + let field_names: Vec = (0 .. fields.len()) + .map(|i| builder.id(&format!("__field{}", i))) + .collect(); + + let visit_seq_expr = declare_visit_seq( + cx, + builder, + struct_path, + &field_names, + ); + + let struct_name = builder.expr().str(struct_ident); + + quote_expr!(cx, { + $visitor_item + + impl $trait_generics ::serde::de::Visitor for $visitor_ty $where_clause { + type Value = $value_ty; + + fn visit_seq<__V>(&mut self, mut visitor: __V) -> Result<$value_ty, __V::Error> + where __V: ::serde::de::SeqVisitor, + { + $visit_seq_expr + } + + fn visit_named_seq<__V>(&mut self, + name: &str, + visitor: __V) -> Result<$value_ty, __V::Error> + where __V: ::serde::de::SeqVisitor, + { + if name == $struct_name { + self.visit_seq(visitor) + } else { + Err(::serde::de::Error::syntax_error()) + } + } + } + + $state.visit($visitor_expr) + }) +} + +fn declare_visit_seq( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + struct_path: ast::Path, + field_names: &[Ident], +) -> P { + let let_values: Vec> = field_names.iter() + .map(|name| { + quote_stmt!(cx, + let $name = match try!(visitor.visit()) { + Some(value) => value, + None => { + return Err(::serde::de::Error::end_of_stream_error()); + } + }; + ) + }) + .collect(); + + let result = builder.expr().call() + .build_path(struct_path) + .with_args(field_names.iter().map(|name| builder.expr().id(*name))) + .build(); + + quote_expr!(cx, { + $let_values + + try!(visitor.end()); + + Ok($result) + }) +} + +fn deserialize_struct_named_fields( + cx: &ExtCtxt, + span: Span, + builder: &aster::AstBuilder, + struct_ident: Ident, + struct_path: ast::Path, + fields: &[(Ident, Span)], + state: P, + struct_def: &StructDef, + trait_generics: &ast::Generics, + visitor_item: P, + visitor_ty: P, + visitor_expr: P, + value_ty: P, +) -> P { + let where_clause = &trait_generics.where_clause; + + // Create the field names for the fields. + let field_names: Vec = (0 .. fields.len()) + .map(|i| token::str_to_ident(&format!("__field{}", i))) + .collect(); + + let field_devisitor = declare_map_field_devisitor( + cx, + span, + builder, + &field_names, + fields, + struct_def, + ); + + let visit_map_expr = declare_visit_map( + cx, + builder, + struct_path, + &field_names, + fields, + struct_def + ); + + let struct_name = builder.expr().str(struct_ident); + + quote_expr!(cx, { + $field_devisitor + + $visitor_item + + impl $trait_generics ::serde::de::Visitor for $visitor_ty $where_clause { + type Value = $value_ty; + + #[inline] + fn visit_map<__V>(&mut self, mut visitor: __V) -> Result<$value_ty, __V::Error> + where __V: ::serde::de::MapVisitor, + { + $visit_map_expr + } + + #[inline] + fn visit_named_map<__V>(&mut self, + name: &str, + visitor: __V) -> Result<$value_ty, __V::Error> + where __V: ::serde::de::MapVisitor, + { + if name == $struct_name { + self.visit_map(visitor) + } else { + Err(::serde::de::Error::syntax_error()) + } + } + } + + $state.visit($visitor_expr) + }) +} + +fn declare_map_field_devisitor( + cx: &ExtCtxt, + span: Span, + _builder: &aster::AstBuilder, + 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() + .map(|field| { + P(respan( + span, + ast::Variant_ { + name: *field, + attrs: Vec::new(), + kind: ast::TupleVariantKind(Vec::new()), + id: ast::DUMMY_NODE_ID, + disr_expr: None, + vis: ast::Inherited, + })) + }) + .collect(); + + let field_enum = cx.item_enum( + span, + 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()) + .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![ + quote_item!(cx, + #[allow(non_camel_case_types)] + $field_enum + ).unwrap(), + + quote_item!(cx, + struct __FieldVisitor; + ).unwrap(), + + quote_item!(cx, + impl ::serde::de::Visitor for __FieldVisitor { + type Value = __Field; + + fn visit_str(&mut self, value: &str) -> Result<__Field, E> + where E: ::serde::de::Error, + { + match value { + $field_arms + _ => Err(::serde::de::Error::syntax_error()), + } + } + } + ).unwrap(), + + quote_item!(cx, + impl ::serde::de::Deserialize for __Field { + #[inline] + fn deserialize(state: &mut S) -> Result<__Field, S::Error> + where S: ::serde::de::Deserializer, + { + state.visit(__FieldVisitor) + } + } + ).unwrap(), + ] +} + + +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, + builder: &aster::AstBuilder, + struct_path: ast::Path, + field_names: &[Ident], + fields: &[(Ident, Span)], + struct_def: &StructDef, +) -> P { + // Declare each field. + let let_values: Vec> = field_names.iter() + .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(); + + // Match arms to extract a value for a field. + let value_arms: Vec = field_names.iter() + .map(|field| { + quote_arm!(cx, __Field::$field => { + $field = Some(try!(visitor.visit_value())); + }) + }) + .collect(); + + let extract_values: Vec> = fields.iter() + .zip(field_names.iter()) + .map(|(&(name, span), field)| { + let name_str = cx.expr_str(span, token::get_ident(name)); + quote_stmt!(cx, + let $field = match $field { + Some($field) => $field, + None => try!(visitor.missing_field($name_str)), + }; + ) + }) + .collect(); + + let result = builder.expr().struct_path(struct_path) + .with_id_exprs( + fields.iter() + .zip(field_names.iter()) + .map(|(&(name, _), field)| { + (name, builder.expr().id(field)) + }) + ) + .build(); + + quote_expr!(cx, { + $let_values + + while let Some(key) = try!(visitor.visit_key()) { + match key { + $value_arms + } + } + + $extract_values + Ok($result) + }) +} + +fn deserialize_enum( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + type_ident: Ident, + fields: &[(Ident, Span, StaticFields)], + state: P, + enum_def: &EnumDef, + trait_generics: &ast::Generics, + type_generics: &ast::Generics, + value_ty: P, +) -> P { + let where_clause = &trait_generics.where_clause; + + let (visitor_struct, visitor_expr) = deserialize_visitor( + builder, + trait_generics, + ); + + let type_name = builder.expr().str(type_ident); + + // Match arms to extract a variant from a string + let variant_arms: Vec = fields.iter() + .zip(enum_def.variants.iter()) + .map(|(&(name, span, ref fields), variant)| { + let value = deserialize_enum_variant( + cx, + span, + builder, + type_ident, + name, + fields, + cx.expr_ident(span, cx.ident_of("visitor")), + variant, + &value_ty, + &trait_generics, + &type_generics, + ); + + let s = builder.expr().str(name); + quote_arm!(cx, $s => $value,) + }) + .collect(); + + quote_expr!(cx, { + $visitor_struct; + + impl $trait_generics ::serde::de::Visitor for __Visitor $type_generics $where_clause { + type Value = $value_ty; + + fn visit_enum<__V>(&mut self, + name: &str, + variant: &str, + visitor: __V) -> Result<$value_ty, __V::Error> + where __V: ::serde::de::EnumVisitor, + { + if name == $type_name { + self.visit_variant(variant, visitor) + } else { + Err(::serde::de::Error::syntax_error()) + } + } + + fn visit_variant<__V>(&mut self, + name: &str, + mut visitor: __V) -> Result<$value_ty, __V::Error> + where __V: ::serde::de::EnumVisitor + { + match name { + $variant_arms + _ => Err(::serde::de::Error::syntax_error()), + } + } + } + + $state.visit_enum($visitor_expr) + }) +} + +fn deserialize_enum_variant( + cx: &ExtCtxt, + span: Span, + builder: &aster::AstBuilder, + type_ident: Ident, + variant_ident: Ident, + fields: &StaticFields, + state: P, + variant: &P, + value_ty: &P, + trait_generics: &ast::Generics, + type_generics: &ast::Generics, +) -> P { + let variant_path = builder.path() + .ids(&[type_ident, variant_ident]) + .build(); + + match *fields { + Unnamed(ref fields) if fields.is_empty() => { + quote_expr!(cx, { + try!($state.visit_unit()); + Ok($variant_path) + }) + } + + Unnamed(ref fields) => { + deserialize_enum_variant_seq( + cx, + builder, + &*fields, + variant_path, + trait_generics, + type_generics, + state, + value_ty, + ) + } + Named(ref fields) => { + deserialize_enum_variant_map( + cx, + span, + builder, + &*fields, + variant_path, + trait_generics, + type_generics, + state, + value_ty, + variant, + ) + } + } +} + +fn deserialize_enum_variant_seq( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + fields: &[Span], + variant_path: ast::Path, + trait_generics: &ast::Generics, + type_generics: &ast::Generics, + state: P, + value_ty: &P, +) -> P { + let where_clause = &trait_generics.where_clause; + + // Create the field names for the fields. + let field_names: Vec = (0 .. fields.len()) + .map(|i| token::str_to_ident(&format!("__field{}", i))) + .collect(); + + let visit_seq_expr = declare_visit_seq( + cx, + builder, + variant_path, + &field_names, + ); + + let (visitor_struct, visitor_expr) = deserialize_visitor( + builder, + trait_generics, + ); + + quote_expr!(cx, { + $visitor_struct + + impl $trait_generics ::serde::de::EnumSeqVisitor for __Visitor $type_generics $where_clause { + type Value = $value_ty; + + fn visit< + V: ::serde::de::SeqVisitor, + >(&mut self, mut visitor: V) -> Result<$value_ty, V::Error> { + $visit_seq_expr + } + } + + $state.visit_seq($visitor_expr) + }) +} + +fn deserialize_enum_variant_map( + cx: &ExtCtxt, + span: Span, + builder: &aster::AstBuilder, + fields: &[(Ident, Span)], + variant_path: ast::Path, + trait_generics: &ast::Generics, + type_generics: &ast::Generics, + state: P, + value_ty: &P, + variant: &P, +) -> P { + let where_clause = &trait_generics.where_clause; + + // Create the field names for the fields. + let field_names: Vec = (0 .. fields.len()) + .map(|i| token::str_to_ident(&format!("__field{}", i))) + .collect(); + + let field_devisitor = declare_map_field_devisitor( + cx, + span, + builder, + &field_names, + fields, + match variant.node.kind { + ast::VariantKind::StructVariantKind(ref sd) => &*sd, + _ => panic!("Mismatched enum types") + }, + ); + + let visit_map_expr = declare_visit_map( + cx, + builder, + variant_path, + &field_names, + fields, + match variant.node.kind { + ast::VariantKind::StructVariantKind(ref sd) => &*sd, + _ => panic!("Mismatched enum types") + }, + ); + + let (visitor_struct, visitor_expr) = deserialize_visitor( + builder, + trait_generics + ); + + quote_expr!(cx, { + $visitor_struct + $field_devisitor + + impl $trait_generics ::serde::de::EnumMapVisitor for __Visitor $type_generics $where_clause { + type Value = $value_ty; + + fn visit< + V: ::serde::de::MapVisitor, + >(&mut self, mut visitor: V) -> Result<$value_ty, V::Error> { + $visit_map_expr + } + } + + $state.visit_map($visitor_expr) + }) +} diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs new file mode 100644 index 00000000..6b9f8e83 --- /dev/null +++ b/serde_macros/src/field.rs @@ -0,0 +1,29 @@ +use syntax::ast; + +pub fn field_alias(field: &ast::StructField) -> Option<&ast::Lit> { + field.node.attrs.iter() + .find(|sa| { + if let ast::MetaList(ref n, _) = sa.node.value.node { + n == &"serde" + } else { + false + } + }) + .and_then(|sa| { + if let ast::MetaList(_, ref vals) = sa.node.value.node { + vals.iter().fold(None, |v, mi| { + if let ast::MetaNameValue(ref n, ref lit) = mi.node { + if n == &"alias" { + Some(lit) + } else { + v + } + } else { + v + } + }) + } else { + None + } + }) +} diff --git a/serde_macros/src/lib.rs b/serde_macros/src/lib.rs index e4329c71..7c7f6569 100644 --- a/serde_macros/src/lib.rs +++ b/serde_macros/src/lib.rs @@ -6,1480 +6,22 @@ extern crate quasi; extern crate rustc; extern crate syntax; -use syntax::ast::{ - Ident, - MetaItem, - MetaItem_, - Item, - Expr, - MutMutable, - StructDef, - EnumDef, -}; -use syntax::ast; -use syntax::codemap::{Span, respan}; -use syntax::ext::base::{ExtCtxt, Decorator, ItemDecorator}; -use syntax::ext::build::AstBuilder; -use syntax::ext::deriving::generic::{ - EnumMatching, - FieldInfo, - MethodDef, - Named, - StaticFields, - StaticStruct, - StaticEnum, - Struct, - Substructure, - TraitDef, - Unnamed, - combine_substructure, -}; -use syntax::ext::deriving::generic::ty::{ - Borrowed, - LifetimeBounds, - Ty, - Path, - borrowed_explicit_self, -}; +use syntax::ext::base::Decorator; use syntax::parse::token; -use syntax::ptr::P; - use rustc::plugin::Registry; +mod ser; +mod de; +mod field; + #[plugin_registrar] #[doc(hidden)] pub fn plugin_registrar(reg: &mut Registry) { reg.register_syntax_extension( token::intern("derive_serialize"), - Decorator(Box::new(expand_derive_serialize))); + Decorator(Box::new(ser::expand_derive_serialize))); reg.register_syntax_extension( token::intern("derive_deserialize"), - Decorator(Box::new(expand_derive_deserialize))); -} - -fn expand_derive_serialize( - cx: &mut ExtCtxt, - sp: Span, - mitem: &MetaItem, - item: &Item, - push: &mut FnMut(P) -) { - let inline = cx.meta_word(sp, token::InternedString::new("inline")); - let attrs = vec!(cx.attribute(sp, inline)); - - let trait_def = TraitDef { - span: sp, - attributes: vec![], - path: Path::new(vec!["serde", "ser", "Serialize"]), - additional_bounds: Vec::new(), - generics: LifetimeBounds::empty(), - associated_types: vec![], - methods: vec![ - MethodDef { - name: "serialize", - generics: LifetimeBounds { - lifetimes: Vec::new(), - bounds: vec![ - ("__S", vec![Path::new(vec!["serde", "ser", "Serializer"])]), - ] - }, - explicit_self: borrowed_explicit_self(), - args: vec![ - Ty::Ptr( - Box::new(Ty::Literal(Path::new_local("__S"))), - Borrowed(None, MutMutable), - ), - ], - ret_ty: Ty::Literal( - Path::new_( - vec!("std", "result", "Result"), - None, - vec![ - Box::new(Ty::Tuple(vec![])), - Box::new(Ty::Literal(Path::new_(vec!["__S", "Error"], - None, - vec![], - false))), - ], - true - ) - ), - attributes: attrs, - combine_substructure: combine_substructure(Box::new(|a, b, c| { - serialize_substructure(a, b, c, item) - })), - } - ] - }; - - trait_def.expand(cx, mitem, item, |item| push(item)) -} - -fn serialize_substructure( - cx: &ExtCtxt, - span: Span, - substr: &Substructure, - item: &Item, -) -> P { - let builder = aster::AstBuilder::new().span(span); - - let serializer = substr.nonself_args[0].clone(); - - match (&item.node, &*substr.fields) { - (&ast::ItemStruct(ref struct_def, ref generics), &Struct(ref fields)) => { - let mut named_fields = vec![]; - let mut unnamed_fields = 0; - - for field in fields { - match field.name { - Some(name) => { named_fields.push(name); } - None => { unnamed_fields += 1; } - } - } - - match (named_fields.is_empty(), unnamed_fields == 0) { - (true, true) => { - serialize_unit_struct( - cx, - &builder, - serializer, - substr.type_ident, - ) - } - (true, false) => { - serialize_tuple_struct( - cx, - &builder, - serializer, - substr.type_ident, - unnamed_fields, - generics, - ) - } - (false, true) => { - serialize_struct( - cx, - &builder, - serializer, - substr.type_ident, - &named_fields, - struct_def, - generics, - ) - } - (false, false) => { - cx.bug("struct has named and unnamed fields") - } - } - } - - (&ast::ItemEnum(_, ref generics), &EnumMatching(_idx, variant, ref fields)) => { - serialize_enum( - cx, - &builder, - serializer, - substr.type_ident, - variant, - &fields, - generics, - ) - } - - _ => cx.bug("expected Struct or EnumMatching in derive_serialize") - } -} - -fn serialize_unit_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - serializer: P, - type_ident: Ident -) -> P { - let type_name = builder.expr().str(type_ident); - - quote_expr!(cx, $serializer.visit_named_unit($type_name)) -} - -fn serialize_tuple_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - serializer: P, - type_ident: Ident, - fields: usize, - generics: &ast::Generics -) -> P { - let value_ty = builder.ty() - .ref_() - .lifetime("'__a") - .ty().path() - .segment(type_ident).with_generics(generics.clone()).build() - .build(); - - let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( - cx, - builder, - value_ty, - fields, - generics, - ); - - let type_name = builder.expr().str(type_ident); - - quote_expr!(cx, { - $visitor_struct - $visitor_impl - $serializer.visit_named_seq($type_name, Visitor { - value: self, - state: 0, - }) - }) -} - -fn serialize_tuple_struct_visitor( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - value_ty: P, - fields: usize, - generics: &ast::Generics -) -> (P, P) { - let arms: Vec = (0 .. fields) - .map(|i| { - let first = builder.expr().bool(i == 0); - let expr = builder.expr() - .tup_field(i) - .field("value").self_(); - - quote_arm!(cx, - $i => { - self.state += 1; - let v = try!(serializer.visit_seq_elt($first, &$expr)); - Ok(Some(v)) - } - ) - }) - .collect(); - - let visitor_impl_generics = builder.from_generics(generics.clone()) - .add_lifetime_bound("'__a") - .add_ty_param_bound( - builder.path().global().ids(&["serde", "ser", "Serialize"]).build() - ) - .lifetime_name("'__a") - .build(); - - let where_clause = &visitor_impl_generics.where_clause; - - let visitor_generics = builder.from_generics(visitor_impl_generics.clone()) - .strip_bounds() - .build(); - - ( - quote_item!(cx, - struct Visitor $visitor_impl_generics $where_clause { - state: usize, - value: $value_ty, - } - ).unwrap(), - - quote_item!(cx, - impl $visitor_impl_generics ::serde::ser::SeqVisitor - for Visitor $visitor_generics - $where_clause { - #[inline] - fn visit(&mut self, serializer: &mut S) -> Result, S::Error> - where S: ::serde::ser::Serializer, - { - match self.state { - $arms - _ => Ok(None), - } - } - - #[inline] - fn len(&self) -> Option { - Some($fields) - } - } - ).unwrap(), - ) -} - -fn serialize_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - serializer: P, - type_ident: Ident, - fields: &[Ident], - struct_def: &StructDef, - generics: &ast::Generics -) -> P { - let value_ty = builder.ty() - .ref_() - .lifetime("'__a") - .ty().path() - .segment(type_ident).with_generics(generics.clone()).build() - .build(); - - let (visitor_struct, visitor_impl) = serialize_struct_visitor( - cx, - builder, - value_ty, - struct_def, - generics, - fields.iter().map(|field| quote_expr!(cx, &self.value.$field)), - ); - - let type_name = builder.expr().str(type_ident); - - quote_expr!(cx, { - $visitor_struct - $visitor_impl - $serializer.visit_named_map($type_name, Visitor { - value: self, - state: 0, - }) - }) -} - -fn serialize_struct_visitor( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - value_ty: P, - struct_def: &StructDef, - generics: &ast::Generics, - value_exprs: I, -) -> (P, P) - where I: Iterator>, -{ - let len = struct_def.fields.len(); - - let key_exprs = struct_def.fields.iter() - .map(|field| { - match field_alias(field) { - Some(lit) => builder.expr().build_lit(P(lit.clone())), - None => { - match field.node.kind { - ast::NamedField(name, _) => { - builder.expr().str(name) - } - ast::UnnamedField(_) => { - cx.bug("struct has named and unnamed fields") - } - } - } - } - }); - - let arms: Vec = key_exprs - .zip(value_exprs) - .enumerate() - .map(|(i, (key_expr, value_expr))| { - let first = i == 0; - - quote_arm!(cx, - $i => { - self.state += 1; - Ok( - Some( - try!( - serializer.visit_map_elt( - $first, - $key_expr, - $value_expr, - ) - ) - ) - ) - } - ) - }) - .collect(); - - let visitor_impl_generics = builder.from_generics(generics.clone()) - .add_lifetime_bound("'__a") - .add_ty_param_bound( - builder.path().global().ids(&["serde", "ser", "Serialize"]).build() - ) - .lifetime_name("'__a") - .build(); - - let where_clause = &visitor_impl_generics.where_clause; - - let visitor_generics = builder.from_generics(visitor_impl_generics.clone()) - .strip_bounds() - .build(); - - ( - quote_item!(cx, - struct Visitor $visitor_impl_generics $where_clause { - state: usize, - value: $value_ty, - } - ).unwrap(), - - quote_item!(cx, - impl $visitor_impl_generics - ::serde::ser::MapVisitor - for Visitor $visitor_generics - $where_clause { - #[inline] - fn visit(&mut self, serializer: &mut S) -> Result, S::Error> - where S: ::serde::ser::Serializer, - { - match self.state { - $arms - _ => Ok(None), - } - } - - #[inline] - fn len(&self) -> Option { - Some($len) - } - } - ).unwrap(), - ) -} - -fn serialize_enum( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - serializer: P, - type_ident: Ident, - variant: &ast::Variant, - fields: &[FieldInfo], - generics: &ast::Generics, -) -> P { - let type_name = builder.expr().str(type_ident); - let variant_name = builder.expr().str(variant.node.name); - - if fields.is_empty() { - quote_expr!(cx, - ::serde::ser::Serializer::visit_enum_unit( - $serializer, - $type_name, - $variant_name) - ) - } else { - match variant.node.kind { - ast::TupleVariantKind(ref args) => { - serialize_tuple_variant( - cx, - builder, - serializer, - type_name, - variant_name, - generics, - args, - fields, - ) - } - ast::StructVariantKind(ref struct_def) => { - serialize_struct_variant( - cx, - builder, - serializer, - type_name, - variant_name, - generics, - struct_def, - fields, - ) - } - } - } -} - -fn serialize_tuple_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - serializer: P, - type_name: P, - variant_name: P, - generics: &ast::Generics, - args: &[ast::VariantArg], - fields: &[FieldInfo], -) -> P { - let value_ty = builder.ty().tuple() - .with_tys( - args.iter().map(|arg| { - builder.ty() - .ref_() - .lifetime("'__a") - .build_ty(arg.ty.clone()) - }) - ) - .build(); - - let value_expr = builder.expr().tuple() - .with_exprs( - fields.iter().map(|field| { - builder.expr() - .addr_of() - .build(field.self_.clone()) - }) - ) - .build(); - - let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( - cx, - builder, - value_ty, - args.len(), - generics, - ); - - quote_expr!(cx, { - $visitor_struct - $visitor_impl - $serializer.visit_enum_seq($type_name, $variant_name, Visitor { - value: $value_expr, - state: 0, - }) - }) -} - -fn serialize_struct_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - serializer: P, - type_name: P, - variant_name: P, - generics: &ast::Generics, - struct_def: &ast::StructDef, - fields: &[FieldInfo], -) -> P { - let value_ty = builder.ty().tuple() - .with_tys( - struct_def.fields.iter().map(|field| { - builder.ty() - .ref_() - .lifetime("'__a") - .build_ty(field.node.ty.clone()) - }) - ) - .build(); - - let value_expr = builder.expr().tuple() - .with_exprs( - fields.iter().map(|field| { - builder.expr() - .addr_of() - .build(field.self_.clone()) - }) - ) - .build(); - - let (visitor_struct, visitor_impl) = serialize_struct_visitor( - cx, - builder, - value_ty, - struct_def, - generics, - (0 .. fields.len()).map(|i| { - builder.expr() - .tup_field(i) - .field("value").self_() - }) - ); - - quote_expr!(cx, { - $visitor_struct - $visitor_impl - $serializer.visit_enum_map($type_name, $variant_name, Visitor { - value: $value_expr, - state: 0, - }) - }) -} - -pub fn expand_derive_deserialize( - cx: &mut ExtCtxt, - sp: Span, - mitem: &MetaItem, - item: &Item, - push: &mut FnMut(P) -) { - let inline = cx.meta_word(sp, token::InternedString::new("inline")); - let attrs = vec!(cx.attribute(sp, inline)); - - let trait_def = TraitDef { - span: sp, - attributes: Vec::new(), - path: Path::new(vec!["serde", "de", "Deserialize"]), - additional_bounds: Vec::new(), - generics: LifetimeBounds::empty(), - associated_types: vec![], - methods: vec!( - MethodDef { - name: "deserialize", - generics: LifetimeBounds { - lifetimes: Vec::new(), - bounds: vec![ - ("__D", vec![Path::new(vec!["serde", "de", "Deserializer"])]), - ], - }, - explicit_self: None, - args: vec![ - Ty::Ptr( - Box::new(Ty::Literal(Path::new_local("__D"))), - Borrowed(None, MutMutable) - ), - ], - ret_ty: Ty::Literal( - Path::new_( - vec!["std", "result", "Result"], - None, - vec![ - Box::new(Ty::Self_), - Box::new(Ty::Literal(Path::new_(vec!["__D", "Error"], - None, - vec![], - false))), - ], - true - ) - ), - attributes: attrs, - combine_substructure: combine_substructure(Box::new(|a, b, c| { - deserialize_substructure(a, b, c, item) - })), - }) - }; - - trait_def.expand(cx, mitem, item, |item| push(item)) -} - -fn deserialize_substructure( - cx: &ExtCtxt, - span: Span, - substr: &Substructure, - item: &Item, -) -> P { - let builder = aster::AstBuilder::new().span(span); - - let state = substr.nonself_args[0].clone(); - - let generics = match item.node { - ast::ItemStruct(_, ref generics) => generics, - ast::ItemEnum(_, ref generics) => generics, - _ => cx.bug("expected ItemStruct or ItemEnum in derive(Deserialize)") - }; - - let trait_generics = builder.from_generics(generics.clone()) - .add_ty_param_bound( - builder.path().global().ids(&["serde", "de", "Deserialize"]).build() - ) - .build(); - - let type_generics = builder.from_generics(trait_generics.clone()) - .strip_bounds() - .build(); - - let visitor_ty = builder.ty().path() - .segment("__Visitor").with_generics(trait_generics.clone()).build() - .build(); - - // Build `__Visitor(PhantomData, PhantomData, ...)` - let (visitor_item, visitor_expr) = deserialize_visitor( - &builder, - &trait_generics - ); - - let value_ty = builder.ty().path() - .segment(substr.type_ident).with_generics(trait_generics.clone()).build() - .build(); - - match *substr.fields { - StaticStruct(ref struct_def, ref fields) => { - deserialize_struct( - cx, - span, - &builder, - substr.type_ident, - substr.type_ident, - builder.path().id(substr.type_ident).build(), - fields, - state, - struct_def, - &trait_generics, - visitor_item, - visitor_ty, - visitor_expr, - value_ty, - ) - } - StaticEnum(ref enum_def, ref fields) => { - deserialize_enum( - cx, - &builder, - substr.type_ident, - &fields, - state, - enum_def, - &trait_generics, - &type_generics, - value_ty, - ) - } - _ => cx.bug("expected StaticEnum or StaticStruct in derive(Deserialize)") - } -} - -// Build `__Visitor(PhantomData, PhantomData, ...)` -fn deserialize_visitor( - builder: &aster::AstBuilder, - trait_generics: &ast::Generics, -) -> (P, P) { - if trait_generics.ty_params.is_empty() { - ( - builder.item().tuple_struct("__Visitor") - .build(), - builder.expr().id("__Visitor"), - ) - } else { - ( - builder.item().tuple_struct("__Visitor") - .generics().with(trait_generics.clone()).build() - .with_tys( - trait_generics.ty_params.iter().map(|ty_param| { - builder.ty().phantom_data().id(ty_param.ident) - }) - ) - .build(), - builder.expr().call().id("__Visitor") - .with_args( - trait_generics.ty_params.iter().map(|_| { - builder.expr().phantom_data() - }) - ) - .build(), - ) - } -} - -fn deserialize_struct( - cx: &ExtCtxt, - span: Span, - builder: &aster::AstBuilder, - type_ident: Ident, - struct_ident: Ident, - struct_path: ast::Path, - fields: &StaticFields, - state: P, - struct_def: &StructDef, - trait_generics: &ast::Generics, - visitor_item: P, - visitor_ty: P, - visitor_expr: P, - value_ty: P, -) -> P { - match *fields { - Unnamed(ref fields) => { - if fields.is_empty() { - deserialize_struct_empty_fields( - cx, - builder, - type_ident, - struct_ident, - struct_path, - state) - } else { - deserialize_struct_unnamed_fields( - cx, - builder, - struct_ident, - struct_path, - &fields, - state, - trait_generics, - visitor_item, - visitor_ty, - visitor_expr, - value_ty, - ) - } - } - Named(ref fields) => { - deserialize_struct_named_fields( - cx, - span, - builder, - struct_ident, - struct_path, - &fields, - state, - struct_def, - trait_generics, - visitor_item, - visitor_ty, - visitor_expr, - value_ty, - ) - } - } -} - -fn deserialize_struct_empty_fields( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - struct_ident: Ident, - struct_path: ast::Path, - state: P, -) -> P { - let struct_name = builder.expr().str(struct_ident); - let result = builder.expr().build_path(struct_path); - - quote_expr!(cx, { - struct __Visitor; - - impl ::serde::de::Visitor for __Visitor { - type Value = $type_ident; - - #[inline] - fn visit_unit(&mut self) -> Result<$type_ident, E> - where E: ::serde::de::Error, - { - Ok($result) - } - - #[inline] - fn visit_named_unit< - E: ::serde::de::Error, - >(&mut self, name: &str) -> Result<$type_ident, E> { - if name == $struct_name { - self.visit_unit() - } else { - Err(::serde::de::Error::syntax_error()) - } - } - - - #[inline] - fn visit_seq(&mut self, mut visitor: V) -> Result<$type_ident, V::Error> - where V: ::serde::de::SeqVisitor, - { - try!(visitor.end()); - self.visit_unit() - } - } - - $state.visit(__Visitor) - }) -} - -fn deserialize_struct_unnamed_fields( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - struct_ident: Ident, - struct_path: ast::Path, - fields: &[Span], - state: P, - trait_generics: &ast::Generics, - visitor_item: P, - visitor_ty: P, - visitor_expr: P, - value_ty: P, -) -> P { - let where_clause = &trait_generics.where_clause; - - let field_names: Vec = (0 .. fields.len()) - .map(|i| builder.id(&format!("__field{}", i))) - .collect(); - - let visit_seq_expr = declare_visit_seq( - cx, - builder, - struct_path, - &field_names, - ); - - let struct_name = builder.expr().str(struct_ident); - - quote_expr!(cx, { - $visitor_item - - impl $trait_generics ::serde::de::Visitor for $visitor_ty $where_clause { - type Value = $value_ty; - - fn visit_seq<__V>(&mut self, mut visitor: __V) -> Result<$value_ty, __V::Error> - where __V: ::serde::de::SeqVisitor, - { - $visit_seq_expr - } - - fn visit_named_seq<__V>(&mut self, - name: &str, - visitor: __V) -> Result<$value_ty, __V::Error> - where __V: ::serde::de::SeqVisitor, - { - if name == $struct_name { - self.visit_seq(visitor) - } else { - Err(::serde::de::Error::syntax_error()) - } - } - } - - $state.visit($visitor_expr) - }) -} - -fn declare_visit_seq( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - struct_path: ast::Path, - field_names: &[Ident], -) -> P { - let let_values: Vec> = field_names.iter() - .map(|name| { - quote_stmt!(cx, - let $name = match try!(visitor.visit()) { - Some(value) => value, - None => { - return Err(::serde::de::Error::end_of_stream_error()); - } - }; - ) - }) - .collect(); - - let result = builder.expr().call() - .build_path(struct_path) - .with_args(field_names.iter().map(|name| builder.expr().id(*name))) - .build(); - - quote_expr!(cx, { - $let_values - - try!(visitor.end()); - - Ok($result) - }) -} - -fn deserialize_struct_named_fields( - cx: &ExtCtxt, - span: Span, - builder: &aster::AstBuilder, - struct_ident: Ident, - struct_path: ast::Path, - fields: &[(Ident, Span)], - state: P, - struct_def: &StructDef, - trait_generics: &ast::Generics, - visitor_item: P, - visitor_ty: P, - visitor_expr: P, - value_ty: P, -) -> P { - let where_clause = &trait_generics.where_clause; - - // Create the field names for the fields. - let field_names: Vec = (0 .. fields.len()) - .map(|i| token::str_to_ident(&format!("__field{}", i))) - .collect(); - - let field_devisitor = declare_map_field_devisitor( - cx, - span, - builder, - &field_names, - fields, - struct_def, - ); - - let visit_map_expr = declare_visit_map( - cx, - builder, - struct_path, - &field_names, - fields, - struct_def - ); - - let struct_name = builder.expr().str(struct_ident); - - quote_expr!(cx, { - $field_devisitor - - $visitor_item - - impl $trait_generics ::serde::de::Visitor for $visitor_ty $where_clause { - type Value = $value_ty; - - #[inline] - fn visit_map<__V>(&mut self, mut visitor: __V) -> Result<$value_ty, __V::Error> - where __V: ::serde::de::MapVisitor, - { - $visit_map_expr - } - - #[inline] - fn visit_named_map<__V>(&mut self, - name: &str, - visitor: __V) -> Result<$value_ty, __V::Error> - where __V: ::serde::de::MapVisitor, - { - if name == $struct_name { - self.visit_map(visitor) - } else { - Err(::serde::de::Error::syntax_error()) - } - } - } - - $state.visit($visitor_expr) - }) -} - -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_devisitor( - cx: &ExtCtxt, - span: Span, - _builder: &aster::AstBuilder, - 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() - .map(|field| { - P(respan( - span, - ast::Variant_ { - name: *field, - attrs: Vec::new(), - kind: ast::TupleVariantKind(Vec::new()), - id: ast::DUMMY_NODE_ID, - disr_expr: None, - vis: ast::Inherited, - })) - }) - .collect(); - - let field_enum = cx.item_enum( - span, - 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()) - .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![ - quote_item!(cx, - #[allow(non_camel_case_types)] - $field_enum - ).unwrap(), - - quote_item!(cx, - struct __FieldVisitor; - ).unwrap(), - - quote_item!(cx, - impl ::serde::de::Visitor for __FieldVisitor { - type Value = __Field; - - fn visit_str(&mut self, value: &str) -> Result<__Field, E> - where E: ::serde::de::Error, - { - match value { - $field_arms - _ => Err(::serde::de::Error::syntax_error()), - } - } - } - ).unwrap(), - - quote_item!(cx, - impl ::serde::de::Deserialize for __Field { - #[inline] - fn deserialize(state: &mut S) -> Result<__Field, S::Error> - where S: ::serde::de::Deserializer, - { - state.visit(__FieldVisitor) - } - } - ).unwrap(), - ] -} - - -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, - builder: &aster::AstBuilder, - struct_path: ast::Path, - field_names: &[Ident], - fields: &[(Ident, Span)], - struct_def: &StructDef, -) -> P { - // Declare each field. - let let_values: Vec> = field_names.iter() - .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(); - - // Match arms to extract a value for a field. - let value_arms: Vec = field_names.iter() - .map(|field| { - quote_arm!(cx, __Field::$field => { - $field = Some(try!(visitor.visit_value())); - }) - }) - .collect(); - - let extract_values: Vec> = fields.iter() - .zip(field_names.iter()) - .map(|(&(name, span), field)| { - let name_str = cx.expr_str(span, token::get_ident(name)); - quote_stmt!(cx, - let $field = match $field { - Some($field) => $field, - None => try!(visitor.missing_field($name_str)), - }; - ) - }) - .collect(); - - let result = builder.expr().struct_path(struct_path) - .with_id_exprs( - fields.iter() - .zip(field_names.iter()) - .map(|(&(name, _), field)| { - (name, builder.expr().id(field)) - }) - ) - .build(); - - quote_expr!(cx, { - $let_values - - while let Some(key) = try!(visitor.visit_key()) { - match key { - $value_arms - } - } - - $extract_values - Ok($result) - }) -} - -fn deserialize_enum( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - fields: &[(Ident, Span, StaticFields)], - state: P, - enum_def: &EnumDef, - trait_generics: &ast::Generics, - type_generics: &ast::Generics, - value_ty: P, -) -> P { - let where_clause = &trait_generics.where_clause; - - let (visitor_struct, visitor_expr) = deserialize_visitor( - builder, - trait_generics, - ); - - let type_name = builder.expr().str(type_ident); - - // Match arms to extract a variant from a string - let variant_arms: Vec = fields.iter() - .zip(enum_def.variants.iter()) - .map(|(&(name, span, ref fields), variant)| { - let value = deserialize_enum_variant( - cx, - span, - builder, - type_ident, - name, - fields, - cx.expr_ident(span, cx.ident_of("visitor")), - variant, - &value_ty, - &trait_generics, - &type_generics, - ); - - let s = builder.expr().str(name); - quote_arm!(cx, $s => $value,) - }) - .collect(); - - quote_expr!(cx, { - $visitor_struct; - - impl $trait_generics ::serde::de::Visitor for __Visitor $type_generics $where_clause { - type Value = $value_ty; - - fn visit_enum<__V>(&mut self, - name: &str, - variant: &str, - visitor: __V) -> Result<$value_ty, __V::Error> - where __V: ::serde::de::EnumVisitor, - { - if name == $type_name { - self.visit_variant(variant, visitor) - } else { - Err(::serde::de::Error::syntax_error()) - } - } - - fn visit_variant<__V>(&mut self, - name: &str, - mut visitor: __V) -> Result<$value_ty, __V::Error> - where __V: ::serde::de::EnumVisitor - { - match name { - $variant_arms - _ => Err(::serde::de::Error::syntax_error()), - } - } - } - - $state.visit_enum($visitor_expr) - }) -} - -fn deserialize_enum_variant( - cx: &ExtCtxt, - span: Span, - builder: &aster::AstBuilder, - type_ident: Ident, - variant_ident: Ident, - fields: &StaticFields, - state: P, - variant: &P, - value_ty: &P, - trait_generics: &ast::Generics, - type_generics: &ast::Generics, -) -> P { - let variant_path = builder.path() - .ids(&[type_ident, variant_ident]) - .build(); - - match *fields { - Unnamed(ref fields) if fields.is_empty() => { - quote_expr!(cx, { - try!($state.visit_unit()); - Ok($variant_path) - }) - } - - Unnamed(ref fields) => { - deserialize_enum_variant_seq( - cx, - builder, - &*fields, - variant_path, - trait_generics, - type_generics, - state, - value_ty, - ) - } - Named(ref fields) => { - deserialize_enum_variant_map( - cx, - span, - builder, - &*fields, - variant_path, - trait_generics, - type_generics, - state, - value_ty, - variant, - ) - } - } -} - -fn deserialize_enum_variant_seq( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - fields: &[Span], - variant_path: ast::Path, - trait_generics: &ast::Generics, - type_generics: &ast::Generics, - state: P, - value_ty: &P, -) -> P { - let where_clause = &trait_generics.where_clause; - - // Create the field names for the fields. - let field_names: Vec = (0 .. fields.len()) - .map(|i| token::str_to_ident(&format!("__field{}", i))) - .collect(); - - let visit_seq_expr = declare_visit_seq( - cx, - builder, - variant_path, - &field_names, - ); - - let (visitor_struct, visitor_expr) = deserialize_visitor( - builder, - trait_generics, - ); - - quote_expr!(cx, { - $visitor_struct - - impl $trait_generics ::serde::de::EnumSeqVisitor for __Visitor $type_generics $where_clause { - type Value = $value_ty; - - fn visit< - V: ::serde::de::SeqVisitor, - >(&mut self, mut visitor: V) -> Result<$value_ty, V::Error> { - $visit_seq_expr - } - } - - $state.visit_seq($visitor_expr) - }) -} - -fn deserialize_enum_variant_map( - cx: &ExtCtxt, - span: Span, - builder: &aster::AstBuilder, - fields: &[(Ident, Span)], - variant_path: ast::Path, - trait_generics: &ast::Generics, - type_generics: &ast::Generics, - state: P, - value_ty: &P, - variant: &P, -) -> P { - let where_clause = &trait_generics.where_clause; - - // Create the field names for the fields. - let field_names: Vec = (0 .. fields.len()) - .map(|i| token::str_to_ident(&format!("__field{}", i))) - .collect(); - - let field_devisitor = declare_map_field_devisitor( - cx, - span, - builder, - &field_names, - fields, - match variant.node.kind { - ast::VariantKind::StructVariantKind(ref sd) => &*sd, - _ => panic!("Mismatched enum types") - }, - ); - - let visit_map_expr = declare_visit_map( - cx, - builder, - variant_path, - &field_names, - fields, - match variant.node.kind { - ast::VariantKind::StructVariantKind(ref sd) => &*sd, - _ => panic!("Mismatched enum types") - }, - ); - - let (visitor_struct, visitor_expr) = deserialize_visitor( - builder, - trait_generics - ); - - quote_expr!(cx, { - $visitor_struct - $field_devisitor - - impl $trait_generics ::serde::de::EnumMapVisitor for __Visitor $type_generics $where_clause { - type Value = $value_ty; - - fn visit< - V: ::serde::de::MapVisitor, - >(&mut self, mut visitor: V) -> Result<$value_ty, V::Error> { - $visit_map_expr - } - } - - $state.visit_map($visitor_expr) - }) + Decorator(Box::new(de::expand_derive_deserialize))); } diff --git a/serde_macros/src/ser.rs b/serde_macros/src/ser.rs new file mode 100644 index 00000000..85fcd7f0 --- /dev/null +++ b/serde_macros/src/ser.rs @@ -0,0 +1,571 @@ +use syntax::ast::{ + Ident, + MetaItem, + Item, + Expr, + MutMutable, + StructDef, +}; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, ItemDecorator}; +use syntax::ext::build::AstBuilder; +use syntax::ext::deriving::generic::{ + EnumMatching, + FieldInfo, + MethodDef, + Struct, + Substructure, + TraitDef, + combine_substructure, +}; +use syntax::ext::deriving::generic::ty::{ + Borrowed, + LifetimeBounds, + Ty, + Path, + borrowed_explicit_self, +}; +use syntax::parse::token; +use syntax::ptr::P; + +use aster; + +use field::field_alias; + +pub fn expand_derive_serialize( + cx: &mut ExtCtxt, + sp: Span, + mitem: &MetaItem, + item: &Item, + push: &mut FnMut(P) +) { + let inline = cx.meta_word(sp, token::InternedString::new("inline")); + let attrs = vec!(cx.attribute(sp, inline)); + + let trait_def = TraitDef { + span: sp, + attributes: vec![], + path: Path::new(vec!["serde", "ser", "Serialize"]), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + associated_types: vec![], + methods: vec![ + MethodDef { + name: "serialize", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec![ + ("__S", vec![Path::new(vec!["serde", "ser", "Serializer"])]), + ] + }, + explicit_self: borrowed_explicit_self(), + args: vec![ + Ty::Ptr( + Box::new(Ty::Literal(Path::new_local("__S"))), + Borrowed(None, MutMutable), + ), + ], + ret_ty: Ty::Literal( + Path::new_( + vec!("std", "result", "Result"), + None, + vec![ + Box::new(Ty::Tuple(vec![])), + Box::new(Ty::Literal(Path::new_(vec!["__S", "Error"], + None, + vec![], + false))), + ], + true + ) + ), + attributes: attrs, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + serialize_substructure(a, b, c, item) + })), + } + ] + }; + + trait_def.expand(cx, mitem, item, |item| push(item)) +} + +fn serialize_substructure( + cx: &ExtCtxt, + span: Span, + substr: &Substructure, + item: &Item, +) -> P { + let builder = aster::AstBuilder::new().span(span); + + let serializer = substr.nonself_args[0].clone(); + + match (&item.node, &*substr.fields) { + (&ast::ItemStruct(ref struct_def, ref generics), &Struct(ref fields)) => { + let mut named_fields = vec![]; + let mut unnamed_fields = 0; + + for field in fields { + match field.name { + Some(name) => { named_fields.push(name); } + None => { unnamed_fields += 1; } + } + } + + match (named_fields.is_empty(), unnamed_fields == 0) { + (true, true) => { + serialize_unit_struct( + cx, + &builder, + serializer, + substr.type_ident, + ) + } + (true, false) => { + serialize_tuple_struct( + cx, + &builder, + serializer, + substr.type_ident, + unnamed_fields, + generics, + ) + } + (false, true) => { + serialize_struct( + cx, + &builder, + serializer, + substr.type_ident, + &named_fields, + struct_def, + generics, + ) + } + (false, false) => { + cx.bug("struct has named and unnamed fields") + } + } + } + + (&ast::ItemEnum(_, ref generics), &EnumMatching(_idx, variant, ref fields)) => { + serialize_enum( + cx, + &builder, + serializer, + substr.type_ident, + variant, + &fields, + generics, + ) + } + + _ => cx.bug("expected Struct or EnumMatching in derive_serialize") + } +} + +fn serialize_unit_struct( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + serializer: P, + type_ident: Ident +) -> P { + let type_name = builder.expr().str(type_ident); + + quote_expr!(cx, $serializer.visit_named_unit($type_name)) +} + +fn serialize_tuple_struct( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + serializer: P, + type_ident: Ident, + fields: usize, + generics: &ast::Generics +) -> P { + let value_ty = builder.ty() + .ref_() + .lifetime("'__a") + .ty().path() + .segment(type_ident).with_generics(generics.clone()).build() + .build(); + + let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( + cx, + builder, + value_ty, + fields, + generics, + ); + + let type_name = builder.expr().str(type_ident); + + quote_expr!(cx, { + $visitor_struct + $visitor_impl + $serializer.visit_named_seq($type_name, Visitor { + value: self, + state: 0, + }) + }) +} + + +fn serialize_struct( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + serializer: P, + type_ident: Ident, + fields: &[Ident], + struct_def: &StructDef, + generics: &ast::Generics +) -> P { + let value_ty = builder.ty() + .ref_() + .lifetime("'__a") + .ty().path() + .segment(type_ident).with_generics(generics.clone()).build() + .build(); + + let (visitor_struct, visitor_impl) = serialize_struct_visitor( + cx, + builder, + value_ty, + struct_def, + generics, + fields.iter().map(|field| quote_expr!(cx, &self.value.$field)), + ); + + let type_name = builder.expr().str(type_ident); + + quote_expr!(cx, { + $visitor_struct + $visitor_impl + $serializer.visit_named_map($type_name, Visitor { + value: self, + state: 0, + }) + }) +} + +fn serialize_enum( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + serializer: P, + type_ident: Ident, + variant: &ast::Variant, + fields: &[FieldInfo], + generics: &ast::Generics, +) -> P { + let type_name = builder.expr().str(type_ident); + let variant_name = builder.expr().str(variant.node.name); + + if fields.is_empty() { + quote_expr!(cx, + ::serde::ser::Serializer::visit_enum_unit( + $serializer, + $type_name, + $variant_name) + ) + } else { + match variant.node.kind { + ast::TupleVariantKind(ref args) => { + serialize_tuple_variant( + cx, + builder, + serializer, + type_name, + variant_name, + generics, + args, + fields, + ) + } + ast::StructVariantKind(ref struct_def) => { + serialize_struct_variant( + cx, + builder, + serializer, + type_name, + variant_name, + generics, + struct_def, + fields, + ) + } + } + } +} + +fn serialize_tuple_variant( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + serializer: P, + type_name: P, + variant_name: P, + generics: &ast::Generics, + args: &[ast::VariantArg], + fields: &[FieldInfo], +) -> P { + let value_ty = builder.ty().tuple() + .with_tys( + args.iter().map(|arg| { + builder.ty() + .ref_() + .lifetime("'__a") + .build_ty(arg.ty.clone()) + }) + ) + .build(); + + let value_expr = builder.expr().tuple() + .with_exprs( + fields.iter().map(|field| { + builder.expr() + .addr_of() + .build(field.self_.clone()) + }) + ) + .build(); + + let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( + cx, + builder, + value_ty, + args.len(), + generics, + ); + + quote_expr!(cx, { + $visitor_struct + $visitor_impl + $serializer.visit_enum_seq($type_name, $variant_name, Visitor { + value: $value_expr, + state: 0, + }) + }) +} + +fn serialize_struct_variant( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + serializer: P, + type_name: P, + variant_name: P, + generics: &ast::Generics, + struct_def: &ast::StructDef, + fields: &[FieldInfo], +) -> P { + let value_ty = builder.ty().tuple() + .with_tys( + struct_def.fields.iter().map(|field| { + builder.ty() + .ref_() + .lifetime("'__a") + .build_ty(field.node.ty.clone()) + }) + ) + .build(); + + let value_expr = builder.expr().tuple() + .with_exprs( + fields.iter().map(|field| { + builder.expr() + .addr_of() + .build(field.self_.clone()) + }) + ) + .build(); + + let (visitor_struct, visitor_impl) = serialize_struct_visitor( + cx, + builder, + value_ty, + struct_def, + generics, + (0 .. fields.len()).map(|i| { + builder.expr() + .tup_field(i) + .field("value").self_() + }) + ); + + quote_expr!(cx, { + $visitor_struct + $visitor_impl + $serializer.visit_enum_map($type_name, $variant_name, Visitor { + value: $value_expr, + state: 0, + }) + }) +} + +fn serialize_tuple_struct_visitor( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + value_ty: P, + fields: usize, + generics: &ast::Generics +) -> (P, P) { + let arms: Vec = (0 .. fields) + .map(|i| { + let first = builder.expr().bool(i == 0); + let expr = builder.expr() + .tup_field(i) + .field("value").self_(); + + quote_arm!(cx, + $i => { + self.state += 1; + let v = try!(serializer.visit_seq_elt($first, &$expr)); + Ok(Some(v)) + } + ) + }) + .collect(); + + let visitor_impl_generics = builder.from_generics(generics.clone()) + .add_lifetime_bound("'__a") + .add_ty_param_bound( + builder.path().global().ids(&["serde", "ser", "Serialize"]).build() + ) + .lifetime_name("'__a") + .build(); + + let where_clause = &visitor_impl_generics.where_clause; + + let visitor_generics = builder.from_generics(visitor_impl_generics.clone()) + .strip_bounds() + .build(); + + ( + quote_item!(cx, + struct Visitor $visitor_impl_generics $where_clause { + state: usize, + value: $value_ty, + } + ).unwrap(), + + quote_item!(cx, + impl $visitor_impl_generics ::serde::ser::SeqVisitor + for Visitor $visitor_generics + $where_clause { + #[inline] + fn visit(&mut self, serializer: &mut S) -> Result, S::Error> + where S: ::serde::ser::Serializer, + { + match self.state { + $arms + _ => Ok(None), + } + } + + #[inline] + fn len(&self) -> Option { + Some($fields) + } + } + ).unwrap(), + ) +} + +fn serialize_struct_visitor( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + value_ty: P, + struct_def: &StructDef, + generics: &ast::Generics, + value_exprs: I, +) -> (P, P) + where I: Iterator>, +{ + let len = struct_def.fields.len(); + + let key_exprs = struct_def.fields.iter() + .map(|field| { + match field_alias(field) { + Some(lit) => builder.expr().build_lit(P(lit.clone())), + None => { + match field.node.kind { + ast::NamedField(name, _) => { + builder.expr().str(name) + } + ast::UnnamedField(_) => { + cx.bug("struct has named and unnamed fields") + } + } + } + } + }); + + let arms: Vec = key_exprs + .zip(value_exprs) + .enumerate() + .map(|(i, (key_expr, value_expr))| { + let first = i == 0; + + quote_arm!(cx, + $i => { + self.state += 1; + Ok( + Some( + try!( + serializer.visit_map_elt( + $first, + $key_expr, + $value_expr, + ) + ) + ) + ) + } + ) + }) + .collect(); + + let visitor_impl_generics = builder.from_generics(generics.clone()) + .add_lifetime_bound("'__a") + .add_ty_param_bound( + builder.path().global().ids(&["serde", "ser", "Serialize"]).build() + ) + .lifetime_name("'__a") + .build(); + + let where_clause = &visitor_impl_generics.where_clause; + + let visitor_generics = builder.from_generics(visitor_impl_generics.clone()) + .strip_bounds() + .build(); + + ( + quote_item!(cx, + struct Visitor $visitor_impl_generics $where_clause { + state: usize, + value: $value_ty, + } + ).unwrap(), + + quote_item!(cx, + impl $visitor_impl_generics + ::serde::ser::MapVisitor + for Visitor $visitor_generics + $where_clause { + #[inline] + fn visit(&mut self, serializer: &mut S) -> Result, S::Error> + where S: ::serde::ser::Serializer, + { + match self.state { + $arms + _ => Ok(None), + } + } + + #[inline] + fn len(&self) -> Option { + Some($len) + } + } + ).unwrap(), + ) +}