From 3fd42e616c61f7ba167651de2347b457bbba87bf Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 10 Feb 2015 21:08:04 -0800 Subject: [PATCH] Initial macro support for deserializing structs and tuple structs --- serde2/serde2_macros/src/lib.rs | 362 ++++++++++++++++++++++++++------ serde2/src/de.rs | 2 + serde2/src/json/error.rs | 16 +- 3 files changed, 316 insertions(+), 64 deletions(-) diff --git a/serde2/serde2_macros/src/lib.rs b/serde2/serde2_macros/src/lib.rs index 5bd233fa..e76ceee0 100644 --- a/serde2/serde2_macros/src/lib.rs +++ b/serde2/serde2_macros/src/lib.rs @@ -12,30 +12,28 @@ use syntax::ast::{ //LitNil, }; use syntax::ast; -use syntax::codemap::Span; +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, + Named, + StaticFields, + StaticStruct, //StaticEnum, Struct, Substructure, TraitDef, - //Unnamed, + Unnamed, combine_substructure, }; use syntax::ext::deriving::generic::ty::{ Borrowed, LifetimeBounds, - Literal, + Ty, Path, - Ptr, - //Self, //Tuple, borrowed_explicit_self, }; @@ -51,11 +49,9 @@ pub fn plugin_registrar(reg: &mut Registry) { token::intern("derive_serialize"), Decorator(Box::new(expand_derive_serialize))); - /* reg.register_syntax_extension( token::intern("derive_deserialize"), - ItemDecorator(Box::new(expand_derive_deserialize))); - */ + Decorator(Box::new(expand_derive_deserialize))); } fn expand_derive_serialize<>(cx: &mut ExtCtxt, @@ -85,24 +81,24 @@ fn expand_derive_serialize<>(cx: &mut ExtCtxt, }, explicit_self: borrowed_explicit_self(), args: vec![ - Ptr( - Box::new(Literal(Path::new_local("__V"))), + Ty::Ptr( + Box::new(Ty::Literal(Path::new_local("__V"))), Borrowed(None, MutMutable), ), ], - ret_ty: Literal( + ret_ty: Ty::Literal( Path::new_( vec!("std", "result", "Result"), None, vec![ - Box::new(Literal(Path::new_(vec!["__V", "Value"], - None, - vec![], - false))), - Box::new(Literal(Path::new_(vec!["__V", "Error"], - None, - vec![], - false))), + Box::new(Ty::Literal(Path::new_(vec!["__V", "Value"], + None, + vec![], + false))), + Box::new(Ty::Literal(Path::new_(vec!["__V", "Error"], + None, + vec![], + false))), ], true ) @@ -115,7 +111,7 @@ fn expand_derive_serialize<>(cx: &mut ExtCtxt, ] }; - trait_def.expand(cx, mitem, item, |item| push.call_mut((item,))) + trait_def.expand(cx, mitem, item, |item| push(item)) } fn serialize_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> P { @@ -241,64 +237,64 @@ fn serialize_enum(cx: &ExtCtxt, }) } -/* pub fn expand_derive_deserialize(cx: &mut ExtCtxt, - span: Span, - mitem: &MetaItem, - item: &Item, - push: |P|) { + sp: Span, + mitem: &MetaItem, + item: &Item, + mut push: Box)>) +{ + let inline = cx.meta_word(sp, token::InternedString::new("inline")); + let attrs = vec!(cx.attribute(sp, inline)); + let trait_def = TraitDef { - span: span, + span: sp, attributes: Vec::new(), - path: Path::new_(vec!("serde2", "de", "Deserialize"), None, - vec!(Box::new(Literal(Path::new_local("__D"))), - Box::new(Literal(Path::new_local("__E")))), true), + path: Path::new(vec!["serde2", "de", "Deserialize"]), additional_bounds: Vec::new(), - generics: LifetimeBounds { - lifetimes: Vec::new(), - bounds: vec!(("__D", None, vec!(Path::new_( - vec!("serde2", "de", "Deserializer"), None, - vec!(Box::new(Literal(Path::new_local("__E")))), true))), - ("__E", None, vec!())) - }, + generics: LifetimeBounds::empty(), associated_types: vec![], methods: vec!( MethodDef { - name: "deserialize_token", - generics: LifetimeBounds::empty(), + name: "deserialize", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec![ + ("__S", vec![Path::new(vec!["serde2", "de", "Deserializer"])]), + ], + }, explicit_self: None, - args: vec!( - Ptr( - Box::new(Literal(Path::new_local("__D"))), + args: vec![ + Ty::Ptr( + Box::new(Ty::Literal(Path::new_local("__S"))), Borrowed(None, MutMutable) ), - Literal(Path::new(vec!("serde2", "de", "Token"))), - ), - ret_ty: Literal( + ], + ret_ty: Ty::Literal( Path::new_( - vec!("std", "result", "Result"), + vec!["std", "result", "Result"], None, - vec!( - Box::new(Self), - Box::new(Literal(Path::new_local("__E"))) - ), + vec![ + Box::new(Ty::Self), + Box::new(Ty::Literal(Path::new_(vec!["__S", "Error"], + None, + vec![], + false))), + ], true ) ), - attributes: Vec::new(), + attributes: attrs, combine_substructure: combine_substructure(Box::new(|a, b, c| { deserialize_substructure(a, b, c) })), }) }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand(cx, mitem, item, |item| push(item)) } -fn deserialize_substructure(cx: &mut ExtCtxt, span: Span, - substr: &Substructure) -> P { - let deserializer = substr.nonself_args[0]; - let token = substr.nonself_args[1]; +fn deserialize_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> P { + let state = substr.nonself_args[0].clone(); match *substr.fields { StaticStruct(_, ref fields) => { @@ -307,9 +303,9 @@ fn deserialize_substructure(cx: &mut ExtCtxt, span: Span, span, substr.type_ident, fields, - deserializer, - token) + state) } + /* StaticEnum(_, ref fields) => { deserialize_enum( cx, @@ -319,10 +315,250 @@ fn deserialize_substructure(cx: &mut ExtCtxt, span: Span, deserializer, token) } + */ _ => cx.bug("expected StaticEnum or StaticStruct in derive(Deserialize)") } } +fn deserialize_struct( + cx: &ExtCtxt, + span: Span, + type_ident: Ident, + fields: &StaticFields, + state: P, +) -> P { + match *fields { + Unnamed(ref fields) => { + deserialize_struct_unnamed_fields( + cx, + span, + type_ident, + &fields[], + state) + } + Named(ref fields) => { + deserialize_struct_named_fields( + cx, + span, + type_ident, + &fields[], + state) + } + } +} + +fn deserialize_struct_unnamed_fields( + cx: &ExtCtxt, + span: Span, + type_ident: Ident, + fields: &[Span], + state: P, +) -> P { + let type_name = cx.expr_str(span, token::get_ident(type_ident)); + + let field_names: Vec = (0 .. fields.len()) + .map(|i| token::str_to_ident(&format!("__field{}", i))) + .collect(); + + let let_values: Vec> = field_names.iter() + .map(|name| { + quote_stmt!(cx, + let $name = match try!(visitor.visit()) { + Some(value) => value, + None => { + return Err(::serde2::de::Error::end_of_stream_error()); + } + }; + ) + }) + .collect(); + + let result = cx.expr_call_ident( + span, + type_ident, + field_names.iter().map(|name| cx.expr_ident(span, *name)).collect()); + + quote_expr!(cx, { + struct __Visitor; + + impl ::serde2::de::Visitor for __Visitor { + type Value = $type_ident; + + fn visit_seq< + __V: ::serde2::de::SeqVisitor, + >(&mut self, mut visitor: __V) -> Result<$type_ident, __V::Error> { + $let_values + + try!(visitor.end()); + + Ok($result) + } + + fn visit_named_seq< + __V: ::serde2::de::SeqVisitor, + >(&mut self, name: &str, visitor: __V) -> Result<$type_ident, __V::Error> { + if name == $type_name { + self.visit_seq(visitor) + } else { + Err(::serde2::de::Error::syntax_error()) + } + } + } + + $state.visit(&mut __Visitor) + }) +} + +fn deserialize_struct_named_fields( + cx: &ExtCtxt, + span: Span, + type_ident: Ident, + fields: &[(Ident, Span)], + state: P, +) -> P { + let type_name = cx.expr_str(span, token::get_ident(type_ident)); + + // Create the field names for the fields. + let field_names: Vec = (0 .. fields.len()) + .map(|i| token::str_to_ident(&format!("__field{}", i))) + .collect(); + + // 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 }); + + // 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),) + }) + .collect(); + + // Declare each field. + let let_values: Vec> = field_names.iter() + .map(|field| { + 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 => { + return Err(::serde2::de::Error::missing_field_error($name_str)); + } + }; + ) + }) + .collect(); + + let result = cx.expr_struct_ident( + span, + type_ident, + fields.iter() + .zip(field_names.iter()) + .map(|(&(name, span), field)| { + cx.field_imm(span, name, cx.expr_ident(span, *field)) + }) + .collect() + ); + + quote_expr!(cx, { + #[allow(non_camel_case_types)] + $field_enum + + struct __FieldVisitor; + + impl ::serde2::de::Visitor for __FieldVisitor { + type Value = __Field; + + fn visit_str< + E: ::serde2::de::Error, + >(&mut self, value: &str) -> Result<__Field, E> { + match value { + $field_arms + _ => Err(::serde2::de::Error::syntax_error()), + } + } + } + + impl ::serde2::de::Deserialize for __Field { + #[inline] + fn deserialize< + __S: ::serde2::de::Deserializer, + >(state: &mut __S) -> Result<__Field, __S::Error> { + state.visit(&mut __FieldVisitor) + } + } + + struct __Visitor; + + impl ::serde2::de::Visitor for __Visitor { + type Value = $type_ident; + + fn visit_map< + __V: ::serde2::de::MapVisitor, + >(&mut self, mut visitor: __V) -> Result<$type_ident, __V::Error> { + $let_values + + while let Some(key) = try!(visitor.visit_key()) { + match key { + $value_arms + } + } + + $extract_values + Ok($result) + } + + fn visit_named_map< + __V: ::serde2::de::MapVisitor, + >(&mut self, name: &str, visitor: __V) -> Result<$type_ident, __V::Error> { + if name == $type_name { + self.visit_map(visitor) + } else { + Err(::serde2::de::Error::syntax_error()) + } + } + } + + $state.visit(&mut __Visitor) + }) +} + +/* fn deserialize_struct( cx: &ExtCtxt, span: Span, @@ -331,6 +567,7 @@ fn deserialize_struct( deserializer: P, token: P ) -> P { + /* let struct_block = deserialize_struct_from_struct( cx, span, @@ -338,6 +575,7 @@ fn deserialize_struct( fields, deserializer ); + */ let map_block = deserialize_struct_from_map( cx, @@ -363,6 +601,7 @@ fn deserialize_struct( ) } +/* fn deserialize_struct_from_struct( cx: &ExtCtxt, span: Span, @@ -392,6 +631,7 @@ fn deserialize_struct_from_struct( Ok(result) }) } +*/ fn deserialize_struct_from_map( cx: &ExtCtxt, @@ -401,7 +641,7 @@ fn deserialize_struct_from_map( deserializer: P ) -> P { let fields = match *fields { - Unnamed(_) => fail!(), + Unnamed(_) => panic!(), Named(ref fields) => &fields[], }; diff --git a/serde2/src/de.rs b/serde2/src/de.rs index 91af3885..462553c0 100644 --- a/serde2/src/de.rs +++ b/serde2/src/de.rs @@ -10,6 +10,8 @@ pub trait Error { fn syntax_error() -> Self; fn end_of_stream_error() -> Self; + + fn missing_field_error(&'static str) -> Self; } pub trait Deserialize { diff --git a/serde2/src/json/error.rs b/serde2/src/json/error.rs index 1163e096..6f1b6497 100644 --- a/serde2/src/json/error.rs +++ b/serde2/src/json/error.rs @@ -84,7 +84,9 @@ pub enum Error { IoError(old_io::IoError), /* ExpectedError(String, String), - MissingFieldError(String), + */ + MissingFieldError(&'static str), + /* UnknownVariantError(String), */ } @@ -96,7 +98,9 @@ impl error::Error for Error { Error::IoError(ref error) => error.description().as_slice(), /* Error::ExpectedError(ref expected, _) => &expected, + */ Error::MissingFieldError(_) => "missing field", + /* Error::UnknownVariantError(_) => "unknown variant", */ } @@ -115,16 +119,18 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Error::SyntaxError(ref code, line, col) => { - write!(fmt, "{:?} at line {:?} column {:?}", code, line, col) + write!(fmt, "{:?} at line {} column {}", code, line, col) } Error::IoError(ref error) => fmt::Display::fmt(error, fmt), /* Error::ExpectedError(ref expected, ref found) => { Some(format!("expected {}, found {}", expected, found)) } + */ Error::MissingFieldError(ref field) => { - Some(format!("missing field {}", field)) + write!(fmt, "missing field {}", field) } + /* Error::UnknownVariantError(ref variant) => { Some(format!("unknown variant {}", variant)) } @@ -147,4 +153,8 @@ impl de::Error for Error { fn end_of_stream_error() -> Error { Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 0, 0) } + + fn missing_field_error(field: &'static str) -> Error { + Error::MissingFieldError(field) + } }