mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-06-19 04:11:02 +00:00
feat(codegen): Support renames that are different when serializing and deserializing
According to #61, if one uses serde to serialize requests that pass url-encoded parameters to a server, it might get responses back with a different capitalization scheme. This patch restores the behavior implemented in #62. # Conflicts: # serde_codegen/src/attr.rs # serde_codegen/src/de.rs # serde_tests/tests/test_annotations.rs
This commit is contained in:
+87
-26
@@ -53,14 +53,16 @@ impl ContainerAttrs {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VariantAttrs {
|
pub struct VariantAttrs {
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
name: Option<ast::Lit>,
|
serialize_name: Option<ast::Lit>,
|
||||||
|
deserialize_name: Option<ast::Lit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariantAttrs {
|
impl VariantAttrs {
|
||||||
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
|
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
|
||||||
let mut variant_attrs = VariantAttrs {
|
let mut variant_attrs = VariantAttrs {
|
||||||
ident: variant.node.name,
|
ident: variant.node.name,
|
||||||
name: None,
|
serialize_name: None,
|
||||||
|
deserialize_name: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
|
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
|
||||||
@@ -68,7 +70,15 @@ impl VariantAttrs {
|
|||||||
match meta_item.node {
|
match meta_item.node {
|
||||||
// Parse `#[serde(rename="foo")]`
|
// Parse `#[serde(rename="foo")]`
|
||||||
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
|
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
|
||||||
variant_attrs.name = Some(lit.clone());
|
variant_attrs.serialize_name = Some(lit.clone());
|
||||||
|
variant_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));
|
||||||
|
variant_attrs.serialize_name = ser_name;
|
||||||
|
variant_attrs.deserialize_name = de_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@@ -92,8 +102,16 @@ impl VariantAttrs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the field name for the field when serializing.
|
/// Return the field name for the field when serializing.
|
||||||
pub fn name_expr(&self) -> P<ast::Expr> {
|
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
|
||||||
match self.name {
|
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())),
|
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
|
||||||
None => self.ident_expr(),
|
None => self.ident_expr(),
|
||||||
}
|
}
|
||||||
@@ -104,7 +122,8 @@ impl VariantAttrs {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FieldAttrs {
|
pub struct FieldAttrs {
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
name: Option<ast::Lit>,
|
serialize_name: Option<ast::Lit>,
|
||||||
|
deserialize_name: Option<ast::Lit>,
|
||||||
skip_serializing_field: bool,
|
skip_serializing_field: bool,
|
||||||
skip_serializing_field_if_empty: bool,
|
skip_serializing_field_if_empty: bool,
|
||||||
skip_serializing_field_if_none: bool,
|
skip_serializing_field_if_none: bool,
|
||||||
@@ -119,38 +138,50 @@ impl FieldAttrs {
|
|||||||
None => { cx.span_bug(field.span, "struct field has no name?") }
|
None => { cx.span_bug(field.span, "struct field has no name?") }
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut skip_serializing_field = false;
|
let mut field_attrs = FieldAttrs {
|
||||||
let mut skip_serializing_field_if_empty = false;
|
ident: field_ident,
|
||||||
let mut skip_serializing_field_if_none = false;
|
serialize_name: None,
|
||||||
let mut field_name = None;
|
deserialize_name: None,
|
||||||
let mut use_default = false;
|
skip_serializing_field: false,
|
||||||
|
skip_serializing_field_if_empty: false,
|
||||||
|
skip_serializing_field_if_none: false,
|
||||||
|
use_default: false,
|
||||||
|
};
|
||||||
|
|
||||||
for meta_items in field.node.attrs.iter().filter_map(get_serde_meta_items) {
|
for meta_items in field.node.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")]`
|
// Parse `#[serde(rename="foo")]`
|
||||||
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
|
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
|
||||||
field_name = Some(lit.clone());
|
field_attrs.serialize_name = Some(lit.clone());
|
||||||
|
field_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));
|
||||||
|
field_attrs.serialize_name = ser_name;
|
||||||
|
field_attrs.deserialize_name = de_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(default)]`
|
// Parse `#[serde(default)]`
|
||||||
ast::MetaWord(ref name) if name == &"default" => {
|
ast::MetaWord(ref name) if name == &"default" => {
|
||||||
use_default = true;
|
field_attrs.use_default = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(skip_serializing)]`
|
// Parse `#[serde(skip_serializing)]`
|
||||||
ast::MetaWord(ref name) if name == &"skip_serializing" => {
|
ast::MetaWord(ref name) if name == &"skip_serializing" => {
|
||||||
skip_serializing_field = true;
|
field_attrs.skip_serializing_field = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(skip_serializing_if_none)]`
|
// Parse `#[serde(skip_serializing_if_none)]`
|
||||||
ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => {
|
ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => {
|
||||||
skip_serializing_field_if_none = true;
|
field_attrs.skip_serializing_field_if_none = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(skip_serializing_if_empty)]`
|
// Parse `#[serde(skip_serializing_if_empty)]`
|
||||||
ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => {
|
ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => {
|
||||||
skip_serializing_field_if_empty = true;
|
field_attrs.skip_serializing_field_if_empty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@@ -165,14 +196,7 @@ impl FieldAttrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FieldAttrs {
|
Ok(field_attrs)
|
||||||
ident: field_ident,
|
|
||||||
name: field_name,
|
|
||||||
skip_serializing_field: skip_serializing_field,
|
|
||||||
skip_serializing_field_if_empty: skip_serializing_field_if_empty,
|
|
||||||
skip_serializing_field_if_none: skip_serializing_field_if_none,
|
|
||||||
use_default: use_default,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the string expression of the field ident.
|
/// Return the string expression of the field ident.
|
||||||
@@ -181,8 +205,16 @@ impl FieldAttrs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the field name for the field when serializing.
|
/// Return the field name for the field when serializing.
|
||||||
pub fn name_expr(&self) -> P<ast::Expr> {
|
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
|
||||||
match self.name {
|
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 deserializing.
|
||||||
|
pub fn deserialize_name_expr(&self) -> P<ast::Expr> {
|
||||||
|
match self.deserialize_name {
|
||||||
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
|
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
|
||||||
None => self.ident_expr(),
|
None => self.ident_expr(),
|
||||||
}
|
}
|
||||||
@@ -215,6 +247,35 @@ pub fn get_struct_field_attrs(cx: &ExtCtxt,
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_renames(cx: &ExtCtxt,
|
||||||
|
items: &[P<ast::MetaItem>]) -> Result<(Option<ast::Lit>, Option<ast::Lit>), Error> {
|
||||||
|
let mut ser_name = None;
|
||||||
|
let mut de_name = None;
|
||||||
|
|
||||||
|
for item in items {
|
||||||
|
match item.node {
|
||||||
|
ast::MetaNameValue(ref name, ref lit) if name == &"serialize" => {
|
||||||
|
ser_name = Some(lit.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::MetaNameValue(ref name, ref lit) if name == &"deserialize" => {
|
||||||
|
de_name = Some(lit.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
cx.span_err(
|
||||||
|
item.span,
|
||||||
|
&format!("unknown rename attribute `{}`",
|
||||||
|
meta_item_to_string(item)));
|
||||||
|
|
||||||
|
return Err(Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((ser_name, de_name))
|
||||||
|
}
|
||||||
|
|
||||||
fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
||||||
match attr.node.value.node {
|
match attr.node.value.node {
|
||||||
ast::MetaList(ref name, ref items) if name == &"serde" => {
|
ast::MetaList(ref name, ref items) if name == &"serde" => {
|
||||||
|
|||||||
@@ -554,7 +554,7 @@ fn deserialize_item_enum(
|
|||||||
enum_def.variants.iter()
|
enum_def.variants.iter()
|
||||||
.map(|variant| {
|
.map(|variant| {
|
||||||
let attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
|
let attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
|
||||||
Ok(attrs.name_expr())
|
Ok(attrs.deserialize_name_expr())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
),
|
),
|
||||||
@@ -924,7 +924,7 @@ fn deserialize_struct_visitor(
|
|||||||
fields.iter()
|
fields.iter()
|
||||||
.map(|field| {
|
.map(|field| {
|
||||||
let attrs = try!(attr::FieldAttrs::from_field(cx, field));
|
let attrs = try!(attr::FieldAttrs::from_field(cx, field));
|
||||||
Ok(attrs.name_expr())
|
Ok(attrs.deserialize_name_expr())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
),
|
),
|
||||||
@@ -1007,7 +1007,7 @@ fn deserialize_map(
|
|||||||
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.ident_expr();
|
||||||
quote_expr!(cx, try!(visitor.missing_field($name)))
|
quote_expr!(cx, try!(visitor.missing_field($name)))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ fn serialize_variant(
|
|||||||
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_attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
|
let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
|
||||||
let variant_name = variant_attrs.name_expr();
|
let variant_name = variant_attrs.serialize_name_expr();
|
||||||
|
|
||||||
match variant.node.data {
|
match variant.node.data {
|
||||||
ast::VariantData::Unit(_) => {
|
ast::VariantData::Unit(_) => {
|
||||||
@@ -613,7 +613,7 @@ fn serialize_struct_visitor<I>(
|
|||||||
.filter(|&(ref field, _)| !field.skip_serializing_field())
|
.filter(|&(ref field, _)| !field.skip_serializing_field())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, (ref field, value_expr))| {
|
.map(|(i, (ref field, value_expr))| {
|
||||||
let key_expr = field.name_expr();
|
let key_expr = field.serialize_name_expr();
|
||||||
|
|
||||||
let stmt = if field.skip_serializing_field_if_empty() {
|
let stmt = if field.skip_serializing_field_if_empty() {
|
||||||
quote_stmt!(cx, if ($value_expr).is_empty() { continue; })
|
quote_stmt!(cx, if ($value_expr).is_empty() { continue; })
|
||||||
|
|||||||
@@ -30,11 +30,39 @@ struct Rename {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
enum RenameEnumVariant {
|
enum RenameVariantVariant {
|
||||||
#[serde(rename="bruce_wayne")]
|
#[serde(rename="bruce_wayne")]
|
||||||
Batman,
|
Batman,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
struct RenameStructSerializeDeserialize {
|
||||||
|
a1: i32,
|
||||||
|
#[serde(rename(serialize="a4", deserialize="a5"))]
|
||||||
|
a2: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
enum RenameEnum {
|
||||||
|
#[serde(rename="bruce_wayne")]
|
||||||
|
Batman,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
|
enum RenameVariantSerializeDeserialize<A> {
|
||||||
|
Map {
|
||||||
|
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)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
struct SkipSerializingFields<A: default::Default> {
|
struct SkipSerializingFields<A: default::Default> {
|
||||||
a: i8,
|
a: i8,
|
||||||
@@ -165,9 +193,106 @@ fn test_rename() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_rename_enum_variant() {
|
fn test_rename_enum_variant() {
|
||||||
assert_tokens(
|
assert_tokens(
|
||||||
&RenameEnumVariant::Batman,
|
&RenameVariantVariant::Batman,
|
||||||
vec![
|
vec![
|
||||||
Token::EnumUnit("RenameEnumVariant", "bruce_wayne"),
|
Token::EnumUnit("RenameVariantVariant", "bruce_wayne"),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_struct_serialize_deserialize() {
|
||||||
|
assert_ser_tokens(
|
||||||
|
&RenameStructSerializeDeserialize { a1: 1, a2: 2 },
|
||||||
|
&[
|
||||||
|
Token::StructStart("RenameStructSerializeDeserialize", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a1"),
|
||||||
|
Token::I32(1),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a4"),
|
||||||
|
Token::I32(2),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&RenameStructSerializeDeserialize { a1: 1, a2: 2 },
|
||||||
|
vec![
|
||||||
|
Token::StructStart("RenameStructSerializeDeserialize", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a1"),
|
||||||
|
Token::I32(1),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a5"),
|
||||||
|
Token::I32(2),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_variant_serialize_deserialize() {
|
||||||
|
assert_ser_tokens(
|
||||||
|
&RenameEnumVariantSerializeDeserialize::Robin,
|
||||||
|
&[
|
||||||
|
Token::EnumUnit("RenameEnumVariantSerializeDeserialize", "dick_grayson"),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&RenameEnumVariantSerializeDeserialize::Robin,
|
||||||
|
vec![
|
||||||
|
Token::EnumUnit("RenameEnumVariantSerializeDeserialize", "jason_todd"),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum_serialize_deserialize() {
|
||||||
|
assert_ser_tokens(
|
||||||
|
&RenameVariantSerializeDeserialize::Map {
|
||||||
|
a: 0,
|
||||||
|
b: String::new(),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::EnumMapStart("RenameVariantSerializeDeserialize", "Map", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(0),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("c"),
|
||||||
|
Token::Str(""),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&RenameVariantSerializeDeserialize::Map {
|
||||||
|
a: 0,
|
||||||
|
b: String::new(),
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
Token::EnumMapStart("RenameVariantSerializeDeserialize", "Map", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(0),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("d"),
|
||||||
|
Token::Str(""),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user