parent
5ad784f6c6
commit
0224e212f9
@ -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<ast::Lit>,
|
||||
deserialize_name: Option<ast::Lit>,
|
||||
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<ContainerAttrs, Error> {
|
||||
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<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 {
|
||||
self.deny_unknown_fields
|
||||
}
|
||||
|
@ -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<ast::Ty>{
|
||||
|
||||
fn deserialize_unit_struct(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
type_ident: Ident,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> 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, {
|
||||
struct __Visitor;
|
||||
@ -298,6 +300,7 @@ fn deserialize_newtype_struct(
|
||||
type_ident: Ident,
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> Result<P<ast::Expr>, 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<ast::Ty>,
|
||||
fields: usize,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> Result<P<ast::Expr>, 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<P<ast::Expr>, 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,
|
||||
|
@ -84,20 +84,18 @@ fn serialize_body(
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
) -> Result<P<ast::Expr>, 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<ast::Ty>,
|
||||
span: Span,
|
||||
variant_data: &ast::VariantData,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> Result<P<ast::Expr>, 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<P<ast::Expr>, 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<P<ast::Expr>, 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<ast::Ty>,
|
||||
fields: usize,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> Result<P<ast::Expr>, 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<ast::Ty>,
|
||||
fields: &[ast::StructField],
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> Result<P<ast::Expr>, 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<ast::Ty>,
|
||||
enum_def: &ast::EnumDef,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> Result<P<ast::Expr>, 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<ast::Ty>,
|
||||
variant: &ast::Variant,
|
||||
variant_index: usize,
|
||||
container_attrs: &attr::ContainerAttrs,
|
||||
) -> 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_attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
|
||||
let variant_name = variant_attrs.serialize_name_expr();
|
||||
|
@ -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<A> {
|
||||
Map {
|
||||
#[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))]
|
||||
enum RenameEnumSerializeDeserialize<A> {
|
||||
#[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: default::Default> {
|
||||
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"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user