refactor(codegen): Add VariantAttrs

This commit is contained in:
Erick Tryzelaar 2016-02-08 08:03:46 -08:00
parent d0ee5b0b4b
commit 365e5129af
4 changed files with 98 additions and 25 deletions

View File

@ -49,6 +49,57 @@ impl ContainerAttrs {
} }
} }
/// Represents variant attribute information
#[derive(Debug)]
pub struct VariantAttrs {
ident: ast::Ident,
name: Option<ast::Lit>,
}
impl VariantAttrs {
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
let mut variant_attrs = VariantAttrs {
ident: variant.node.name,
name: None,
};
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
variant_attrs.name = Some(lit.clone());
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde variant attribute `{}`",
meta_item_to_string(meta_item)));
return Err(Error);
}
}
}
}
Ok(variant_attrs)
}
/// Return the string expression of the field ident.
pub fn ident_expr(&self) -> P<ast::Expr> {
AstBuilder::new().expr().str(self.ident)
}
/// Return the field name for the field when serializing.
pub fn name_expr(&self) -> P<ast::Expr> {
match self.name {
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
None => self.ident_expr(),
}
}
}
/// Represents field attribute information /// Represents field attribute information
#[derive(Debug)] #[derive(Debug)]
pub struct FieldAttrs { pub struct FieldAttrs {
@ -124,22 +175,16 @@ impl FieldAttrs {
}) })
} }
pub fn from_variant(variant: &ast::Variant) -> Self { /// Return the string expression of the field ident.
FieldAttrs { pub fn ident_expr(&self) -> P<ast::Expr> {
ident: variant.node.name, AstBuilder::new().expr().str(self.ident)
name: None,
skip_serializing_field: false,
skip_serializing_field_if_empty: false,
skip_serializing_field_if_none: false,
use_default: false,
}
} }
/// Return the default field name for the field. /// Return the field name for the field when serializing.
pub fn name_expr(&self) -> P<ast::Expr> { pub fn name_expr(&self) -> P<ast::Expr> {
match self.name { match self.name {
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
None => AstBuilder::new().expr().str(self.ident), None => self.ident_expr(),
} }
} }

View File

@ -550,9 +550,14 @@ fn deserialize_item_enum(
let variant_visitor = deserialize_field_visitor( let variant_visitor = deserialize_field_visitor(
cx, cx,
builder, builder,
enum_def.variants.iter() try!(
.map(|variant| attr::FieldAttrs::from_variant(variant)) enum_def.variants.iter()
.collect(), .map(|variant| {
let attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
Ok(attrs.name_expr())
})
.collect()
),
container_attrs, container_attrs,
); );
@ -790,11 +795,11 @@ fn deserialize_struct_variant(
fn deserialize_field_visitor( fn deserialize_field_visitor(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
field_attrs: Vec<attr::FieldAttrs>, field_names: Vec<P<ast::Expr>>,
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
) -> Vec<P<ast::Item>> { ) -> Vec<P<ast::Item>> {
// Create the field names for the fields. // Create the field names for the fields.
let field_idents: Vec<ast::Ident> = (0 .. field_attrs.len()) let field_idents: Vec<ast::Ident> = (0 .. field_names.len())
.map(|i| builder.id(format!("__field{}", i))) .map(|i| builder.id(format!("__field{}", i)))
.collect(); .collect();
@ -832,10 +837,9 @@ fn deserialize_field_visitor(
// Match arms to extract a field from a string // Match arms to extract a field from a string
let default_field_arms: Vec<_> = field_idents.iter() let default_field_arms: Vec<_> = field_idents.iter()
.zip(field_attrs.iter()) .zip(field_names.iter())
.map(|(field_ident, field_attrs)| { .map(|(field_ident, field_name)| {
let expr = &field_attrs.name_expr(); quote_arm!(cx, $field_name => { Ok(__Field::$field_ident) })
quote_arm!(cx, $expr => { Ok(__Field::$field_ident) })
}) })
.collect(); .collect();
@ -916,7 +920,14 @@ fn deserialize_struct_visitor(
let field_visitor = deserialize_field_visitor( let field_visitor = deserialize_field_visitor(
cx, cx,
builder, builder,
try!(attr::get_struct_field_attrs(cx, fields)), try!(
fields.iter()
.map(|field| {
let attrs = try!(attr::FieldAttrs::from_field(cx, field));
Ok(attrs.name_expr())
})
.collect()
),
container_attrs container_attrs
); );
@ -990,13 +1001,13 @@ fn deserialize_map(
let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); let field_attrs = try!(attr::get_struct_field_attrs(cx, fields));
let extract_values: Vec<P<ast::Stmt>> = field_names.iter() let extract_values = field_names.iter()
.zip(field_attrs.iter()) .zip(field_attrs.iter())
.map(|(field_name, field_attr)| { .map(|(field_name, field_attr)| {
let missing_expr = if field_attr.use_default() { let missing_expr = if field_attr.use_default() {
quote_expr!(cx, ::std::default::Default::default()) quote_expr!(cx, ::std::default::Default::default())
} else { } else {
let name = &field_attr.name_expr(); let name = field_attr.name_expr();
quote_expr!(cx, try!(visitor.missing_field($name))) quote_expr!(cx, try!(visitor.missing_field($name)))
}; };
@ -1007,7 +1018,7 @@ fn deserialize_map(
}; };
).unwrap() ).unwrap()
}) })
.collect(); .collect::<Vec<_>>();
let result = builder.expr().struct_path(struct_path) let result = builder.expr().struct_path(struct_path)
.with_id_exprs( .with_id_exprs(

View File

@ -310,7 +310,8 @@ fn serialize_variant(
) -> Result<ast::Arm, Error> { ) -> Result<ast::Arm, Error> {
let type_name = builder.expr().str(type_ident); let type_name = builder.expr().str(type_ident);
let variant_ident = variant.node.name; let variant_ident = variant.node.name;
let variant_name = builder.expr().str(variant_ident); let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
let variant_name = variant_attrs.name_expr();
match variant.node.data { match variant.node.data {
ast::VariantData::Unit(_) => { ast::VariantData::Unit(_) => {

View File

@ -29,6 +29,12 @@ struct Rename {
a2: i32, a2: i32,
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum RenameEnumVariant {
#[serde(rename="bruce_wayne")]
Batman,
}
#[derive(Debug, PartialEq, Deserialize, Serialize)] #[derive(Debug, PartialEq, Deserialize, Serialize)]
struct SkipSerializingFields<A: default::Default> { struct SkipSerializingFields<A: default::Default> {
a: i8, a: i8,
@ -156,6 +162,16 @@ fn test_rename() {
); );
} }
#[test]
fn test_rename_enum_variant() {
assert_tokens(
&RenameEnumVariant::Batman,
vec![
Token::EnumUnit("RenameEnumVariant", "bruce_wayne"),
]
);
}
#[test] #[test]
fn test_skip_serializing_fields() { fn test_skip_serializing_fields() {
assert_ser_tokens( assert_ser_tokens(