Merge pull request #22 from hugoduncan/feature/add-field-aliases
Add aliases on struct fields
This commit is contained in:
commit
dd4f7537ea
@ -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, {
|
||||||
|
Loading…
Reference in New Issue
Block a user