Merge pull request #22 from hugoduncan/feature/add-field-aliases

Add aliases on struct fields
This commit is contained in:
Erick Tryzelaar 2015-03-03 08:25:37 -08:00
commit dd4f7537ea

View File

@ -6,9 +6,12 @@ extern crate rustc;
use syntax::ast::{ use syntax::ast::{
Ident, Ident,
MetaItem, MetaItem,
MetaItem_,
Item, Item,
Expr, Expr,
MutMutable, MutMutable,
StructDef,
EnumDef,
}; };
use syntax::ast; use syntax::ast;
use syntax::ast_util; use syntax::ast_util;
@ -122,11 +125,12 @@ fn serialize_substructure(cx: &ExtCtxt,
let visitor = substr.nonself_args[0].clone(); let visitor = substr.nonself_args[0].clone();
match (&item.node, &*substr.fields) { match (&item.node, &*substr.fields) {
(&ast::ItemStruct(..), &Struct(ref fields)) => { (&ast::ItemStruct(ref struct_def, _), &Struct(ref fields)) => {
if fields.is_empty() { if fields.is_empty() {
serialize_tuple_struct(cx) serialize_tuple_struct(cx)
} else { } 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, span: Span,
visitor: P<Expr>, visitor: P<Expr>,
type_ident: Ident, type_ident: Ident,
fields: &[FieldInfo]) -> P<Expr> { fields: &[FieldInfo],
struct_def: &StructDef) -> P<Expr> {
let type_name = cx.expr_str( let type_name = cx.expr_str(
span, span,
token::get_ident(type_ident)); token::get_ident(type_ident));
let len = fields.len(); let len = fields.len();
let aliases : Vec<Option<&ast::Lit>> = struct_def.fields.iter()
.map(field_alias)
.collect();
let arms: Vec<ast::Arm> = fields.iter() let arms: Vec<ast::Arm> = fields.iter()
.zip(aliases.iter())
.enumerate() .enumerate()
.map(|(i, &FieldInfo { name, span, .. })| { .map(|(i, (&FieldInfo { name, span, .. }, alias_lit ))| {
let first = if i == 0 { let first = if i == 0 {
quote_expr!(cx, true) quote_expr!(cx, true)
} else { } else {
@ -169,7 +179,13 @@ fn serialize_struct(cx: &ExtCtxt,
}; };
let name = name.unwrap(); 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; 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(); let state = substr.nonself_args[0].clone();
match *substr.fields { match *substr.fields {
StaticStruct(_, ref fields) => { StaticStruct(ref struct_def, ref fields) => {
deserialize_struct( deserialize_struct(
cx, cx,
span, span,
@ -615,15 +631,17 @@ fn deserialize_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) ->
substr.type_ident, substr.type_ident,
cx.path(span, vec![substr.type_ident]), cx.path(span, vec![substr.type_ident]),
fields, fields,
state) state,
struct_def)
} }
StaticEnum(_, ref fields) => { StaticEnum(ref enum_def, ref fields) => {
deserialize_enum( deserialize_enum(
cx, cx,
span, span,
substr.type_ident, substr.type_ident,
&fields, &fields,
state) state,
enum_def)
} }
_ => cx.bug("expected StaticEnum or StaticStruct in derive(Deserialize)") _ => cx.bug("expected StaticEnum or StaticStruct in derive(Deserialize)")
} }
@ -637,6 +655,7 @@ fn deserialize_struct(
struct_path: ast::Path, struct_path: ast::Path,
fields: &StaticFields, fields: &StaticFields,
state: P<ast::Expr>, state: P<ast::Expr>,
struct_def: &StructDef
) -> P<ast::Expr> { ) -> P<ast::Expr> {
match *fields { match *fields {
Unnamed(ref fields) => { Unnamed(ref fields) => {
@ -667,7 +686,8 @@ fn deserialize_struct(
struct_ident, struct_ident,
struct_path, struct_path,
&fields[], &fields[],
state) state,
struct_def)
} }
} }
} }
@ -803,6 +823,7 @@ fn deserialize_struct_named_fields(
struct_path: ast::Path, struct_path: ast::Path,
fields: &[(Ident, Span)], fields: &[(Ident, Span)],
state: P<ast::Expr>, state: P<ast::Expr>,
struct_def: &StructDef,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
let struct_name = cx.expr_str(span, token::get_ident(struct_ident)); let struct_name = cx.expr_str(span, token::get_ident(struct_ident));
@ -816,6 +837,7 @@ fn deserialize_struct_named_fields(
span, span,
&field_names[], &field_names[],
fields, fields,
struct_def,
); );
let visit_map_expr = declare_visit_map( let visit_map_expr = declare_visit_map(
@ -824,6 +846,7 @@ fn deserialize_struct_named_fields(
struct_path, struct_path,
&field_names[], &field_names[],
fields, fields,
struct_def
); );
quote_expr!(cx, { 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( fn declare_map_field_deserializer(
cx: &ExtCtxt, cx: &ExtCtxt,
span: Span, span: Span,
field_names: &[ast::Ident], field_names: &[ast::Ident],
fields: &[(Ident, Span)], fields: &[(Ident, Span)],
struct_def: &StructDef,
) -> Vec<P<ast::Item>> { ) -> Vec<P<ast::Item>> {
// Create the field names for the fields. // Create the field names for the fields.
let field_variants: Vec<P<ast::Variant>> = field_names.iter() let field_variants: Vec<P<ast::Variant>> = field_names.iter()
@ -884,13 +935,24 @@ fn declare_map_field_deserializer(
token::str_to_ident("__Field"), token::str_to_ident("__Field"),
ast::EnumDef { variants: field_variants }); ast::EnumDef { variants: field_variants });
// Get aliases
let aliases : Vec<Option<&ast::Lit>> = struct_def.fields.iter()
.map(field_alias)
.collect();
// Match arms to extract a field from a string // Match arms to extract a field from a string
let field_arms: Vec<ast::Arm> = fields.iter() let field_arms: Vec<ast::Arm> = fields.iter()
.zip(field_names.iter()) .zip(field_names.iter())
.map(|(&(name, span), field)| { .zip(aliases.iter())
let s = cx.expr_str(span, token::get_ident(name)); .map(|((&(name, span), field), alias_lit)| {
quote_arm!(cx, $s => Ok(__Field::$field),) 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(); .collect();
vec![ 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( fn declare_visit_map(
cx: &ExtCtxt, cx: &ExtCtxt,
span: Span, span: Span,
struct_path: ast::Path, struct_path: ast::Path,
field_names: &[Ident], field_names: &[Ident],
fields: &[(Ident, Span)], fields: &[(Ident, Span)],
struct_def: &StructDef,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
// Declare each field. // Declare each field.
let let_values: Vec<P<ast::Stmt>> = field_names.iter() let let_values: Vec<P<ast::Stmt>> = field_names.iter()
.map(|field| { .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;) quote_stmt!(cx, let mut $field = None;)
}
}) })
.collect(); .collect();
@ -1001,12 +1094,14 @@ fn deserialize_enum(
type_ident: Ident, type_ident: Ident,
fields: &[(Ident, Span, StaticFields)], fields: &[(Ident, Span, StaticFields)],
state: P<ast::Expr>, state: P<ast::Expr>,
enum_def: &EnumDef,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
let type_name = cx.expr_str(span, token::get_ident(type_ident)); let type_name = cx.expr_str(span, token::get_ident(type_ident));
// Match arms to extract a variant from a string // Match arms to extract a variant from a string
let variant_arms: Vec<ast::Arm> = fields.iter() let variant_arms: Vec<ast::Arm> = fields.iter()
.map(|&(name, span, ref fields)| { .zip(enum_def.variants.iter())
.map(|(&(name, span, ref fields), variant_ptr)| {
let value = deserialize_enum_variant( let value = deserialize_enum_variant(
cx, cx,
span, span,
@ -1014,6 +1109,7 @@ fn deserialize_enum(
name, name,
fields, fields,
cx.expr_ident(span, cx.ident_of("visitor")), cx.expr_ident(span, cx.ident_of("visitor")),
variant_ptr,
); );
let s = cx.expr_str(span, token::get_ident(name)); let s = cx.expr_str(span, token::get_ident(name));
@ -1058,6 +1154,7 @@ fn deserialize_enum_variant(
variant_ident: Ident, variant_ident: Ident,
fields: &StaticFields, fields: &StaticFields,
state: P<ast::Expr>, state: P<ast::Expr>,
variant_ptr: &P<ast::Variant>,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
let variant_path = cx.path(span, vec![type_ident, variant_ident]); let variant_path = cx.path(span, vec![type_ident, variant_ident]);
@ -1111,6 +1208,10 @@ fn deserialize_enum_variant(
span, span,
&field_names[], &field_names[],
fields, fields,
match variant_ptr.node.kind {
ast::VariantKind::StructVariantKind(ref sd) => &*sd,
_ => panic!("Mismatched enum types")
},
); );
let visit_map_expr = declare_visit_map( let visit_map_expr = declare_visit_map(
@ -1119,6 +1220,10 @@ fn deserialize_enum_variant(
variant_path, variant_path,
&field_names[], &field_names[],
fields, fields,
match variant_ptr.node.kind {
ast::VariantKind::StructVariantKind(ref sd) => &*sd,
_ => panic!("Mismatched enum types")
},
); );
quote_expr!(cx, { quote_expr!(cx, {