feat(codegen): Allow containers to be renamed

Closes #218
This commit is contained in:
Erick Tryzelaar
2016-02-08 08:00:38 -08:00
parent 5ad784f6c6
commit 0224e212f9
4 changed files with 139 additions and 75 deletions
+40
View File
@@ -11,6 +11,9 @@ use error::Error;
/// Represents container (e.g. struct) attribute information /// Represents container (e.g. struct) attribute information
#[derive(Debug)] #[derive(Debug)]
pub struct ContainerAttrs { pub struct ContainerAttrs {
ident: ast::Ident,
serialize_name: Option<ast::Lit>,
deserialize_name: Option<ast::Lit>,
deny_unknown_fields: bool, deny_unknown_fields: bool,
} }
@@ -18,12 +21,28 @@ impl ContainerAttrs {
/// Extract out the `#[serde(...)]` attributes from an item. /// Extract out the `#[serde(...)]` attributes from an item.
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<ContainerAttrs, Error> { pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<ContainerAttrs, Error> {
let mut container_attrs = ContainerAttrs { let mut container_attrs = ContainerAttrs {
ident: item.ident,
serialize_name: None,
deserialize_name: None,
deny_unknown_fields: false, deny_unknown_fields: false,
}; };
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) { for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items { for meta_item in meta_items {
match meta_item.node { 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)]` // Parse `#[serde(deny_unknown_fields)]`
ast::MetaWord(ref name) if name == &"deny_unknown_fields" => { ast::MetaWord(ref name) if name == &"deny_unknown_fields" => {
container_attrs.deny_unknown_fields = true; container_attrs.deny_unknown_fields = true;
@@ -44,6 +63,27 @@ impl ContainerAttrs {
Ok(container_attrs) Ok(container_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 serialize_name_expr(&self) -> P<ast::Expr> {
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<ast::Expr> {
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 { pub fn deny_unknown_fields(&self) -> bool {
self.deny_unknown_fields self.deny_unknown_fields
} }
+11 -7
View File
@@ -132,8 +132,8 @@ fn deserialize_item_struct(
ast::VariantData::Unit(_) => { ast::VariantData::Unit(_) => {
deserialize_unit_struct( deserialize_unit_struct(
cx, cx,
&builder,
item.ident, item.ident,
container_attrs,
) )
} }
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
@@ -143,6 +143,7 @@ fn deserialize_item_struct(
item.ident, item.ident,
impl_generics, impl_generics,
ty, ty,
container_attrs,
) )
} }
ast::VariantData::Tuple(ref fields, _) => { ast::VariantData::Tuple(ref fields, _) => {
@@ -157,6 +158,7 @@ fn deserialize_item_struct(
impl_generics, impl_generics,
ty, ty,
fields.len(), fields.len(),
container_attrs,
) )
} }
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, _) => {
@@ -261,10 +263,10 @@ fn deserializer_ty_arg(builder: &aster::AstBuilder) -> P<ast::Ty>{
fn deserialize_unit_struct( fn deserialize_unit_struct(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder,
type_ident: Ident, type_ident: Ident,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.deserialize_name_expr();
Ok(quote_expr!(cx, { Ok(quote_expr!(cx, {
struct __Visitor; struct __Visitor;
@@ -298,6 +300,7 @@ fn deserialize_newtype_struct(
type_ident: Ident, type_ident: Ident,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
@@ -315,7 +318,7 @@ fn deserialize_newtype_struct(
1, 1,
); );
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.deserialize_name_expr();
Ok(quote_expr!(cx, { Ok(quote_expr!(cx, {
$visitor_item $visitor_item
@@ -350,6 +353,7 @@ fn deserialize_tuple_struct(
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
fields: usize, fields: usize,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
@@ -367,7 +371,7 @@ fn deserialize_tuple_struct(
fields, fields,
); );
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.deserialize_name_expr();
Ok(quote_expr!(cx, { Ok(quote_expr!(cx, {
$visitor_item $visitor_item
@@ -503,7 +507,7 @@ fn deserialize_struct(
container_attrs container_attrs
)); ));
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.deserialize_name_expr();
Ok(quote_expr!(cx, { Ok(quote_expr!(cx, {
$field_visitor $field_visitor
@@ -545,7 +549,7 @@ fn deserialize_item_enum(
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let where_clause = &impl_generics.where_clause; 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( let variant_visitor = deserialize_field_visitor(
cx, cx,
+21 -22
View File
@@ -84,20 +84,18 @@ fn serialize_body(
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
// Note: While we don't have any container attributes, we still want to try to let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
// 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));
match item.node { match item.node {
ast::ItemStruct(ref variant_data, _) => { ast::ItemStruct(ref variant_data, _) => {
serialize_item_struct( serialize_item_struct(
cx, cx,
builder, builder,
item,
impl_generics, impl_generics,
ty, ty,
item.span, item.span,
variant_data, variant_data,
&container_attrs,
) )
} }
ast::ItemEnum(ref enum_def, _) => { ast::ItemEnum(ref enum_def, _) => {
@@ -108,6 +106,7 @@ fn serialize_body(
impl_generics, impl_generics,
ty, ty,
enum_def, enum_def,
&container_attrs,
) )
} }
_ => { _ => {
@@ -120,25 +119,23 @@ fn serialize_body(
fn serialize_item_struct( fn serialize_item_struct(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
span: Span, span: Span,
variant_data: &ast::VariantData, variant_data: &ast::VariantData,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
match *variant_data { match *variant_data {
ast::VariantData::Unit(_) => { ast::VariantData::Unit(_) => {
serialize_unit_struct( serialize_unit_struct(
cx, cx,
&builder, container_attrs,
item.ident,
) )
} }
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
serialize_newtype_struct( serialize_newtype_struct(
cx, cx,
&builder, container_attrs,
item.ident,
) )
} }
ast::VariantData::Tuple(ref fields, _) => { ast::VariantData::Tuple(ref fields, _) => {
@@ -149,10 +146,10 @@ fn serialize_item_struct(
serialize_tuple_struct( serialize_tuple_struct(
cx, cx,
&builder, &builder,
item.ident,
impl_generics, impl_generics,
ty, ty,
fields.len(), fields.len(),
container_attrs,
) )
} }
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, _) => {
@@ -163,10 +160,10 @@ fn serialize_item_struct(
serialize_struct( serialize_struct(
cx, cx,
&builder, &builder,
item.ident,
impl_generics, impl_generics,
ty, ty,
fields, fields,
container_attrs,
) )
} }
} }
@@ -174,10 +171,9 @@ fn serialize_item_struct(
fn serialize_unit_struct( fn serialize_unit_struct(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, container_attrs: &attr::ContainerAttrs,
type_ident: Ident
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.serialize_name_expr();
Ok(quote_expr!(cx, Ok(quote_expr!(cx,
serializer.serialize_unit_struct($type_name) serializer.serialize_unit_struct($type_name)
@@ -186,10 +182,9 @@ fn serialize_unit_struct(
fn serialize_newtype_struct( fn serialize_newtype_struct(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, container_attrs: &attr::ContainerAttrs,
type_ident: Ident
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.serialize_name_expr();
Ok(quote_expr!(cx, Ok(quote_expr!(cx,
serializer.serialize_newtype_struct($type_name, &self.0) serializer.serialize_newtype_struct($type_name, &self.0)
@@ -199,10 +194,10 @@ fn serialize_newtype_struct(
fn serialize_tuple_struct( fn serialize_tuple_struct(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
type_ident: Ident,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
fields: usize, fields: usize,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
cx, cx,
@@ -216,7 +211,7 @@ fn serialize_tuple_struct(
impl_generics, impl_generics,
); );
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.serialize_name_expr();
Ok(quote_expr!(cx, { Ok(quote_expr!(cx, {
$visitor_struct $visitor_struct
@@ -232,10 +227,10 @@ fn serialize_tuple_struct(
fn serialize_struct( fn serialize_struct(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
type_ident: Ident,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
fields: &[ast::StructField], fields: &[ast::StructField],
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let value_exprs = fields.iter().map(|field| { let value_exprs = fields.iter().map(|field| {
let name = field.node.ident().expect("struct has unnamed field"); let name = field.node.ident().expect("struct has unnamed field");
@@ -255,7 +250,7 @@ fn serialize_struct(
value_exprs, value_exprs,
)); ));
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.serialize_name_expr();
Ok(quote_expr!(cx, { Ok(quote_expr!(cx, {
$visitor_struct $visitor_struct
@@ -275,6 +270,7 @@ fn serialize_item_enum(
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
enum_def: &ast::EnumDef, enum_def: &ast::EnumDef,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let mut arms = vec![]; let mut arms = vec![];
@@ -287,6 +283,7 @@ fn serialize_item_enum(
ty.clone(), ty.clone(),
variant, variant,
variant_index, variant_index,
container_attrs,
)); ));
arms.push(arm); arms.push(arm);
@@ -307,8 +304,10 @@ fn serialize_variant(
ty: P<ast::Ty>, ty: P<ast::Ty>,
variant: &ast::Variant, variant: &ast::Variant,
variant_index: usize, variant_index: usize,
container_attrs: &attr::ContainerAttrs,
) -> Result<ast::Arm, Error> { ) -> Result<ast::Arm, Error> {
let type_name = builder.expr().str(type_ident); let type_name = container_attrs.serialize_name_expr();
let variant_ident = variant.node.name; let variant_ident = variant.node.name;
let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant)); let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
let variant_name = variant_attrs.serialize_name_expr(); let variant_name = variant_attrs.serialize_name_expr();
+67 -46
View File
@@ -23,46 +23,48 @@ struct DisallowUnknown {
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Rename { #[serde(rename="Superhero")]
struct RenameStruct {
a1: i32, a1: i32,
#[serde(rename="a3")] #[serde(rename="a3")]
a2: i32, a2: i32,
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
enum RenameVariantVariant { #[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))]
#[serde(rename="bruce_wayne")] struct RenameStructSerializeDeserialize {
Batman,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct RenameStructFieldSerializeDeserialize {
a1: i32, a1: i32,
#[serde(rename(serialize="a4", deserialize="a5"))] #[serde(rename(serialize="a4", deserialize="a5"))]
a2: i32, a2: i32,
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename="Superhero")]
enum RenameEnum { enum RenameEnum {
#[serde(rename="bruce_wayne")] #[serde(rename="bruce_wayne")]
Batman, 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)] #[derive(Debug, PartialEq, Deserialize, Serialize)]
enum RenameVariantSerializeDeserialize<A> { #[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))]
Map { enum RenameEnumSerializeDeserialize<A> {
#[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))]
Robin {
a: i8, a: i8,
#[serde(rename(serialize="c", deserialize="d"))] #[serde(rename(serialize="c", deserialize="d"))]
b: A, b: A,
}, },
} }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum RenameEnumVariantSerializeDeserialize {
#[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))]
Robin,
}
#[derive(Debug, PartialEq, Deserialize, Serialize)] #[derive(Debug, PartialEq, Deserialize, Serialize)]
struct SkipSerializingFields<A: default::Default> { struct SkipSerializingFields<A: default::Default> {
a: i8, a: i8,
@@ -171,11 +173,11 @@ fn test_ignore_unknown() {
} }
#[test] #[test]
fn test_rename_struct_field() { fn test_rename_struct() {
assert_tokens( assert_tokens(
&Rename { a1: 1, a2: 2 }, &RenameStruct { a1: 1, a2: 2 },
vec![ vec![
Token::StructStart("Rename", Some(2)), Token::StructStart("Superhero", Some(2)),
Token::MapSep, Token::MapSep,
Token::Str("a1"), Token::Str("a1"),
@@ -191,21 +193,11 @@ fn test_rename_struct_field() {
} }
#[test] #[test]
fn test_rename_enum_variant() { fn test_rename_struct_serialize_deserialize() {
assert_tokens(
&RenameVariantVariant::Batman,
vec![
Token::EnumUnit("RenameVariantVariant", "bruce_wayne"),
]
);
}
#[test]
fn test_rename_struct_field_serialize_deserialize() {
assert_ser_tokens( 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::MapSep,
Token::Str("a1"), Token::Str("a1"),
@@ -220,9 +212,9 @@ fn test_rename_struct_field_serialize_deserialize() {
); );
assert_de_tokens( assert_de_tokens(
&RenameStructFieldSerializeDeserialize { a1: 1, a2: 2 }, &RenameStructSerializeDeserialize { a1: 1, a2: 2 },
vec![ vec![
Token::StructStart("RenameStructFieldSerializeDeserialize", Some(2)), Token::StructStart("SuperheroDe", Some(2)),
Token::MapSep, Token::MapSep,
Token::Str("a1"), Token::Str("a1"),
@@ -238,18 +230,47 @@ fn test_rename_struct_field_serialize_deserialize() {
} }
#[test] #[test]
fn test_rename_variant_serialize_deserialize() { fn test_rename_enum() {
assert_ser_tokens( assert_tokens(
&RenameEnumVariantSerializeDeserialize::Robin, &RenameEnum::Batman,
&[ vec![
Token::EnumUnit("RenameEnumVariantSerializeDeserialize", "dick_grayson"), Token::EnumUnit("Superhero", "bruce_wayne"),
] ]
); );
assert_de_tokens( assert_tokens(
&RenameEnumVariantSerializeDeserialize::Robin, &RenameEnum::Superman(0),
vec![ 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] #[test]
fn test_enum_serialize_deserialize() { fn test_enum_serialize_deserialize() {
assert_ser_tokens( assert_ser_tokens(
&RenameVariantSerializeDeserialize::Map { &RenameEnumSerializeDeserialize::Robin {
a: 0, a: 0,
b: String::new(), b: String::new(),
}, },
&[ &[
Token::EnumMapStart("RenameVariantSerializeDeserialize", "Map", Some(2)), Token::EnumMapStart("SuperheroSer", "dick_grayson", Some(2)),
Token::MapSep, Token::MapSep,
Token::Str("a"), Token::Str("a"),
@@ -277,12 +298,12 @@ fn test_enum_serialize_deserialize() {
); );
assert_de_tokens( assert_de_tokens(
&RenameVariantSerializeDeserialize::Map { &RenameEnumSerializeDeserialize::Robin {
a: 0, a: 0,
b: String::new(), b: String::new(),
}, },
vec![ vec![
Token::EnumMapStart("RenameVariantSerializeDeserialize", "Map", Some(2)), Token::EnumMapStart("SuperheroDe", "jason_todd", Some(2)),
Token::MapSep, Token::MapSep,
Token::Str("a"), Token::Str("a"),