diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 387650c8..9c036284 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -11,6 +11,9 @@ use error::Error; /// Represents container (e.g. struct) attribute information #[derive(Debug)] pub struct ContainerAttrs { + ident: ast::Ident, + serialize_name: Option, + deserialize_name: Option, deny_unknown_fields: bool, } @@ -18,12 +21,28 @@ impl ContainerAttrs { /// Extract out the `#[serde(...)]` attributes from an item. pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result { let mut container_attrs = ContainerAttrs { + ident: item.ident, + serialize_name: None, + deserialize_name: None, deny_unknown_fields: false, }; for meta_items in item.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" => { + container_attrs.serialize_name = Some(lit.clone()); + container_attrs.deserialize_name = Some(lit.clone()); + } + + // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` + ast::MetaList(ref name, ref meta_items) if name == &"rename" => { + let (ser_name, de_name) = try!(get_renames(cx, meta_items)); + container_attrs.serialize_name = ser_name; + container_attrs.deserialize_name = de_name; + } + // Parse `#[serde(deny_unknown_fields)]` ast::MetaWord(ref name) if name == &"deny_unknown_fields" => { container_attrs.deny_unknown_fields = true; @@ -44,6 +63,27 @@ impl ContainerAttrs { Ok(container_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 serialize_name_expr(&self) -> P { + match self.serialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } + + /// Return the field name for the field when serializing. + pub fn deserialize_name_expr(&self) -> P { + match self.deserialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } + pub fn deny_unknown_fields(&self) -> bool { self.deny_unknown_fields } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 0e924fa5..ea5ed9b6 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -132,8 +132,8 @@ fn deserialize_item_struct( ast::VariantData::Unit(_) => { deserialize_unit_struct( cx, - &builder, item.ident, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { @@ -143,6 +143,7 @@ fn deserialize_item_struct( item.ident, impl_generics, ty, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) => { @@ -157,6 +158,7 @@ fn deserialize_item_struct( impl_generics, ty, fields.len(), + container_attrs, ) } ast::VariantData::Struct(ref fields, _) => { @@ -261,10 +263,10 @@ fn deserializer_ty_arg(builder: &aster::AstBuilder) -> P{ fn deserialize_unit_struct( cx: &ExtCtxt, - builder: &aster::AstBuilder, type_ident: Ident, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { struct __Visitor; @@ -298,6 +300,7 @@ fn deserialize_newtype_struct( type_ident: Ident, impl_generics: &ast::Generics, ty: P, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &impl_generics.where_clause; @@ -315,7 +318,7 @@ fn deserialize_newtype_struct( 1, ); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { $visitor_item @@ -350,6 +353,7 @@ fn deserialize_tuple_struct( impl_generics: &ast::Generics, ty: P, fields: usize, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &impl_generics.where_clause; @@ -367,7 +371,7 @@ fn deserialize_tuple_struct( fields, ); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { $visitor_item @@ -503,7 +507,7 @@ fn deserialize_struct( container_attrs )); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { $field_visitor @@ -545,7 +549,7 @@ fn deserialize_item_enum( ) -> Result, Error> { let where_clause = &impl_generics.where_clause; - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); let variant_visitor = deserialize_field_visitor( cx, diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 07ac9174..1c1be6a2 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -84,20 +84,18 @@ fn serialize_body( impl_generics: &ast::Generics, ty: P, ) -> Result, Error> { - // Note: While we don't have any container attributes, we still want to try to - // parse them so we can report a proper error if we get passed an unknown attribute. - let _container_attrs = try!(attr::ContainerAttrs::from_item(cx, item)); + let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item)); match item.node { ast::ItemStruct(ref variant_data, _) => { serialize_item_struct( cx, builder, - item, impl_generics, ty, item.span, variant_data, + &container_attrs, ) } ast::ItemEnum(ref enum_def, _) => { @@ -108,6 +106,7 @@ fn serialize_body( impl_generics, ty, enum_def, + &container_attrs, ) } _ => { @@ -120,25 +119,23 @@ fn serialize_body( fn serialize_item_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, - item: &Item, impl_generics: &ast::Generics, ty: P, span: Span, variant_data: &ast::VariantData, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { match *variant_data { ast::VariantData::Unit(_) => { serialize_unit_struct( cx, - &builder, - item.ident, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { serialize_newtype_struct( cx, - &builder, - item.ident, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) => { @@ -149,10 +146,10 @@ fn serialize_item_struct( serialize_tuple_struct( cx, &builder, - item.ident, impl_generics, ty, fields.len(), + container_attrs, ) } ast::VariantData::Struct(ref fields, _) => { @@ -163,10 +160,10 @@ fn serialize_item_struct( serialize_struct( cx, &builder, - item.ident, impl_generics, ty, fields, + container_attrs, ) } } @@ -174,10 +171,9 @@ fn serialize_item_struct( fn serialize_unit_struct( cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, serializer.serialize_unit_struct($type_name) @@ -186,10 +182,9 @@ fn serialize_unit_struct( fn serialize_newtype_struct( cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, serializer.serialize_newtype_struct($type_name, &self.0) @@ -199,10 +194,10 @@ fn serialize_newtype_struct( fn serialize_tuple_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, - type_ident: Ident, impl_generics: &ast::Generics, ty: P, fields: usize, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( cx, @@ -216,7 +211,7 @@ fn serialize_tuple_struct( impl_generics, ); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, { $visitor_struct @@ -232,10 +227,10 @@ fn serialize_tuple_struct( fn serialize_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, - type_ident: Ident, impl_generics: &ast::Generics, ty: P, fields: &[ast::StructField], + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let value_exprs = fields.iter().map(|field| { let name = field.node.ident().expect("struct has unnamed field"); @@ -255,7 +250,7 @@ fn serialize_struct( value_exprs, )); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, { $visitor_struct @@ -275,6 +270,7 @@ fn serialize_item_enum( impl_generics: &ast::Generics, ty: P, enum_def: &ast::EnumDef, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let mut arms = vec![]; @@ -287,6 +283,7 @@ fn serialize_item_enum( ty.clone(), variant, variant_index, + container_attrs, )); arms.push(arm); @@ -307,8 +304,10 @@ fn serialize_variant( ty: P, variant: &ast::Variant, variant_index: usize, + container_attrs: &attr::ContainerAttrs, ) -> Result { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); + let variant_ident = variant.node.name; let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant)); let variant_name = variant_attrs.serialize_name_expr(); diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 9485b5f2..82f1265e 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -23,46 +23,48 @@ struct DisallowUnknown { } #[derive(Debug, PartialEq, Serialize, Deserialize)] -struct Rename { +#[serde(rename="Superhero")] +struct RenameStruct { a1: i32, #[serde(rename="a3")] a2: i32, } #[derive(Debug, PartialEq, Serialize, Deserialize)] -enum RenameVariantVariant { - #[serde(rename="bruce_wayne")] - Batman, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -struct RenameStructFieldSerializeDeserialize { +#[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))] +struct RenameStructSerializeDeserialize { a1: i32, #[serde(rename(serialize="a4", deserialize="a5"))] a2: i32, } #[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename="Superhero")] enum RenameEnum { #[serde(rename="bruce_wayne")] Batman, + #[serde(rename="clark_kent")] + Superman(i8), + #[serde(rename="diana_prince")] + WonderWoman(i8, i8), + #[serde(rename="barry_allan")] + Flash { + #[serde(rename="b")] + a: i32, + }, } #[derive(Debug, PartialEq, Deserialize, Serialize)] -enum RenameVariantSerializeDeserialize { - Map { +#[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))] +enum RenameEnumSerializeDeserialize { + #[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))] + Robin { a: i8, #[serde(rename(serialize="c", deserialize="d"))] b: A, }, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -enum RenameEnumVariantSerializeDeserialize { - #[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))] - Robin, -} - #[derive(Debug, PartialEq, Deserialize, Serialize)] struct SkipSerializingFields { a: i8, @@ -171,11 +173,11 @@ fn test_ignore_unknown() { } #[test] -fn test_rename_struct_field() { +fn test_rename_struct() { assert_tokens( - &Rename { a1: 1, a2: 2 }, + &RenameStruct { a1: 1, a2: 2 }, vec![ - Token::StructStart("Rename", Some(2)), + Token::StructStart("Superhero", Some(2)), Token::MapSep, Token::Str("a1"), @@ -191,21 +193,11 @@ fn test_rename_struct_field() { } #[test] -fn test_rename_enum_variant() { - assert_tokens( - &RenameVariantVariant::Batman, - vec![ - Token::EnumUnit("RenameVariantVariant", "bruce_wayne"), - ] - ); -} - -#[test] -fn test_rename_struct_field_serialize_deserialize() { +fn test_rename_struct_serialize_deserialize() { assert_ser_tokens( - &RenameStructFieldSerializeDeserialize { a1: 1, a2: 2 }, + &RenameStructSerializeDeserialize { a1: 1, a2: 2 }, &[ - Token::StructStart("RenameStructFieldSerializeDeserialize", Some(2)), + Token::StructStart("SuperheroSer", Some(2)), Token::MapSep, Token::Str("a1"), @@ -220,9 +212,9 @@ fn test_rename_struct_field_serialize_deserialize() { ); assert_de_tokens( - &RenameStructFieldSerializeDeserialize { a1: 1, a2: 2 }, + &RenameStructSerializeDeserialize { a1: 1, a2: 2 }, vec![ - Token::StructStart("RenameStructFieldSerializeDeserialize", Some(2)), + Token::StructStart("SuperheroDe", Some(2)), Token::MapSep, Token::Str("a1"), @@ -238,18 +230,47 @@ fn test_rename_struct_field_serialize_deserialize() { } #[test] -fn test_rename_variant_serialize_deserialize() { - assert_ser_tokens( - &RenameEnumVariantSerializeDeserialize::Robin, - &[ - Token::EnumUnit("RenameEnumVariantSerializeDeserialize", "dick_grayson"), +fn test_rename_enum() { + assert_tokens( + &RenameEnum::Batman, + vec![ + Token::EnumUnit("Superhero", "bruce_wayne"), ] ); - assert_de_tokens( - &RenameEnumVariantSerializeDeserialize::Robin, + assert_tokens( + &RenameEnum::Superman(0), vec![ - Token::EnumUnit("RenameEnumVariantSerializeDeserialize", "jason_todd"), + Token::EnumNewtype("Superhero", "clark_kent"), + Token::I8(0), + ] + ); + + assert_tokens( + &RenameEnum::WonderWoman(0, 1), + vec![ + Token::EnumSeqStart("Superhero", "diana_prince", Some(2)), + + Token::SeqSep, + Token::I8(0), + + Token::SeqSep, + Token::I8(1), + + Token::SeqEnd, + ] + ); + + assert_tokens( + &RenameEnum::Flash { a: 1 }, + vec![ + Token::EnumMapStart("Superhero", "barry_allan", Some(1)), + + Token::MapSep, + Token::Str("b"), + Token::I32(1), + + Token::MapEnd, ] ); } @@ -257,12 +278,12 @@ fn test_rename_variant_serialize_deserialize() { #[test] fn test_enum_serialize_deserialize() { assert_ser_tokens( - &RenameVariantSerializeDeserialize::Map { + &RenameEnumSerializeDeserialize::Robin { a: 0, b: String::new(), }, &[ - Token::EnumMapStart("RenameVariantSerializeDeserialize", "Map", Some(2)), + Token::EnumMapStart("SuperheroSer", "dick_grayson", Some(2)), Token::MapSep, Token::Str("a"), @@ -277,12 +298,12 @@ fn test_enum_serialize_deserialize() { ); assert_de_tokens( - &RenameVariantSerializeDeserialize::Map { + &RenameEnumSerializeDeserialize::Robin { a: 0, b: String::new(), }, vec![ - Token::EnumMapStart("RenameVariantSerializeDeserialize", "Map", Some(2)), + Token::EnumMapStart("SuperheroDe", "jason_todd", Some(2)), Token::MapSep, Token::Str("a"),