Initial support for #[deriving_deserializable] deserializing from maps

This commit is contained in:
Erick Tryzelaar
2014-07-27 18:33:16 -07:00
parent 4077d83cf2
commit 04e2528a29
+208 -147
View File
@@ -2,7 +2,7 @@
#![crate_type = "dylib"] #![crate_type = "dylib"]
#![license = "MIT/ASL2"] #![license = "MIT/ASL2"]
#![feature(plugin_registrar)] #![feature(plugin_registrar, quote)]
extern crate syntax; extern crate syntax;
extern crate rustc; extern crate rustc;
@@ -303,174 +303,91 @@ pub fn expand_deriving_deserializable(cx: &mut ExtCtxt,
fn deserializable_substructure(cx: &mut ExtCtxt, trait_span: Span, fn deserializable_substructure(cx: &mut ExtCtxt, trait_span: Span,
substr: &Substructure) -> Gc<Expr> { substr: &Substructure) -> Gc<Expr> {
let deserializer = substr.nonself_args[0]; let deserializer = substr.nonself_args[0];
let token = substr.nonself_args[1];
match *substr.fields { match *substr.fields {
StaticStruct(_, ref summary) => { StaticStruct(_, ref fields) => {
let mut stmts = vec!(); let struct_block = struct_block(
let call = cx.expr_method_call(
trait_span,
deserializer,
cx.ident_of("expect_struct_start"),
vec!(
substr.nonself_args[1],
cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
)
);
let call = cx.expr_try(trait_span, call);
stmts.push(cx.stmt_expr(call));
let expect_struct_field = cx.ident_of("expect_struct_field");
let call = deserializable_static_fields(
cx, cx,
trait_span, trait_span,
substr.type_ident, substr.type_ident,
summary, fields,
|cx, span, name| { deserializer
cx.expr_try(span,
cx.expr_method_call(
span,
deserializer,
expect_struct_field,
vec!(
cx.expr_str(span, name),
)
)
)
}
); );
let result = cx.ident_of("result"); let map_block = map_block(
cx,
stmts.push(
cx.stmt_let(
trait_span,
false,
result,
call
)
);
let call = cx.expr_method_call(
trait_span, trait_span,
deserializer, substr.type_ident,
cx.ident_of("expect_struct_end"), fields,
vec!() deserializer
); );
let call = cx.expr_try(trait_span, call);
stmts.push(cx.stmt_expr(call));
cx.expr_block( quote_expr!(
cx.block( cx,
trait_span, match $token {
stmts, ::serde::de::StructStart(_, _) => $struct_block,
Some( ::serde::de::MapStart(_) => $map_block,
cx.expr_ok( _ => $deserializer.syntax_error(),
trait_span, }
cx.expr_ident(trait_span, result)
)
)
)
) )
} }
StaticEnum(_, ref fields) => { StaticEnum(_, ref fields) => {
let mut stmts = vec!(); let type_name = cx.expr_str(
trait_span,
token::get_ident(substr.type_ident)
);
let mut arms = vec!(); let variants = fields.iter()
let mut variants = vec!(); .map(|&(name, span, _)| {
cx.expr_str(span, token::get_ident(name))
})
.collect();
let expect_enum_sep = cx.ident_of("expect_enum_sep"); let variants = cx.expr_vec(trait_span, variants);
for (i, &(name, v_span, ref parts)) in fields.iter().enumerate() {
variants.push(cx.expr_str(v_span, token::get_ident(name)));
let deserializabled = deserializable_static_fields(cx, let arms: Vec<ast::Arm> = fields.iter()
v_span, .enumerate()
name, .map(|(i, &(name, span, ref parts))| {
parts, let call = deserializable_static_fields(
|cx, span, _| { cx,
cx.expr_try(span, span,
cx.expr_method_call( name,
span, parts,
deserializer, |cx, span, _| {
expect_enum_sep, cx.expr_try(span,
vec!() cx.expr_method_call(
) span,
) deserializer,
}); cx.ident_of("expect_enum_sep"),
vec!()
)
)
}
);
arms.push(
cx.arm( cx.arm(
v_span, span,
vec!( vec!(
cx.pat_lit(v_span, cx.expr_uint(v_span, i)), cx.pat_lit(span, cx.expr_uint(span, i)),
), ),
deserializabled call
) )
); })
} .collect();
arms.push(cx.arm_unreachable(trait_span)); quote_expr!(cx, {
let i = try!($deserializer.expect_enum_start($token, $type_name, $variants));
let result = match i {
$arms
_ => { unreachable!() }
};
let call = cx.expr_method_call( try!($deserializer.expect_enum_end());
trait_span,
deserializer,
cx.ident_of("expect_enum_start"),
vec!(
substr.nonself_args[1],
cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
cx.expr_vec(trait_span, variants),
)
);
let call = cx.expr_try(trait_span, call);
let variant = cx.ident_of("i"); Ok(result)
stmts.push( })
cx.stmt_let(
trait_span,
false,
variant,
call
)
);
let result = cx.ident_of("result");
let call = cx.expr_match(
trait_span,
cx.expr_ident(trait_span, variant),
arms
);
stmts.push(
cx.stmt_let(
trait_span,
false,
result,
call
)
);
let call = cx.expr_method_call(
trait_span,
deserializer,
cx.ident_of("expect_enum_end"),
vec!()
);
let call = cx.expr_try(trait_span, call);
stmts.push(cx.stmt_expr(call));
cx.expr_block(
cx.block(
trait_span,
stmts,
Some(
cx.expr_ok(
trait_span,
cx.expr_ident(trait_span, result)
)
)
)
)
} }
_ => cx.bug("expected StaticEnum or StaticStruct in deriving(Deserializable)") _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Deserializable)")
} }
@@ -478,13 +395,13 @@ fn deserializable_substructure(cx: &mut ExtCtxt, trait_span: Span,
/// Create a deserializer for a single enum variant/struct: /// Create a deserializer for a single enum variant/struct:
/// - `outer_pat_ident` is the name of this enum variant/struct /// - `outer_pat_ident` is the name of this enum variant/struct
/// - `getarg` should retrieve the `uint`-th field with name `@str`. /// - `getarg` should retrieve the `uint`-th field with name `&str`.
fn deserializable_static_fields( fn deserializable_static_fields(
cx: &mut ExtCtxt, cx: &ExtCtxt,
trait_span: Span, trait_span: Span,
outer_pat_ident: Ident, outer_pat_ident: Ident,
fields: &StaticFields, fields: &StaticFields,
getarg: |&mut ExtCtxt, Span, token::InternedString| -> Gc<Expr> getarg: |&ExtCtxt, Span, token::InternedString| -> Gc<Expr>
) -> Gc<Expr> { ) -> Gc<Expr> {
match *fields { match *fields {
Unnamed(ref fields) => { Unnamed(ref fields) => {
@@ -517,3 +434,147 @@ fn deserializable_static_fields(
} }
} }
} }
fn struct_block<'a>(
cx: &ExtCtxt<'a>,
span: Span,
type_ident: Ident,
fields: &StaticFields,
deserializer: Gc<ast::Expr>
) -> Gc<ast::Expr> {
let mut stmts = vec!();
let expect_struct_field = cx.ident_of("expect_struct_field");
let call = deserializable_static_fields(
cx,
span,
type_ident,
fields,
|cx, span, name| {
cx.expr_try(span,
cx.expr_method_call(
span,
deserializer,
expect_struct_field,
vec!(
cx.expr_str(span, name),
)
)
)
}
);
let result = cx.ident_of("result");
stmts.push(
cx.stmt_let(span, false, result, call)
);
let call = cx.expr_method_call(
span,
deserializer,
cx.ident_of("expect_struct_end"),
vec!()
);
let call = cx.expr_try(span, call);
stmts.push(cx.stmt_expr(call));
let expr = cx.expr_ok(span, cx.expr_ident(span, result));
cx.expr_block(
cx.block(
span,
stmts,
Some(expr)
)
)
}
fn map_block<'a>(
cx: &ExtCtxt<'a>,
span: Span,
type_ident: Ident,
fields: &StaticFields,
deserializer: Gc<ast::Expr>
) -> Gc<ast::Expr> {
let fields = match *fields {
Unnamed(_) => fail!(),
Named(ref fields) => fields.as_slice(),
};
// Declare each field.
let let_fields: Vec<Gc<ast::Stmt>> = fields.iter()
.map(|&(name, span)| {
cx.stmt_let(span, true, name, cx.expr_none(span))
})
.collect();
// Declare key arms.
let key_arms: Vec<Vec<ast::TokenTree>> = 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)));
})
})
.collect();
let fields_tuple = cx.expr_tuple(
span,
fields.iter()
.map(|&(name, span)| {
cx.expr_ident(span, name)
})
.collect()
);
let fields_pats: Vec<Gc<ast::Pat>> = fields.iter()
.map(|&(name, span)| {
cx.pat_some(span, cx.pat_ident(span, name))
})
.collect();
let fields_pat = cx.pat_tuple(span, fields_pats);
let result = cx.expr_struct_ident(
span,
type_ident,
fields.iter()
.map(|&(name, span)| {
cx.field_imm(span, name, cx.expr_ident(span, name))
})
.collect()
);
quote_expr!(cx, {
$let_fields
loop {
let token = match try!($deserializer.expect_token()) {
::serde::de::End => { break; }
token => token,
};
let key = match token {
::serde::de::Str(s) => s,
::serde::de::String(ref s) => s.as_slice(),
_ => { return $deserializer.syntax_error(); }
};
match key {
$key_arms
_ => { return $deserializer.syntax_error(); }
}
}
let result = match $fields_tuple {
$fields_pat => { $result }
_ => { return $deserializer.syntax_error(); }
};
try!($deserializer.expect_struct_end());
Ok(result)
})
}