diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 74ab447e..984fe623 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -49,6 +49,57 @@ impl ContainerAttrs { } } +/// Represents variant attribute information +#[derive(Debug)] +pub struct VariantAttrs { + ident: ast::Ident, + name: Option, +} + +impl VariantAttrs { + pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result { + 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 { + AstBuilder::new().expr().str(self.ident) + } + + /// Return the field name for the field when serializing. + pub fn name_expr(&self) -> P { + match self.name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } +} + /// Represents field attribute information #[derive(Debug)] pub struct FieldAttrs { @@ -124,22 +175,16 @@ impl FieldAttrs { }) } - pub fn from_variant(variant: &ast::Variant) -> Self { - FieldAttrs { - ident: variant.node.name, - name: None, - skip_serializing_field: false, - skip_serializing_field_if_empty: false, - skip_serializing_field_if_none: false, - use_default: false, - } + /// Return the string expression of the field ident. + pub fn ident_expr(&self) -> P { + AstBuilder::new().expr().str(self.ident) } - /// Return the default field name for the field. + /// Return the field name for the field when serializing. pub fn name_expr(&self) -> P { match self.name { Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), - None => AstBuilder::new().expr().str(self.ident), + None => self.ident_expr(), } } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 14a68bec..66336f80 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -550,9 +550,14 @@ fn deserialize_item_enum( let variant_visitor = deserialize_field_visitor( cx, builder, - enum_def.variants.iter() - .map(|variant| attr::FieldAttrs::from_variant(variant)) - .collect(), + try!( + enum_def.variants.iter() + .map(|variant| { + let attrs = try!(attr::VariantAttrs::from_variant(cx, variant)); + Ok(attrs.name_expr()) + }) + .collect() + ), container_attrs, ); @@ -790,11 +795,11 @@ fn deserialize_struct_variant( fn deserialize_field_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, - field_attrs: Vec, + field_names: Vec>, container_attrs: &attr::ContainerAttrs, ) -> Vec> { // Create the field names for the fields. - let field_idents: Vec = (0 .. field_attrs.len()) + let field_idents: Vec = (0 .. field_names.len()) .map(|i| builder.id(format!("__field{}", i))) .collect(); @@ -832,10 +837,9 @@ fn deserialize_field_visitor( // Match arms to extract a field from a string let default_field_arms: Vec<_> = field_idents.iter() - .zip(field_attrs.iter()) - .map(|(field_ident, field_attrs)| { - let expr = &field_attrs.name_expr(); - quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }) + .zip(field_names.iter()) + .map(|(field_ident, field_name)| { + quote_arm!(cx, $field_name => { Ok(__Field::$field_ident) }) }) .collect(); @@ -916,7 +920,14 @@ fn deserialize_struct_visitor( let field_visitor = deserialize_field_visitor( cx, 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 ); @@ -990,13 +1001,13 @@ fn deserialize_map( let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); - let extract_values: Vec> = field_names.iter() + let extract_values = field_names.iter() .zip(field_attrs.iter()) .map(|(field_name, field_attr)| { let missing_expr = if field_attr.use_default() { quote_expr!(cx, ::std::default::Default::default()) } else { - let name = &field_attr.name_expr(); + let name = field_attr.name_expr(); quote_expr!(cx, try!(visitor.missing_field($name))) }; @@ -1007,7 +1018,7 @@ fn deserialize_map( }; ).unwrap() }) - .collect(); + .collect::>(); let result = builder.expr().struct_path(struct_path) .with_id_exprs( diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 4ebd0902..ad3dc002 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -310,7 +310,8 @@ fn serialize_variant( ) -> Result { let type_name = builder.expr().str(type_ident); 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 { ast::VariantData::Unit(_) => { diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 551e7d5b..f129787c 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -29,6 +29,12 @@ struct Rename { a2: i32, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +enum RenameEnumVariant { + #[serde(rename="bruce_wayne")] + Batman, +} + #[derive(Debug, PartialEq, Deserialize, Serialize)] struct SkipSerializingFields { 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] fn test_skip_serializing_fields() { assert_ser_tokens(