From 14d97f9c26c571518fffc292ef3a90bad6a63719 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 27 Jul 2014 20:16:18 -0700 Subject: [PATCH] Cleaning up #[deriving_deserializable] --- serde_macros/src/lib.rs | 261 ++++++++++++++++++++++------------------ src/de.rs | 2 +- 2 files changed, 142 insertions(+), 121 deletions(-) diff --git a/serde_macros/src/lib.rs b/serde_macros/src/lib.rs index a464b9ee..dc7aa9f5 100644 --- a/serde_macros/src/lib.rs +++ b/serde_macros/src/lib.rs @@ -307,135 +307,62 @@ fn deserializable_substructure(cx: &mut ExtCtxt, trait_span: Span, match *substr.fields { StaticStruct(_, ref fields) => { - let struct_block = struct_block( + deserialize_struct( cx, trait_span, substr.type_ident, fields, - deserializer - ); - - let map_block = map_block( - cx, - trait_span, - substr.type_ident, - fields, - deserializer - ); - - quote_expr!( - cx, - match $token { - ::serde::de::StructStart(_, _) => $struct_block, - ::serde::de::MapStart(_) => $map_block, - _ => $deserializer.syntax_error(), - } - ) + deserializer, + token) } StaticEnum(_, ref fields) => { - let type_name = cx.expr_str( + deserialize_enum( + cx, trait_span, - token::get_ident(substr.type_ident) - ); - - let variants = fields.iter() - .map(|&(name, span, _)| { - cx.expr_str(span, token::get_ident(name)) - }) - .collect(); - - let variants = cx.expr_vec(trait_span, variants); - - let arms: Vec = fields.iter() - .enumerate() - .map(|(i, &(name, span, ref parts))| { - let call = deserializable_static_fields( - cx, - span, - name, - parts, - |cx, span, _| { - cx.expr_try(span, - cx.expr_method_call( - span, - deserializer, - cx.ident_of("expect_enum_sep"), - vec!() - ) - ) - } - ); - - cx.arm( - span, - vec!( - cx.pat_lit(span, cx.expr_uint(span, i)), - ), - call - ) - }) - .collect(); - - quote_expr!(cx, { - let i = try!($deserializer.expect_enum_start($token, $type_name, $variants)); - - let result = match i { - $arms - _ => { unreachable!() } - }; - - try!($deserializer.expect_enum_end()); - - Ok(result) - }) + substr.type_ident, + fields.as_slice(), + deserializer, + token) } _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Deserializable)") } } -/// Create a deserializer for a single enum variant/struct: -/// - `outer_pat_ident` is the name of this enum variant/struct -/// - `getarg` should retrieve the `uint`-th field with name `&str`. -fn deserializable_static_fields( - cx: &ExtCtxt, - trait_span: Span, - outer_pat_ident: Ident, +fn deserialize_struct<'a>( + cx: &ExtCtxt<'a>, + span: Span, + type_ident: Ident, fields: &StaticFields, - getarg: |&ExtCtxt, Span, token::InternedString| -> Gc -) -> Gc { - match *fields { - Unnamed(ref fields) => { - if fields.is_empty() { - cx.expr_ident(trait_span, outer_pat_ident) - } else { - let fields = fields.iter().enumerate().map(|(i, &span)| { - getarg( - cx, - span, - token::intern_and_get_ident(format!("_field{}", i).as_slice()) - ) - }).collect(); + deserializer: Gc, + token: Gc +) -> Gc { + let struct_block = deserialize_struct_from_struct( + cx, + span, + type_ident, + fields, + deserializer + ); - cx.expr_call_ident(trait_span, outer_pat_ident, fields) - } - } - Named(ref fields) => { - // use the field's span to get nicer error messages. - let fields = fields.iter().map(|&(name, span)| { - let arg = getarg( - cx, - span, - token::get_ident(name) - ); - cx.field_imm(span, name, arg) - }).collect(); + let map_block = deserialize_struct_from_map( + cx, + span, + type_ident, + fields, + deserializer + ); - cx.expr_struct_ident(trait_span, outer_pat_ident, fields) + quote_expr!( + cx, + match $token { + ::serde::de::StructStart(_, _) => $struct_block, + ::serde::de::MapStart(_) => $map_block, + _ => $deserializer.syntax_error(), } - } + ) } -fn struct_block<'a>( +fn deserialize_struct_from_struct<'a>( cx: &ExtCtxt<'a>, span: Span, type_ident: Ident, @@ -491,7 +418,7 @@ fn struct_block<'a>( ) } -fn map_block<'a>( +fn deserialize_struct_from_map<'a>( cx: &ExtCtxt<'a>, span: Span, type_ident: Ident, @@ -506,17 +433,20 @@ fn map_block<'a>( // Declare each field. let let_fields: Vec> = fields.iter() .map(|&(name, span)| { - cx.stmt_let(span, true, name, cx.expr_none(span)) + quote_stmt!(cx, let mut $name = None) }) .collect(); // Declare key arms. - let key_arms: Vec> = fields.iter() + let key_arms: Vec = fields.iter() .map(|&(name, span)| { let s = cx.expr_str(span, token::get_ident(name)); - quote_tokens!(cx, $s => { - $name = Some(try!(::serde::de::Deserializable::deserialize($deserializer))); - }) + quote_arm!(cx, + $s => { + $name = Some( + try!(::serde::de::Deserializable::deserialize($deserializer)) + ); + }) }) .collect(); @@ -528,10 +458,10 @@ fn map_block<'a>( }) .collect() ); - + let fields_pats: Vec> = fields.iter() .map(|&(name, span)| { - cx.pat_some(span, cx.pat_ident(span, name)) + quote_pat!(cx, Some($name)) }) .collect(); @@ -569,7 +499,7 @@ fn map_block<'a>( } let result = match $fields_tuple { - $fields_pat => { $result } + $fields_pat => $result, _ => { return $deserializer.syntax_error(); } }; @@ -578,3 +508,94 @@ fn map_block<'a>( Ok(result) }) } + +fn deserialize_enum<'a>( + cx: &ExtCtxt<'a>, + span: Span, + type_ident: Ident, + fields: &[(Ident, Span, StaticFields)], + deserializer: Gc, + token: Gc +) -> Gc { + let type_name = cx.expr_str(span, token::get_ident(type_ident)); + + let variants = fields.iter() + .map(|&(name, span, _)| { + cx.expr_str(span, token::get_ident(name)) + }) + .collect(); + + let variants = cx.expr_vec(span, variants); + + let arms: Vec = fields.iter() + .enumerate() + .map(|(i, &(name, span, ref parts))| { + let call = deserializable_static_fields( + cx, + span, + name, + parts, + |cx, span, _| { + quote_expr!(cx, try!($deserializer.expect_enum_elt())) + } + ); + + quote_arm!(cx, $i => $call,) + }) + .collect(); + + quote_expr!(cx, { + let i = try!($deserializer.expect_enum_start($token, $type_name, $variants)); + + let result = match i { + $arms + _ => { unreachable!() } + }; + + try!($deserializer.expect_enum_end()); + + Ok(result) + }) +} + +/// Create a deserializer for a single enum variant/struct: +/// - `outer_pat_ident` is the name of this enum variant/struct +/// - `getarg` should retrieve the `uint`-th field with name `&str`. +fn deserializable_static_fields( + cx: &ExtCtxt, + trait_span: Span, + outer_pat_ident: Ident, + fields: &StaticFields, + getarg: |&ExtCtxt, Span, token::InternedString| -> Gc +) -> Gc { + match *fields { + Unnamed(ref fields) => { + if fields.is_empty() { + cx.expr_ident(trait_span, outer_pat_ident) + } else { + let fields = fields.iter().enumerate().map(|(i, &span)| { + getarg( + cx, + span, + token::intern_and_get_ident(format!("_field{}", i).as_slice()) + ) + }).collect(); + + cx.expr_call_ident(trait_span, outer_pat_ident, fields) + } + } + Named(ref fields) => { + // use the field's span to get nicer error messages. + let fields = fields.iter().map(|&(name, span)| { + let arg = getarg( + cx, + span, + token::get_ident(name) + ); + cx.field_imm(span, name, arg) + }).collect(); + + cx.expr_struct_ident(trait_span, outer_pat_ident, fields) + } + } +} diff --git a/src/de.rs b/src/de.rs index d805c374..7ca6cbc3 100644 --- a/src/de.rs +++ b/src/de.rs @@ -229,7 +229,7 @@ pub trait Deserializer: Iterator> { } #[inline] - fn expect_enum_sep< + fn expect_enum_elt< T: Deserializable >(&mut self) -> Result { Deserializable::deserialize(self)