From 8ea6c66cf74326a501b690846e7f63b09f7fb13b Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 15 Feb 2016 16:02:09 -0800 Subject: [PATCH 1/9] refactor(tests): Rename and move some structs and enums --- serde_tests/tests/test_annotations.rs | 358 ++++++++++---------------- 1 file changed, 132 insertions(+), 226 deletions(-) diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 82f1265e..c5ff6e38 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -1,5 +1,3 @@ -use std::default; - use token::{ Error, Token, @@ -16,76 +14,6 @@ struct Default { a2: i32, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -struct DisallowUnknown { - a1: i32, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename="Superhero")] -struct RenameStruct { - a1: i32, - #[serde(rename="a3")] - a2: i32, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[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)] -#[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, Deserialize, Serialize)] -struct SkipSerializingFields { - a: i8, - #[serde(skip_serializing, default)] - b: A, -} - -#[derive(Debug, PartialEq, Deserialize, Serialize)] -struct SkipSerializingIfEmptyFields { - a: i8, - #[serde(skip_serializing_if_empty, default)] - b: Vec, -} - -#[derive(Debug, PartialEq, Deserialize, Serialize)] -struct SkipSerializingIfNoneFields { - a: i8, - #[serde(skip_serializing_if_none, default)] - b: Option, -} - #[test] fn test_default() { assert_de_tokens( @@ -119,6 +47,12 @@ fn test_default() { ); } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +struct DenyUnknown { + a1: i32, +} + #[test] fn test_ignore_unknown() { // 'Default' allows unknown. Basic smoke test of ignore... @@ -154,9 +88,9 @@ fn test_ignore_unknown() { ] ); - assert_de_tokens_error::( + assert_de_tokens_error::( vec![ - Token::StructStart("DisallowUnknown", Some(2)), + Token::StructStart("DenyUnknown", Some(2)), Token::MapSep, Token::Str("a1"), @@ -172,6 +106,22 @@ fn test_ignore_unknown() { ); } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename="Superhero")] +struct RenameStruct { + a1: i32, + #[serde(rename="a3")] + a2: i32, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))] +struct RenameStructSerializeDeserialize { + a1: i32, + #[serde(rename(serialize="a4", deserialize="a5"))] + a2: i32, +} + #[test] fn test_rename_struct() { assert_tokens( @@ -190,10 +140,7 @@ fn test_rename_struct() { Token::MapEnd, ] ); -} -#[test] -fn test_rename_struct_serialize_deserialize() { assert_ser_tokens( &RenameStructSerializeDeserialize { a1: 1, a2: 2 }, &[ @@ -229,6 +176,33 @@ fn test_rename_struct_serialize_deserialize() { ); } +#[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)] +#[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, + }, +} + #[test] fn test_rename_enum() { assert_tokens( @@ -273,10 +247,7 @@ fn test_rename_enum() { Token::MapEnd, ] ); -} -#[test] -fn test_enum_serialize_deserialize() { assert_ser_tokens( &RenameEnumSerializeDeserialize::Robin { a: 0, @@ -318,194 +289,129 @@ fn test_enum_serialize_deserialize() { ); } +#[derive(Debug, PartialEq, Serialize)] +struct SkipSerializingStruct<'a, B, D, E> { + a: &'a i8, + #[serde(skip_serializing)] + b: B, + #[serde(skip_serializing_if_none)] + d: Option, + #[serde(skip_serializing_if_empty)] + e: Vec, +} + #[test] -fn test_skip_serializing_fields() { +fn test_skip_serializing_struct() { + let a = 1; assert_ser_tokens( - &SkipSerializingFields { - a: 1, + &SkipSerializingStruct { + a: &a, b: 2, + d: Some(4), + e: vec![5], }, &[ - Token::StructStart("SkipSerializingFields", Some(1)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapEnd, - ] - ); - - assert_de_tokens( - &SkipSerializingFields { - a: 1, - b: 0, - }, - vec![ - Token::StructStart("SkipSerializingFields", Some(1)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapEnd, - ] - ); -} - -#[test] -fn test_skip_serializing_fields_if_empty() { - assert_ser_tokens( - &SkipSerializingIfEmptyFields:: { - a: 1, - b: vec![], - }, - &[ - Token::StructStart("SkipSerializingIfEmptyFields", Some(1)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapEnd, - ] - ); - - assert_de_tokens( - &SkipSerializingIfEmptyFields:: { - a: 1, - b: vec![], - }, - vec![ - Token::StructStart("SkipSerializingIfEmptyFields", Some(1)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapEnd, - ] - ); - - assert_ser_tokens( - &SkipSerializingIfEmptyFields { - a: 1, - b: vec![2], - }, - &[ - Token::StructStart("SkipSerializingIfEmptyFields", Some(2)), + Token::StructStart("SkipSerializingStruct", Some(3)), Token::MapSep, Token::Str("a"), Token::I8(1), Token::MapSep, - Token::Str("b"), + Token::Str("d"), + Token::Option(true), + Token::I32(4), + + Token::MapSep, + Token::Str("e"), Token::SeqStart(Some(1)), Token::SeqSep, - Token::I32(2), + Token::I32(5), Token::SeqEnd, Token::MapEnd, ] ); - assert_de_tokens( - &SkipSerializingIfEmptyFields { - a: 1, - b: vec![2], + assert_ser_tokens( + &SkipSerializingStruct { + a: &a, + b: 2, + d: None::, + e: Vec::::new(), }, - vec![ - Token::StructStart("SkipSerializingIfEmptyFields", Some(2)), + &[ + Token::StructStart("SkipSerializingStruct", Some(1)), + + Token::MapSep, + Token::Str("a"), + Token::I8(1), + + Token::MapEnd, + ] + ); +} + +#[derive(Debug, PartialEq, Serialize)] +enum SkipSerializingEnum<'a, B, D, E> { + Struct { + a: &'a i8, + #[serde(skip_serializing)] + _b: B, + #[serde(skip_serializing_if_none)] + d: Option, + #[serde(skip_serializing_if_empty)] + e: Vec, + } +} + +#[test] +fn test_skip_serializing_enum() { + let a = 1; + assert_ser_tokens( + &SkipSerializingEnum::Struct { + a: &a, + _b: 2, + d: Some(4), + e: vec![5], + }, + &[ + Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(3)), Token::MapSep, Token::Str("a"), Token::I8(1), Token::MapSep, - Token::Str("b"), + Token::Str("d"), + Token::Option(true), + Token::I32(4), + + Token::MapSep, + Token::Str("e"), Token::SeqStart(Some(1)), Token::SeqSep, - Token::I32(2), + Token::I32(5), Token::SeqEnd, Token::MapEnd, ] ); -} - -#[test] -fn test_skip_serializing_fields_if_none() { - assert_ser_tokens( - &SkipSerializingIfNoneFields:: { - a: 1, - b: None, - }, - &[ - Token::StructStart("SkipSerializingIfNoneFields", Some(1)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapEnd, - ] - ); - - assert_de_tokens( - &SkipSerializingIfNoneFields:: { - a: 1, - b: None, - }, - vec![ - Token::StructStart("SkipSerializingIfNoneFields", Some(1)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapEnd, - ] - ); assert_ser_tokens( - &SkipSerializingIfNoneFields { - a: 1, - b: Some(2), + &SkipSerializingEnum::Struct { + a: &a, + _b: 2, + d: None::, + e: Vec::::new(), }, &[ - Token::StructStart("SkipSerializingIfNoneFields", Some(2)), + Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(1)), Token::MapSep, Token::Str("a"), Token::I8(1), - Token::MapSep, - Token::Str("b"), - Token::Option(true), - Token::I32(2), - - Token::MapEnd, - ] - ); - - assert_de_tokens( - &SkipSerializingIfNoneFields { - a: 1, - b: Some(2), - }, - vec![ - Token::StructStart("SkipSerializingIfNoneFields", Some(2)), - - Token::MapSep, - Token::Str("a"), - Token::I8(1), - - Token::MapSep, - Token::Str("b"), - Token::Option(true), - Token::I32(2), - Token::MapEnd, ] ); From 9812a4c9c602220fa6dc84e37f67203e4d5d05f6 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 12 Feb 2016 21:53:35 -0800 Subject: [PATCH 2/9] feat(codegen): Allow `#[serde(default="123")]` This feature adds support for the default to be specified to be some expression (which unfortunately needs to be parsed from a string) without needing this value to have an implementation of `Default`, or for a new-type wrapper in order to provide an alternative implementation. This expression is run in a function, and therefore has no access to any of the internal state of the deserializer. --- serde_codegen/src/attr.rs | 92 ++++++++++++++++++++++++--- serde_codegen/src/de.rs | 45 ++++++------- serde_codegen/src/ser.rs | 2 +- serde_tests/tests/test_annotations.rs | 86 ++++++++++++++++++++++--- 4 files changed, 184 insertions(+), 41 deletions(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 376d3444..7c69cdf1 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -1,7 +1,8 @@ use syntax::ast; use syntax::attr; use syntax::ext::base::ExtCtxt; -use syntax::print::pprust::meta_item_to_string; +use syntax::print::pprust::{lit_to_string, meta_item_to_string}; +use syntax::parse; use syntax::ptr::P; use aster::AstBuilder; @@ -167,12 +168,16 @@ pub struct FieldAttrs { skip_serializing_field: bool, skip_serializing_field_if_empty: bool, skip_serializing_field_if_none: bool, - use_default: bool, + default_expr_if_missing: Option>, } impl FieldAttrs { /// Extract out the `#[serde(...)]` attributes from a struct field. - pub fn from_field(cx: &ExtCtxt, field: &ast::StructField) -> Result { + pub fn from_field(cx: &ExtCtxt, + generics: &ast::Generics, + field: &ast::StructField) -> Result { + let builder = AstBuilder::new(); + let field_ident = match field.node.ident() { Some(ident) => ident, None => { cx.span_bug(field.span, "struct field has no name?") } @@ -185,7 +190,7 @@ impl FieldAttrs { skip_serializing_field: false, skip_serializing_field_if_empty: false, skip_serializing_field_if_none: false, - use_default: false, + default_expr_if_missing: None, }; for meta_items in field.node.attrs.iter().filter_map(get_serde_meta_items) { @@ -206,7 +211,20 @@ impl FieldAttrs { // Parse `#[serde(default)]` ast::MetaItemKind::Word(ref name) if name == &"default" => { - field_attrs.use_default = true; + let default_expr = builder.expr().default(); + field_attrs.default_expr_if_missing = Some(default_expr); + } + + // Parse `#[serde(default="...")]` + ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { + let wrapped_expr = wrap_default( + cx, + &field.node.ty, + generics, + try!(parse_lit_into_expr(cx, name, lit)), + ); + + field_attrs.default_expr_if_missing = Some(wrapped_expr); } // Parse `#[serde(skip_serializing)]` @@ -261,8 +279,18 @@ impl FieldAttrs { } /// Predicate for using a field's default value - pub fn use_default(&self) -> bool { - self.use_default + pub fn expr_is_missing(&self) -> P { + match self.default_expr_if_missing { + Some(ref expr) => expr.clone(), + None => { + let name = self.ident_expr(); + AstBuilder::new().expr() + .try() + .method_call("missing_field").id("visitor") + .with_arg(name) + .build() + } + } } /// Predicate for ignoring a field when serializing a value @@ -281,9 +309,10 @@ impl FieldAttrs { /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn get_struct_field_attrs(cx: &ExtCtxt, + generics: &ast::Generics, fields: &[ast::StructField]) -> Result, Error> { fields.iter() - .map(|field| FieldAttrs::from_field(cx, field)) + .map(|field| FieldAttrs::from_field(cx, generics, field)) .collect() } @@ -325,3 +354,50 @@ fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P]> { _ => None } } + +fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result, Error> { + let s: &str = match lit.node { + ast::LitKind::Str(ref s, ast::StrStyle::Cooked) => &s, + _ => { + cx.span_err( + lit.span, + &format!("{} literal `{}` must be a string", + name, + lit_to_string(lit))); + + return Err(Error); + } + }; + + let expr = parse::parse_expr_from_source_str("".to_string(), + s.to_owned(), + cx.cfg(), + cx.parse_sess()); + + Ok(expr) +} + +/// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it +/// from accessing the internal `Deserialize` state. +fn wrap_default(cx: &ExtCtxt, + field_ty: &P, + generics: &ast::Generics, + expr: P) -> P { + let builder = AstBuilder::new(); + + // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. + let fn_path = builder.path() + .segment("__serde_default") + .with_generics(generics.clone()) + .build() + .build(); + + let where_clause = &generics.where_clause; + + quote_expr!(cx, { + fn __serde_default $generics() -> $field_ty $where_clause { + $expr + } + $fn_path() + }) +} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 298c856d..458913e0 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -503,6 +503,7 @@ fn deserialize_struct( cx, builder, type_path.clone(), + impl_generics, fields, container_attrs )); @@ -756,6 +757,7 @@ fn deserialize_struct_variant( cx, builder, type_path, + generics, fields, container_attrs, )); @@ -918,20 +920,21 @@ fn deserialize_struct_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_path: ast::Path, + generics: &ast::Generics, fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, ) -> Result<(Vec>, ast::Stmt, P), Error> { + let field_exprs = fields.iter() + .map(|field| { + let field_attrs = try!(attr::FieldAttrs::from_field(cx, generics, field)); + Ok(field_attrs.deserialize_name_expr()) + }) + .collect(); + let field_visitor = deserialize_field_visitor( cx, builder, - try!( - fields.iter() - .map(|field| { - let attrs = try!(attr::FieldAttrs::from_field(cx, field)); - Ok(attrs.deserialize_name_expr()) - }) - .collect() - ), + try!(field_exprs), container_attrs ); @@ -939,6 +942,7 @@ fn deserialize_struct_visitor( cx, builder, struct_path, + generics, fields, container_attrs, )); @@ -968,6 +972,7 @@ fn deserialize_map( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_path: ast::Path, + generics: &ast::Generics, fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { @@ -1003,26 +1008,22 @@ fn deserialize_map( .chain(ignored_arm.into_iter()) .collect(); - let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); + let extract_values = fields.iter() + .zip(field_names.iter()) + .map(|(field, field_name)| { + let field_attr = try!(attr::FieldAttrs::from_field(cx, generics, field)); + let missing_expr = field_attr.expr_is_missing(); - 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.ident_expr(); - quote_expr!(cx, try!(visitor.missing_field($name))) - }; - - quote_stmt!(cx, + Ok(quote_stmt!(cx, let $field_name = match $field_name { Some($field_name) => $field_name, None => $missing_expr }; - ).unwrap() + ).unwrap()) }) - .collect::>(); + .collect::, _>>(); + + let extract_values = try!(extract_values); 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 ab59071a..7aef3de1 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -605,7 +605,7 @@ fn serialize_struct_visitor( { let value_exprs = value_exprs.collect::>(); - let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); + let field_attrs = try!(attr::get_struct_field_attrs(cx, generics, fields)); let arms: Vec = field_attrs.iter() .zip(value_exprs.iter()) diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index c5ff6e38..7f525746 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -7,19 +7,29 @@ use token::{ assert_de_tokens_error }; +trait Trait { + fn my_default() -> Self; +} + +impl Trait for i32 { + fn my_default() -> Self { 123 } +} + #[derive(Debug, PartialEq, Serialize, Deserialize)] -struct Default { - a1: i32, +struct DefaultStruct where C: Trait { + a1: A, #[serde(default)] - a2: i32, + a2: B, + #[serde(default="Trait::my_default()")] + a3: C, } #[test] -fn test_default() { +fn test_default_struct() { assert_de_tokens( - &Default { a1: 1, a2: 2 }, + &DefaultStruct { a1: 1, a2: 2, a3: 3 }, vec![ - Token::StructStart("Default", Some(2)), + Token::StructStart("DefaultStruct", Some(3)), Token::MapSep, Token::Str("a1"), @@ -29,14 +39,66 @@ fn test_default() { Token::Str("a2"), Token::I32(2), + Token::MapSep, + Token::Str("a3"), + Token::I32(3), + Token::MapEnd, ] ); assert_de_tokens( - &Default { a1: 1, a2: 0 }, + &DefaultStruct { a1: 1, a2: 0, a3: 123 }, vec![ - Token::StructStart("Default", Some(1)), + Token::StructStart("DefaultStruct", Some(1)), + + Token::MapSep, + Token::Str("a1"), + Token::I32(1), + + Token::MapEnd, + ] + ); +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +enum DefaultEnum where C: Trait { + Struct { + a1: A, + #[serde(default)] + a2: B, + #[serde(default="Trait::my_default()")] + a3: C, + } +} + +#[test] +fn test_default_enum() { + assert_de_tokens( + &DefaultEnum::Struct { a1: 1, a2: 2, a3: 3 }, + vec![ + Token::EnumMapStart("DefaultEnum", "Struct", Some(3)), + + Token::MapSep, + Token::Str("a1"), + Token::I32(1), + + Token::MapSep, + Token::Str("a2"), + Token::I32(2), + + Token::MapSep, + Token::Str("a3"), + Token::I32(3), + + Token::MapEnd, + ] + ); + + assert_de_tokens( + &DefaultEnum::Struct { a1: 1, a2: 0, a3: 123 }, + vec![ + Token::EnumMapStart("DefaultEnum", "Struct", Some(3)), Token::MapSep, Token::Str("a1"), @@ -57,9 +119,9 @@ struct DenyUnknown { fn test_ignore_unknown() { // 'Default' allows unknown. Basic smoke test of ignore... assert_de_tokens( - &Default { a1: 1, a2: 2}, + &DefaultStruct { a1: 1, a2: 2, a3: 3 }, vec![ - Token::StructStart("Default", Some(5)), + Token::StructStart("DefaultStruct", Some(5)), Token::MapSep, Token::Str("whoops1"), @@ -84,6 +146,10 @@ fn test_ignore_unknown() { Token::Str("whoops3"), Token::I32(2), + Token::MapSep, + Token::Str("a3"), + Token::I32(3), + Token::MapEnd, ] ); From de89f95f3162a2f1404ed63940dc26ffa1bfbaf0 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 12 Feb 2016 22:05:02 -0800 Subject: [PATCH 3/9] feat(codegen): Add support for #![serde(skip_serialize_if="$expr")] This allows end users to use an arbitrary expression to decide whether or not to serialize some field. This expression has access to all the fields in the struct, but none of the internal state of the Serialize implementation. For structs, serde implements this by creating a temporary trait and implementing the struct for it. For struct variants, the fields are copied by reference into a temporary struct first before implementing the temporary trait. This also fixes a bug where the serde_codegen wasn't making calls to Serializer::serialize_{tuple,struct}_variant{,_elt}. --- serde_codegen/src/attr.rs | 46 ++- serde_codegen/src/de.rs | 19 +- serde_codegen/src/ser.rs | 262 ++++++++++------- serde_tests/tests/test_annotations.rs | 138 +++++---- serde_tests/tests/test_de.rs | 42 +-- serde_tests/tests/test_macros.rs | 129 +++++---- serde_tests/tests/test_ser.rs | 34 +-- serde_tests/tests/token.rs | 396 +++++++++++++++++++++++--- 8 files changed, 761 insertions(+), 305 deletions(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 7c69cdf1..b440af7e 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -166,6 +166,7 @@ pub struct FieldAttrs { serialize_name: Option, deserialize_name: Option, skip_serializing_field: bool, + skip_serializing_field_if: Option>, skip_serializing_field_if_empty: bool, skip_serializing_field_if_none: bool, default_expr_if_missing: Option>, @@ -174,6 +175,7 @@ pub struct FieldAttrs { impl FieldAttrs { /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn from_field(cx: &ExtCtxt, + container_ty: &P, generics: &ast::Generics, field: &ast::StructField) -> Result { let builder = AstBuilder::new(); @@ -188,6 +190,7 @@ impl FieldAttrs { serialize_name: None, deserialize_name: None, skip_serializing_field: false, + skip_serializing_field_if: None, skip_serializing_field_if_empty: false, skip_serializing_field_if_none: false, default_expr_if_missing: None, @@ -232,6 +235,18 @@ impl FieldAttrs { field_attrs.skip_serializing_field = true; } + // Parse `#[serde(skip_serializing_if="...")]` + ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => { + let expr = wrap_skip_serializing( + cx, + container_ty, + generics, + try!(parse_lit_into_expr(cx, name, lit)), + ); + + field_attrs.skip_serializing_field_if = Some(expr); + } + // Parse `#[serde(skip_serializing_if_none)]` ast::MetaItemKind::Word(ref name) if name == &"skip_serializing_if_none" => { field_attrs.skip_serializing_field_if_none = true; @@ -298,6 +313,10 @@ impl FieldAttrs { self.skip_serializing_field } + pub fn skip_serializing_field_if(&self) -> Option<&P> { + self.skip_serializing_field_if.as_ref() + } + pub fn skip_serializing_field_if_empty(&self) -> bool { self.skip_serializing_field_if_empty } @@ -307,12 +326,14 @@ impl FieldAttrs { } } + /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn get_struct_field_attrs(cx: &ExtCtxt, + container_ty: &P, generics: &ast::Generics, fields: &[ast::StructField]) -> Result, Error> { fields.iter() - .map(|field| FieldAttrs::from_field(cx, generics, field)) + .map(|field| FieldAttrs::from_field(cx, container_ty, generics, field)) .collect() } @@ -401,3 +422,26 @@ fn wrap_default(cx: &ExtCtxt, $fn_path() }) } + +/// This function wraps the expression in `#[serde(skip_serializing_if="...")]` in a trait to +/// prevent it from accessing the internal `Serialize` state. +fn wrap_skip_serializing(cx: &ExtCtxt, + container_ty: &P, + generics: &ast::Generics, + expr: P) -> P { + let where_clause = &generics.where_clause; + + quote_expr!(cx, { + trait __SerdeShouldSkipSerializing { + fn __serde_should_skip_serializing(&self) -> bool; + } + + impl $generics __SerdeShouldSkipSerializing for $container_ty $where_clause { + fn __serde_should_skip_serializing(&self) -> bool { + $expr + } + } + + self.value.__serde_should_skip_serializing() + }) +} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 458913e0..e95fe2dc 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -503,6 +503,7 @@ fn deserialize_struct( cx, builder, type_path.clone(), + &ty, impl_generics, fields, container_attrs @@ -757,6 +758,7 @@ fn deserialize_struct_variant( cx, builder, type_path, + &ty, generics, fields, container_attrs, @@ -920,13 +922,19 @@ fn deserialize_struct_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_path: ast::Path, + container_ty: &P, generics: &ast::Generics, fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, ) -> Result<(Vec>, ast::Stmt, P), Error> { let field_exprs = fields.iter() .map(|field| { - let field_attrs = try!(attr::FieldAttrs::from_field(cx, generics, field)); + let field_attrs = try!( + attr::FieldAttrs::from_field(cx, + container_ty, + generics, + field) + ); Ok(field_attrs.deserialize_name_expr()) }) .collect(); @@ -942,6 +950,7 @@ fn deserialize_struct_visitor( cx, builder, struct_path, + container_ty, generics, fields, container_attrs, @@ -972,6 +981,7 @@ fn deserialize_map( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_path: ast::Path, + container_ty: &P, generics: &ast::Generics, fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, @@ -1011,7 +1021,12 @@ fn deserialize_map( let extract_values = fields.iter() .zip(field_names.iter()) .map(|(field, field_name)| { - let field_attr = try!(attr::FieldAttrs::from_field(cx, generics, field)); + let field_attr = try!( + attr::FieldAttrs::from_field(cx, + container_ty, + generics, + field) + ); let missing_expr = field_attr.expr_is_missing(); Ok(quote_stmt!(cx, diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 7aef3de1..a604d298 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -33,14 +33,30 @@ pub fn expand_derive_serialize( let builder = aster::AstBuilder::new().span(span); + let impl_item = match serialize_item(cx, &builder, &item) { + Ok(item) => item, + Err(Error) => { + // An error occured, but it should have been reported already. + return; + } + }; + + push(Annotatable::Item(impl_item)) +} + +fn serialize_item( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + item: &Item, +) -> Result, Error> { let generics = match item.node { ast::ItemKind::Struct(_, ref generics) => generics, ast::ItemKind::Enum(_, ref generics) => generics, _ => { cx.span_err( - meta_item.span, + item.span, "`#[derive(Serialize)]` may only be applied to structs and enums"); - return; + return Err(Error); } }; @@ -54,17 +70,15 @@ pub fn expand_derive_serialize( .segment(item.ident).with_generics(impl_generics.clone()).build() .build(); - let body = match serialize_body(cx, &builder, &item, &impl_generics, ty.clone()) { - Ok(body) => body, - Err(Error) => { - // An error occured, but it should have been reported already. - return; - } - }; + let body = try!(serialize_body(cx, + &builder, + &item, + &impl_generics, + ty.clone())); let where_clause = &impl_generics.where_clause; - let impl_item = quote_item!(cx, + Ok(quote_item!(cx, impl $impl_generics ::serde::ser::Serialize for $ty $where_clause { fn serialize<__S>(&self, serializer: &mut __S) -> ::std::result::Result<(), __S::Error> where __S: ::serde::ser::Serializer, @@ -72,9 +86,7 @@ pub fn expand_derive_serialize( $body } } - ).unwrap(); - - push(Annotatable::Item(impl_item)) + ).unwrap()) } fn serialize_body( @@ -207,6 +219,7 @@ fn serialize_tuple_struct( .ref_() .lifetime("'__a") .build_ty(ty.clone()), + builder.id("serialize_tuple_struct_elt"), fields, impl_generics, ); @@ -232,11 +245,6 @@ fn serialize_struct( 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"); - quote_expr!(cx, &self.value.$name) - }); - let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor( cx, builder, @@ -245,9 +253,9 @@ fn serialize_struct( .ref_() .lifetime("'__a") .build_ty(ty.clone()), + builder.id("serialize_struct_elt"), fields, impl_generics, - value_exprs, )); let type_name = container_attrs.serialize_name_expr(); @@ -272,22 +280,23 @@ fn serialize_item_enum( enum_def: &ast::EnumDef, container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let mut arms = vec![]; - - for (variant_index, variant) in enum_def.variants.iter().enumerate() { - let arm = try!(serialize_variant( - cx, - builder, - type_ident, - impl_generics, - ty.clone(), - variant, - variant_index, - container_attrs, - )); - - arms.push(arm); - } + let arms: Vec<_> = try!( + enum_def.variants.iter() + .enumerate() + .map(|(variant_index, variant)| { + serialize_variant( + cx, + builder, + type_ident, + impl_generics, + ty.clone(), + variant, + variant_index, + container_attrs, + ) + }) + .collect() + ); Ok(quote_expr!(cx, match *self { @@ -404,13 +413,13 @@ fn serialize_variant( let expr = try!(serialize_struct_variant( cx, builder, - type_name, variant_index, variant_name, generics, ty, fields, field_names, + container_attrs, )); Ok(quote_arm!(cx, @@ -447,6 +456,7 @@ fn serialize_tuple_variant( builder, structure_ty.clone(), variant_ty, + builder.id("serialize_tuple_variant_elt"), fields.len(), generics, ); @@ -473,55 +483,88 @@ fn serialize_tuple_variant( fn serialize_struct_variant( cx: &ExtCtxt, builder: &aster::AstBuilder, - type_name: P, variant_index: usize, variant_name: P, generics: &ast::Generics, - structure_ty: P, + ty: P, fields: &[ast::StructField], field_names: Vec, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let value_ty = builder.ty().tuple() - .with_tys( + let variant_generics = builder.generics() + .with(generics.clone()) + .add_lifetime_bound("'__serde_variant") + .lifetime_name("'__serde_variant") + .build(); + + let variant_struct = builder.item().struct_("__VariantStruct") + .with_generics(variant_generics.clone()) + .with_fields( fields.iter().map(|field| { - builder.ty() + builder.struct_field(field.node.ident().expect("struct has unnamed fields")) + .with_attrs(field.node.attrs.iter().cloned()) + .ty() .ref_() - .lifetime("'__a") + .lifetime("'__serde_variant") .build_ty(field.node.ty.clone()) }) ) + .field("__serde_container_ty") + .ty().phantom_data().build(ty.clone()) .build(); - let value_expr = builder.expr().tuple() - .with_exprs( - field_names.iter().map(|field| { - builder.expr().id(field) - }) + let variant_expr = builder.expr().struct_id("__VariantStruct") + .with_id_exprs( + fields.iter() + .zip(field_names.iter()) + .map(|(field, field_name)| { + ( + field.node.ident().expect("struct has unnamed fields"), + builder.expr().id(field_name), + ) + }) ) + .field("__serde_container_ty").path() + .global() + .id("std").id("marker") + .segment("PhantomData") + .with_ty(ty.clone()) + .build() + .build() + .build(); + + let variant_ty = builder.ty().path() + .segment("__VariantStruct") + .with_generics(variant_generics.clone()) + .build() .build(); let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor( cx, builder, - structure_ty.clone(), - value_ty, + variant_ty.clone(), + variant_ty.clone(), + builder.id("serialize_struct_variant_elt"), fields, - generics, - (0 .. field_names.len()).map(|i| { - builder.expr() - .tup_field(i) - .field("value").self_() - }) + &variant_generics, )); + let container_name = container_attrs.serialize_name_expr(); + Ok(quote_expr!(cx, { + $variant_struct $visitor_struct $visitor_impl - serializer.serialize_struct_variant($type_name, $variant_index, $variant_name, Visitor { - value: $value_expr, - state: 0, - _structure_ty: ::std::marker::PhantomData::<&$structure_ty>, - }) + serializer.serialize_struct_variant( + $container_name, + $variant_index, + $variant_name, + Visitor { + value: $variant_expr, + state: 0, + _structure_ty: ::std::marker::PhantomData, + }, + ) })) } @@ -530,20 +573,21 @@ fn serialize_tuple_struct_visitor( builder: &aster::AstBuilder, structure_ty: P, variant_ty: P, + serializer_method: ast::Ident, fields: usize, generics: &ast::Generics ) -> (P, P) { let arms: Vec = (0 .. fields) .map(|i| { - let expr = builder.expr() - .tup_field(i) - .field("value").self_(); + let expr = builder.expr().method_call(serializer_method) + .id("serializer") + .arg().ref_().tup_field(i).field("value").self_() + .build(); quote_arm!(cx, $i => { self.state += 1; - let v = try!(serializer.serialize_tuple_struct_elt(&$expr)); - Ok(Some(v)) + Ok(Some(try!($expr))) } ) }) @@ -592,51 +636,51 @@ fn serialize_tuple_struct_visitor( ) } -fn serialize_struct_visitor( +fn serialize_struct_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, structure_ty: P, variant_ty: P, + serializer_method: ast::Ident, fields: &[ast::StructField], generics: &ast::Generics, - value_exprs: I, -) -> Result<(P, P), Error> - where I: Iterator>, -{ - let value_exprs = value_exprs.collect::>(); +) -> Result<(P, P), Error> { + let field_attrs = try!( + attr::get_struct_field_attrs(cx, &structure_ty, generics, fields) + ); - let field_attrs = try!(attr::get_struct_field_attrs(cx, generics, fields)); - - let arms: Vec = field_attrs.iter() - .zip(value_exprs.iter()) - .filter(|&(ref field, _)| !field.skip_serializing_field()) + let arms: Vec = fields.iter().zip(field_attrs.iter()) + .filter(|&(_, ref field_attr)| !field_attr.skip_serializing_field()) .enumerate() - .map(|(i, (ref field, value_expr))| { - let key_expr = field.serialize_name_expr(); + .map(|(i, (ref field, ref field_attr))| { + let name = field.node.ident().expect("struct has unnamed field"); - let stmt = if field.skip_serializing_field_if_empty() { - quote_stmt!(cx, if ($value_expr).is_empty() { continue; }) - } else if field.skip_serializing_field_if_none() { - quote_stmt!(cx, if ($value_expr).is_none() { continue; }) - } else { - quote_stmt!(cx, {}) + let key_expr = field_attr.serialize_name_expr(); + + let stmt = match field_attr.skip_serializing_field_if() { + Some(expr) => { + Some(quote_stmt!(cx, if $expr { continue; })) + } + None => { + if field_attr.skip_serializing_field_if_empty() { + Some(quote_stmt!(cx, if self.value.$name.is_empty() { continue; })) + } else if field_attr.skip_serializing_field_if_none() { + Some(quote_stmt!(cx, if self.value.$name.is_none() { continue; })) + } else { + None + } + } }; + let expr = quote_expr!(cx, + serializer.$serializer_method($key_expr, &self.value.$name) + ); + quote_arm!(cx, $i => { self.state += 1; $stmt - - return Ok( - Some( - try!( - serializer.serialize_struct_elt( - $key_expr, - $value_expr, - ) - ) - ) - ); + return Ok(Some(try!($expr))); } ) }) @@ -653,17 +697,27 @@ fn serialize_struct_visitor( .strip_bounds() .build(); - let len = field_attrs.iter() - .zip(value_exprs.iter()) - .map(|(field, value_expr)| { - if field.skip_serializing_field() { + let len = fields.iter().zip(field_attrs.iter()) + .map(|(field, field_attr)| { + if field_attr.skip_serializing_field() { quote_expr!(cx, 0) - } else if field.skip_serializing_field_if_empty() { - quote_expr!(cx, if ($value_expr).is_empty() { 0 } else { 1 }) - } else if field.skip_serializing_field_if_none() { - quote_expr!(cx, if ($value_expr).is_none() { 0 } else { 1 }) } else { - quote_expr!(cx, 1) + match field_attr.skip_serializing_field_if() { + Some(expr) => { + quote_expr!(cx, if $expr { 0 } else { 1 }) + } + None => { + let name = field.node.ident().expect("struct has unnamed field"); + + if field_attr.skip_serializing_field_if_empty() { + quote_expr!(cx, if self.value.$name.is_empty() { 0 } else { 1 }) + } else if field_attr.skip_serializing_field_if_none() { + quote_expr!(cx, if self.value.$name.is_none() { 0 } else { 1 }) + } else { + quote_expr!(cx, 1) + } + } + } } }) .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 7f525746..831c32cd 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -9,10 +9,14 @@ use token::{ trait Trait { fn my_default() -> Self; + + fn should_skip(&self) -> bool; } impl Trait for i32 { fn my_default() -> Self { 123 } + + fn should_skip(&self) -> bool { *self == 123 } } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -31,19 +35,19 @@ fn test_default_struct() { vec![ Token::StructStart("DefaultStruct", Some(3)), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("a2"), Token::I32(2), - Token::MapSep, + Token::StructSep, Token::Str("a3"), Token::I32(3), - Token::MapEnd, + Token::StructEnd, ] ); @@ -52,11 +56,11 @@ fn test_default_struct() { vec![ Token::StructStart("DefaultStruct", Some(1)), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapEnd, + Token::StructEnd, ] ); } @@ -79,19 +83,19 @@ fn test_default_enum() { vec![ Token::EnumMapStart("DefaultEnum", "Struct", Some(3)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::EnumMapSep, Token::Str("a2"), Token::I32(2), - Token::MapSep, + Token::EnumMapSep, Token::Str("a3"), Token::I32(3), - Token::MapEnd, + Token::EnumMapEnd, ] ); @@ -100,11 +104,11 @@ fn test_default_enum() { vec![ Token::EnumMapStart("DefaultEnum", "Struct", Some(3)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a1"), Token::I32(1), - Token::MapEnd, + Token::EnumMapEnd, ] ); } @@ -123,34 +127,34 @@ fn test_ignore_unknown() { vec![ Token::StructStart("DefaultStruct", Some(5)), - Token::MapSep, + Token::StructSep, Token::Str("whoops1"), Token::I32(2), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("whoops2"), Token::SeqStart(Some(1)), Token::SeqSep, Token::I32(2), Token::SeqEnd, - Token::MapSep, + Token::StructSep, Token::Str("a2"), Token::I32(2), - Token::MapSep, + Token::StructSep, Token::Str("whoops3"), Token::I32(2), - Token::MapSep, + Token::StructSep, Token::Str("a3"), Token::I32(3), - Token::MapEnd, + Token::StructEnd, ] ); @@ -158,15 +162,15 @@ fn test_ignore_unknown() { vec![ Token::StructStart("DenyUnknown", Some(2)), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("whoops"), Token::I32(2), - Token::MapEnd, + Token::StructEnd, ], Error::UnknownFieldError("whoops".to_owned()) ); @@ -195,15 +199,15 @@ fn test_rename_struct() { vec![ Token::StructStart("Superhero", Some(2)), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("a3"), Token::I32(2), - Token::MapEnd, + Token::StructEnd, ] ); @@ -212,15 +216,15 @@ fn test_rename_struct() { &[ Token::StructStart("SuperheroSer", Some(2)), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("a4"), Token::I32(2), - Token::MapEnd, + Token::StructEnd, ] ); @@ -229,15 +233,15 @@ fn test_rename_struct() { vec![ Token::StructStart("SuperheroDe", Some(2)), - Token::MapSep, + Token::StructSep, Token::Str("a1"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("a5"), Token::I32(2), - Token::MapEnd, + Token::StructEnd, ] ); } @@ -281,7 +285,7 @@ fn test_rename_enum() { assert_tokens( &RenameEnum::Superman(0), vec![ - Token::EnumNewtype("Superhero", "clark_kent"), + Token::EnumNewType("Superhero", "clark_kent"), Token::I8(0), ] ); @@ -291,13 +295,13 @@ fn test_rename_enum() { vec![ Token::EnumSeqStart("Superhero", "diana_prince", Some(2)), - Token::SeqSep, + Token::EnumSeqSep, Token::I8(0), - Token::SeqSep, + Token::EnumSeqSep, Token::I8(1), - Token::SeqEnd, + Token::EnumSeqEnd, ] ); @@ -306,11 +310,11 @@ fn test_rename_enum() { vec![ Token::EnumMapStart("Superhero", "barry_allan", Some(1)), - Token::MapSep, + Token::EnumMapSep, Token::Str("b"), Token::I32(1), - Token::MapEnd, + Token::EnumMapEnd, ] ); @@ -322,15 +326,15 @@ fn test_rename_enum() { &[ Token::EnumMapStart("SuperheroSer", "dick_grayson", Some(2)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I8(0), - Token::MapSep, + Token::EnumMapSep, Token::Str("c"), Token::Str(""), - Token::MapEnd, + Token::EnumMapEnd, ] ); @@ -342,24 +346,26 @@ fn test_rename_enum() { vec![ Token::EnumMapStart("SuperheroDe", "jason_todd", Some(2)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I8(0), - Token::MapSep, + Token::EnumMapSep, Token::Str("d"), Token::Str(""), - Token::MapEnd, + Token::EnumMapEnd, ] ); } #[derive(Debug, PartialEq, Serialize)] -struct SkipSerializingStruct<'a, B, D, E> { +struct SkipSerializingStruct<'a, B, C, D, E> where C: Trait { a: &'a i8, #[serde(skip_serializing)] b: B, + #[serde(skip_serializing_if="self.c.should_skip()")] + c: C, #[serde(skip_serializing_if_none)] d: Option, #[serde(skip_serializing_if_empty)] @@ -373,29 +379,34 @@ fn test_skip_serializing_struct() { &SkipSerializingStruct { a: &a, b: 2, + c: 3, d: Some(4), e: vec![5], }, &[ - Token::StructStart("SkipSerializingStruct", Some(3)), + Token::StructStart("SkipSerializingStruct", Some(4)), - Token::MapSep, + Token::StructSep, Token::Str("a"), Token::I8(1), - Token::MapSep, + Token::StructSep, + Token::Str("c"), + Token::I32(3), + + Token::StructSep, Token::Str("d"), Token::Option(true), Token::I32(4), - Token::MapSep, + Token::StructSep, Token::Str("e"), Token::SeqStart(Some(1)), Token::SeqSep, Token::I32(5), Token::SeqEnd, - Token::MapEnd, + Token::StructEnd, ] ); @@ -403,27 +414,30 @@ fn test_skip_serializing_struct() { &SkipSerializingStruct { a: &a, b: 2, + c: 123, d: None::, e: Vec::::new(), }, &[ Token::StructStart("SkipSerializingStruct", Some(1)), - Token::MapSep, + Token::StructSep, Token::Str("a"), Token::I8(1), - Token::MapEnd, + Token::StructEnd, ] ); } #[derive(Debug, PartialEq, Serialize)] -enum SkipSerializingEnum<'a, B, D, E> { +enum SkipSerializingEnum<'a, B, C, D, E> where C: Trait { Struct { a: &'a i8, #[serde(skip_serializing)] _b: B, + #[serde(skip_serializing_if="self.c.should_skip()")] + c: C, #[serde(skip_serializing_if_none)] d: Option, #[serde(skip_serializing_if_empty)] @@ -438,29 +452,34 @@ fn test_skip_serializing_enum() { &SkipSerializingEnum::Struct { a: &a, _b: 2, + c: 3, d: Some(4), e: vec![5], }, &[ - Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(3)), + Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(4)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I8(1), - Token::MapSep, + Token::EnumMapSep, + Token::Str("c"), + Token::I32(3), + + Token::EnumMapSep, Token::Str("d"), Token::Option(true), Token::I32(4), - Token::MapSep, + Token::EnumMapSep, Token::Str("e"), Token::SeqStart(Some(1)), Token::SeqSep, Token::I32(5), Token::SeqEnd, - Token::MapEnd, + Token::EnumMapEnd, ] ); @@ -468,17 +487,18 @@ fn test_skip_serializing_enum() { &SkipSerializingEnum::Struct { a: &a, _b: 2, + c: 123, d: None::, e: Vec::::new(), }, &[ Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(1)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I8(1), - Token::MapEnd, + Token::EnumMapEnd, ] ); } diff --git a/serde_tests/tests/test_de.rs b/serde_tests/tests/test_de.rs index 395e7645..dcc6e8f0 100644 --- a/serde_tests/tests/test_de.rs +++ b/serde_tests/tests/test_de.rs @@ -184,27 +184,27 @@ declare_tests! { ], TupleStruct(1, 2, 3) => vec![ Token::TupleStructStart("TupleStruct", Some(3)), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(1), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(2), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(3), - Token::SeqEnd, + Token::TupleSeqEnd, ], TupleStruct(1, 2, 3) => vec![ Token::TupleStructStart("TupleStruct", None), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(1), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(2), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(3), - Token::SeqEnd, + Token::TupleSeqEnd, ], } test_btreeset { @@ -495,18 +495,18 @@ declare_tests! { ], Struct { a: 1, b: 2, c: 3 } => vec![ Token::StructStart("Struct", Some(3)), - Token::MapSep, + Token::StructSep, Token::Str("a"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("b"), Token::I32(2), - Token::MapSep, + Token::StructSep, Token::Str("c"), Token::I32(3), - Token::MapEnd, + Token::StructEnd, ], } test_enum_unit { @@ -516,39 +516,39 @@ declare_tests! { } test_enum_simple { Enum::Simple(1) => vec![ - Token::EnumNewtype("Enum", "Simple"), + Token::EnumNewType("Enum", "Simple"), Token::I32(1), ], } test_enum_seq { Enum::Seq(1, 2, 3) => vec![ Token::EnumSeqStart("Enum", "Seq", Some(3)), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(1), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(2), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(3), - Token::SeqEnd, + Token::EnumSeqEnd, ], } test_enum_map { Enum::Map { a: 1, b: 2, c: 3 } => vec![ Token::EnumMapStart("Enum", "Map", Some(3)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I32(1), - Token::MapSep, + Token::EnumMapSep, Token::Str("b"), Token::I32(2), - Token::MapSep, + Token::EnumMapSep, Token::Str("c"), Token::I32(3), - Token::MapEnd, + Token::EnumMapEnd, ], } test_enum_unit_usize { diff --git a/serde_tests/tests/test_macros.rs b/serde_tests/tests/test_macros.rs index f8396fb7..842fa670 100644 --- a/serde_tests/tests/test_macros.rs +++ b/serde_tests/tests/test_macros.rs @@ -123,7 +123,7 @@ pub struct GenericStruct { } #[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct GenericNewtypeStruct(T); +pub struct GenericNewTypeStruct(T); #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct GenericTupleStruct(T, U); @@ -131,7 +131,7 @@ pub struct GenericTupleStruct(T, U); #[derive(Debug, PartialEq, Serialize, Deserialize)] pub enum GenericEnum { Unit, - Newtype(T), + NewType(T), Seq(T, U), Map { x: T, y: U }, } @@ -153,16 +153,16 @@ fn test_ser_named_tuple() { &SerNamedTuple(&a, &mut b, c), &[ Token::TupleStructStart("SerNamedTuple", Some(3)), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(5), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(6), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(7), - Token::SeqEnd, + Token::TupleSeqEnd, ], ); } @@ -172,7 +172,7 @@ fn test_de_named_tuple() { assert_de_tokens( &DeNamedTuple(5, 6, 7), vec![ - Token::TupleStructStart("DeNamedTuple", Some(3)), + Token::SeqStart(Some(3)), Token::SeqSep, Token::I32(5), @@ -185,6 +185,23 @@ fn test_de_named_tuple() { Token::SeqEnd, ] ); + + assert_de_tokens( + &DeNamedTuple(5, 6, 7), + vec![ + Token::TupleStructStart("DeNamedTuple", Some(3)), + Token::TupleSeqSep, + Token::I32(5), + + Token::TupleSeqSep, + Token::I32(6), + + Token::TupleSeqSep, + Token::I32(7), + + Token::TupleSeqEnd, + ] + ); } #[test] @@ -202,19 +219,19 @@ fn test_ser_named_map() { &[ Token::StructStart("SerNamedMap", Some(3)), - Token::MapSep, + Token::StructSep, Token::Str("a"), Token::I32(5), - Token::MapSep, + Token::StructSep, Token::Str("b"), Token::I32(6), - Token::MapSep, + Token::StructSep, Token::Str("c"), Token::I32(7), - Token::MapEnd, + Token::StructEnd, ] ); } @@ -230,19 +247,19 @@ fn test_de_named_map() { vec![ Token::StructStart("DeNamedMap", Some(3)), - Token::MapSep, + Token::StructSep, Token::Str("a"), Token::I32(5), - Token::MapSep, + Token::StructSep, Token::Str("b"), Token::I32(6), - Token::MapSep, + Token::StructSep, Token::Str("c"), Token::I32(7), - Token::MapEnd, + Token::StructEnd, ] ); } @@ -278,19 +295,19 @@ fn test_ser_enum_seq() { &[ Token::EnumSeqStart("SerEnum", "Seq", Some(4)), - Token::SeqSep, + Token::EnumSeqSep, Token::I8(1), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(2), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(3), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(5), - Token::SeqEnd, + Token::EnumSeqEnd, ], ); } @@ -316,23 +333,23 @@ fn test_ser_enum_map() { &[ Token::EnumMapStart("SerEnum", "Map", Some(4)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I8(1), - Token::MapSep, + Token::EnumMapSep, Token::Str("b"), Token::I32(2), - Token::MapSep, + Token::EnumMapSep, Token::Str("c"), Token::I32(3), - Token::MapSep, + Token::EnumMapSep, Token::Str("e"), Token::I32(5), - Token::MapEnd, + Token::EnumMapEnd, ], ); } @@ -368,19 +385,19 @@ fn test_de_enum_seq() { vec![ Token::EnumSeqStart("DeEnum", "Seq", Some(4)), - Token::SeqSep, + Token::EnumSeqSep, Token::I8(1), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(2), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(3), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(5), - Token::SeqEnd, + Token::EnumSeqEnd, ], ); } @@ -406,23 +423,23 @@ fn test_de_enum_map() { vec![ Token::EnumMapStart("DeEnum", "Map", Some(4)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I8(1), - Token::MapSep, + Token::EnumMapSep, Token::Str("b"), Token::I32(2), - Token::MapSep, + Token::EnumMapSep, Token::Str("c"), Token::I32(3), - Token::MapSep, + Token::EnumMapSep, Token::Str("e"), Token::I32(5), - Token::MapEnd, + Token::EnumMapEnd, ], ); } @@ -434,7 +451,7 @@ fn test_lifetimes() { assert_ser_tokens( &Lifetimes::LifetimeSeq(&value), &[ - Token::EnumNewtype("Lifetimes", "LifetimeSeq"), + Token::EnumNewType("Lifetimes", "LifetimeSeq"), Token::I32(5), ] ); @@ -442,7 +459,7 @@ fn test_lifetimes() { assert_ser_tokens( &Lifetimes::NoLifetimeSeq(5), &[ - Token::EnumNewtype("Lifetimes", "NoLifetimeSeq"), + Token::EnumNewType("Lifetimes", "NoLifetimeSeq"), Token::I32(5), ] ); @@ -452,11 +469,11 @@ fn test_lifetimes() { &[ Token::EnumMapStart("Lifetimes", "LifetimeMap", Some(1)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I32(5), - Token::MapEnd, + Token::EnumMapEnd, ] ); @@ -465,11 +482,11 @@ fn test_lifetimes() { &[ Token::EnumMapStart("Lifetimes", "NoLifetimeMap", Some(1)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I32(5), - Token::MapEnd, + Token::EnumMapEnd, ] ); } @@ -481,11 +498,11 @@ fn test_generic_struct() { vec![ Token::StructStart("GenericStruct", Some(1)), - Token::MapSep, + Token::StructSep, Token::Str("x"), Token::U32(5), - Token::MapEnd, + Token::StructEnd, ] ); } @@ -493,9 +510,9 @@ fn test_generic_struct() { #[test] fn test_generic_newtype_struct() { assert_tokens( - &GenericNewtypeStruct(5u32), + &GenericNewTypeStruct(5u32), vec![ - Token::StructNewtype("GenericNewtypeStruct"), + Token::StructNewType("GenericNewTypeStruct"), Token::U32(5), ] ); @@ -508,13 +525,13 @@ fn test_generic_tuple_struct() { vec![ Token::TupleStructStart("GenericTupleStruct", Some(2)), - Token::SeqSep, + Token::TupleSeqSep, Token::U32(5), - Token::SeqSep, + Token::TupleSeqSep, Token::U32(6), - Token::SeqEnd, + Token::TupleSeqEnd, ] ); } @@ -532,9 +549,9 @@ fn test_generic_enum_unit() { #[test] fn test_generic_enum_newtype() { assert_tokens( - &GenericEnum::Newtype::(5), + &GenericEnum::NewType::(5), vec![ - Token::EnumNewtype("GenericEnum", "Newtype"), + Token::EnumNewType("GenericEnum", "NewType"), Token::U32(5), ] ); @@ -547,13 +564,13 @@ fn test_generic_enum_seq() { vec![ Token::EnumSeqStart("GenericEnum", "Seq", Some(2)), - Token::SeqSep, + Token::EnumSeqSep, Token::U32(5), - Token::SeqSep, + Token::EnumSeqSep, Token::U32(6), - Token::SeqEnd, + Token::EnumSeqEnd, ] ); } @@ -565,15 +582,15 @@ fn test_generic_enum_map() { vec![ Token::EnumMapStart("GenericEnum", "Map", Some(2)), - Token::MapSep, + Token::EnumMapSep, Token::Str("x"), Token::U32(5), - Token::MapSep, + Token::EnumMapSep, Token::Str("y"), Token::U32(6), - Token::MapEnd, + Token::EnumMapEnd, ] ); } diff --git a/serde_tests/tests/test_ser.rs b/serde_tests/tests/test_ser.rs index 77bf3eb8..e08d8c14 100644 --- a/serde_tests/tests/test_ser.rs +++ b/serde_tests/tests/test_ser.rs @@ -76,11 +76,11 @@ declare_ser_tests! { } test_result { Ok::(0) => &[ - Token::EnumNewtype("Result", "Ok"), + Token::EnumNewType("Result", "Ok"), Token::I32(0), ], Err::(1) => &[ - Token::EnumNewtype("Result", "Err"), + Token::EnumNewType("Result", "Err"), Token::I32(1), ], } @@ -214,56 +214,56 @@ declare_ser_tests! { test_tuple_struct { TupleStruct(1, 2, 3) => &[ Token::TupleStructStart("TupleStruct", Some(3)), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(1), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(2), - Token::SeqSep, + Token::TupleSeqSep, Token::I32(3), - Token::SeqEnd, + Token::TupleSeqEnd, ], } test_struct { Struct { a: 1, b: 2, c: 3 } => &[ Token::StructStart("Struct", Some(3)), - Token::MapSep, + Token::StructSep, Token::Str("a"), Token::I32(1), - Token::MapSep, + Token::StructSep, Token::Str("b"), Token::I32(2), - Token::MapSep, + Token::StructSep, Token::Str("c"), Token::I32(3), - Token::MapEnd, + Token::StructEnd, ], } test_enum { Enum::Unit => &[Token::EnumUnit("Enum", "Unit")], - Enum::One(42) => &[Token::EnumNewtype("Enum", "One"), Token::I32(42)], + Enum::One(42) => &[Token::EnumNewType("Enum", "One"), Token::I32(42)], Enum::Seq(1, 2) => &[ Token::EnumSeqStart("Enum", "Seq", Some(2)), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(1), - Token::SeqSep, + Token::EnumSeqSep, Token::I32(2), - Token::SeqEnd, + Token::EnumSeqEnd, ], Enum::Map { a: 1, b: 2 } => &[ Token::EnumMapStart("Enum", "Map", Some(2)), - Token::MapSep, + Token::EnumMapSep, Token::Str("a"), Token::I32(1), - Token::MapSep, + Token::EnumMapSep, Token::Str("b"), Token::I32(2), - Token::MapEnd, + Token::EnumMapEnd, ], } test_num_bigint { diff --git a/serde_tests/tests/token.rs b/serde_tests/tests/token.rs index 1b14a7d3..40810d47 100644 --- a/serde_tests/tests/token.rs +++ b/serde_tests/tests/token.rs @@ -2,7 +2,8 @@ use std::fmt; use std::iter; use std::error; -use serde::{ser, de}; +use serde::ser::{self, Serialize}; +use serde::de; use serde::de::value::{self, ValueDeserializer}; #[derive(Clone, PartialEq, Debug)] @@ -30,23 +31,35 @@ pub enum Token<'a> { Unit, UnitStruct(&'a str), - StructNewtype(&'a str), + StructNewType(&'a str), EnumStart(&'a str), EnumUnit(&'a str, &'a str), - EnumNewtype(&'a str, &'a str), - EnumSeqStart(&'a str, &'a str, Option), - EnumMapStart(&'a str, &'a str, Option), + EnumNewType(&'a str, &'a str), SeqStart(Option), - TupleStructStart(&'a str, Option), SeqSep, SeqEnd, + TupleStructStart(&'a str, Option), + TupleSeqSep, + TupleSeqEnd, + MapStart(Option), - StructStart(&'a str, Option), MapSep, MapEnd, + + StructStart(&'a str, Option), + StructSep, + StructEnd, + + EnumSeqStart(&'a str, &'a str, Option), + EnumSeqSep, + EnumSeqEnd, + + EnumMapStart(&'a str, &'a str, Option), + EnumMapSep, + EnumMapEnd, } ////////////////////////////////////////////////////////////////////////////// @@ -102,7 +115,7 @@ impl<'a, I> ser::Serializer for Serializer value: T) -> Result<(), Error> where T: ser::Serialize, { - assert_eq!(self.tokens.next(), Some(&Token::EnumNewtype(name, variant))); + assert_eq!(self.tokens.next(), Some(&Token::EnumNewType(name, variant))); value.serialize(self) } @@ -218,43 +231,65 @@ impl<'a, I> ser::Serializer for Serializer self.visit_sequence(visitor) } + fn serialize_seq_elt(&mut self, value: T) -> Result<(), Error> + where T: ser::Serialize + { + assert_eq!(self.tokens.next(), Some(&Token::SeqSep)); + value.serialize(self) + } + fn serialize_newtype_struct(&mut self, name: &'static str, value: T) -> Result<(), Error> where T: ser::Serialize, { - assert_eq!(self.tokens.next(), Some(&Token::StructNewtype(name))); + assert_eq!(self.tokens.next(), Some(&Token::StructNewType(name))); value.serialize(self) } - fn serialize_tuple_struct(&mut self, name: &str, visitor: V) -> Result<(), Error> + fn serialize_tuple_struct(&mut self, name: &str, mut visitor: V) -> Result<(), Error> where V: ser::SeqVisitor { let len = visitor.len(); assert_eq!(self.tokens.next(), Some(&Token::TupleStructStart(name, len))); - self.visit_sequence(visitor) + while let Some(()) = try!(visitor.visit(self)) { } + + assert_eq!(self.tokens.next(), Some(&Token::TupleSeqEnd)); + + Ok(()) + } + + fn serialize_tuple_struct_elt(&mut self, value: T) -> Result<(), Error> + where T: ser::Serialize, + { + assert_eq!(self.tokens.next(), Some(&Token::TupleSeqSep)); + value.serialize(self) } fn serialize_tuple_variant(&mut self, - name: &str, - _variant_index: usize, - variant: &str, - visitor: V) -> Result<(), Error> + name: &str, + _variant_index: usize, + variant: &str, + mut visitor: V) -> Result<(), Error> where V: ser::SeqVisitor { let len = visitor.len(); assert_eq!(self.tokens.next(), Some(&Token::EnumSeqStart(name, variant, len))); - self.visit_sequence(visitor) + while let Some(()) = try!(visitor.visit(self)) { } + + assert_eq!(self.tokens.next(), Some(&Token::EnumSeqEnd)); + + Ok(()) } - fn serialize_seq_elt(&mut self, value: T) -> Result<(), Error> - where T: ser::Serialize + fn serialize_tuple_variant_elt(&mut self, value: T) -> Result<(), Error> + where T: ser::Serialize, { - assert_eq!(self.tokens.next(), Some(&Token::SeqSep)); + assert_eq!(self.tokens.next(), Some(&Token::EnumSeqSep)); value.serialize(self) } @@ -268,35 +303,61 @@ impl<'a, I> ser::Serializer for Serializer self.visit_mapping(visitor) } - fn serialize_struct(&mut self, name: &str, visitor: V) -> Result<(), Error> + fn serialize_map_elt(&mut self, key: K, value: V) -> Result<(), Error> + where K: ser::Serialize, + V: ser::Serialize, + { + assert_eq!(self.tokens.next(), Some(&Token::MapSep)); + + try!(key.serialize(self)); + value.serialize(self) + } + + fn serialize_struct(&mut self, name: &str, mut visitor: V) -> Result<(), Error> where V: ser::MapVisitor { let len = visitor.len(); assert_eq!(self.tokens.next(), Some(&Token::StructStart(name, len))); - self.visit_mapping(visitor) + while let Some(()) = try!(visitor.visit(self)) { } + + assert_eq!(self.tokens.next(), Some(&Token::StructEnd)); + + Ok(()) + } + + fn serialize_struct_elt(&mut self, key: &'static str, value: T) -> Result<(), Error> + where T: ser::Serialize, + { + assert_eq!(self.tokens.next(), Some(&Token::StructSep)); + + try!(key.serialize(self)); + value.serialize(self) } fn serialize_struct_variant(&mut self, - name: &str, - _variant_index: usize, - variant: &str, - visitor: V) -> Result<(), Error> + name: &str, + _variant_index: usize, + variant: &str, + mut visitor: V) -> Result<(), Error> where V: ser::MapVisitor { let len = visitor.len(); assert_eq!(self.tokens.next(), Some(&Token::EnumMapStart(name, variant, len))); - self.visit_mapping(visitor) + while let Some(()) = try!(visitor.visit(self)) { } + + assert_eq!(self.tokens.next(), Some(&Token::EnumMapEnd)); + + Ok(()) } - fn serialize_map_elt(&mut self, key: K, value: V) -> Result<(), Error> - where K: ser::Serialize, - V: ser::Serialize, + fn serialize_struct_variant_elt(&mut self, key: &'static str, value: T) -> Result<(), Error> + where T: ser::Serialize, { - assert_eq!(self.tokens.next(), Some(&Token::MapSep)); + assert_eq!(self.tokens.next(), Some(&Token::EnumMapSep)); try!(key.serialize(self)); value.serialize(self) @@ -383,6 +444,24 @@ impl Deserializer }) } + fn visit_tuple_seq(&mut self, len: Option, mut visitor: V) -> Result + where V: de::Visitor, + { + visitor.visit_seq(DeserializerTupleSeqVisitor { + de: self, + len: len, + }) + } + + fn visit_variant_seq(&mut self, len: Option, mut visitor: V) -> Result + where V: de::Visitor, + { + visitor.visit_seq(DeserializerVariantSeqVisitor { + de: self, + len: len, + }) + } + fn visit_map(&mut self, len: Option, mut visitor: V) -> Result where V: de::Visitor, { @@ -391,6 +470,24 @@ impl Deserializer len: len, }) } + + fn visit_struct(&mut self, len: Option, mut visitor: V) -> Result + where V: de::Visitor, + { + visitor.visit_map(DeserializerStructVisitor { + de: self, + len: len, + }) + } + + fn visit_variant_map(&mut self, len: Option, mut visitor: V) -> Result + where V: de::Visitor, + { + visitor.visit_map(DeserializerVariantMapVisitor { + de: self, + len: len, + }) + } } impl de::Deserializer for Deserializer @@ -429,7 +526,6 @@ impl de::Deserializer for Deserializer Some(Token::MapStart(len)) | Some(Token::StructStart(_, len)) => { self.visit_map(len, visitor) } - //Some(Token::Name(_)) => self.visit(visitor), Some(token) => Err(Error::UnexpectedToken(token)), None => Err(Error::EndOfStreamError), } @@ -473,7 +569,7 @@ impl de::Deserializer for Deserializer }) } Some(&Token::EnumUnit(n, _)) - | Some(&Token::EnumNewtype(n, _)) + | Some(&Token::EnumNewType(n, _)) | Some(&Token::EnumSeqStart(n, _, _)) | Some(&Token::EnumMapStart(n, _, _)) if name == n => { visitor.visit(DeserializerVariantVisitor { @@ -506,12 +602,12 @@ impl de::Deserializer for Deserializer } fn deserialize_newtype_struct(&mut self, - name: &str, - mut visitor: V) -> Result + name: &str, + mut visitor: V) -> Result where V: de::Visitor, { match self.tokens.peek() { - Some(&Token::StructNewtype(n)) => { + Some(&Token::StructNewType(n)) => { self.tokens.next(); if name == n { visitor.visit_newtype_struct(self) @@ -525,9 +621,9 @@ impl de::Deserializer for Deserializer } fn deserialize_tuple_struct(&mut self, - name: &str, - len: usize, - mut visitor: V) -> Result + name: &str, + len: usize, + mut visitor: V) -> Result where V: de::Visitor, { match self.tokens.peek() { @@ -542,7 +638,7 @@ impl de::Deserializer for Deserializer Some(&Token::TupleStructStart(n, _)) => { self.tokens.next(); if name == n { - self.visit_seq(Some(len), visitor) + self.visit_tuple_seq(Some(len), visitor) } else { Err(Error::InvalidName(n)) } @@ -557,16 +653,16 @@ impl de::Deserializer for Deserializer } fn deserialize_struct(&mut self, - name: &str, - fields: &'static [&'static str], - visitor: V) -> Result + name: &str, + fields: &'static [&'static str], + visitor: V) -> Result where V: de::Visitor, { match self.tokens.peek() { Some(&Token::StructStart(n, _)) => { self.tokens.next(); if name == n { - self.visit_map(Some(fields.len()), visitor) + self.visit_struct(Some(fields.len()), visitor) } else { Err(Error::InvalidName(n)) } @@ -628,6 +724,96 @@ impl<'a, I> de::SeqVisitor for DeserializerSeqVisitor<'a, I> ////////////////////////////////////////////////////////////////////////// +struct DeserializerTupleSeqVisitor<'a, I: 'a> where I: Iterator> { + de: &'a mut Deserializer, + len: Option, +} + +impl<'a, I> de::SeqVisitor for DeserializerTupleSeqVisitor<'a, I> + where I: Iterator>, +{ + type Error = Error; + + fn visit(&mut self) -> Result, Error> + where T: de::Deserialize, + { + match self.de.tokens.peek() { + Some(&Token::TupleSeqSep) => { + self.de.tokens.next(); + self.len = self.len.map(|len| len - 1); + Ok(Some(try!(de::Deserialize::deserialize(self.de)))) + } + Some(&Token::TupleSeqEnd) => Ok(None), + Some(_) => { + let token = self.de.tokens.next().unwrap(); + Err(Error::UnexpectedToken(token)) + } + None => Err(Error::EndOfStreamError), + } + } + + fn end(&mut self) -> Result<(), Error> { + //assert_eq!(self.len.unwrap_or(0), 0); + match self.de.tokens.next() { + Some(Token::TupleSeqEnd) => Ok(()), + Some(token) => Err(Error::UnexpectedToken(token)), + None => Err(Error::EndOfStreamError), + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len.unwrap_or(0); + (len, self.len) + } +} + +////////////////////////////////////////////////////////////////////////// + +struct DeserializerVariantSeqVisitor<'a, I: 'a> where I: Iterator> { + de: &'a mut Deserializer, + len: Option, +} + +impl<'a, I> de::SeqVisitor for DeserializerVariantSeqVisitor<'a, I> + where I: Iterator>, +{ + type Error = Error; + + fn visit(&mut self) -> Result, Error> + where T: de::Deserialize, + { + match self.de.tokens.peek() { + Some(&Token::EnumSeqSep) => { + self.de.tokens.next(); + self.len = self.len.map(|len| len - 1); + Ok(Some(try!(de::Deserialize::deserialize(self.de)))) + } + Some(&Token::EnumSeqEnd) => Ok(None), + Some(_) => { + let token = self.de.tokens.next().unwrap(); + Err(Error::UnexpectedToken(token)) + } + None => Err(Error::EndOfStreamError), + } + } + + fn end(&mut self) -> Result<(), Error> { + //assert_eq!(self.len.unwrap_or(0), 0); + match self.de.tokens.next() { + Some(Token::EnumSeqEnd) => Ok(()), + Some(token) => Err(Error::UnexpectedToken(token)), + None => Err(Error::EndOfStreamError), + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len.unwrap_or(0); + (len, self.len) + } +} + +////////////////////////////////////////////////////////////////////////// + struct DeserializerMapVisitor<'a, I: 'a> where I: Iterator> { de: &'a mut Deserializer, len: Option, @@ -679,6 +865,57 @@ impl<'a, I> de::MapVisitor for DeserializerMapVisitor<'a, I> ////////////////////////////////////////////////////////////////////////// +struct DeserializerStructVisitor<'a, I: 'a> where I: Iterator> { + de: &'a mut Deserializer, + len: Option, +} + +impl<'a, I> de::MapVisitor for DeserializerStructVisitor<'a, I> + where I: Iterator>, +{ + type Error = Error; + + fn visit_key(&mut self) -> Result, Error> + where K: de::Deserialize, + { + match self.de.tokens.peek() { + Some(&Token::StructSep) => { + self.de.tokens.next(); + self.len = self.len.map(|len| if len > 0 { len - 1} else { 0 }); + Ok(Some(try!(de::Deserialize::deserialize(self.de)))) + } + Some(&Token::StructEnd) => Ok(None), + Some(_) => { + let token = self.de.tokens.next().unwrap(); + Err(Error::UnexpectedToken(token)) + } + None => Err(Error::EndOfStreamError), + } + } + + fn visit_value(&mut self) -> Result + where V: de::Deserialize, + { + Ok(try!(de::Deserialize::deserialize(self.de))) + } + + fn end(&mut self) -> Result<(), Error> { + //assert_eq!(self.len.unwrap_or(0), 0); + match self.de.tokens.next() { + Some(Token::StructEnd) => Ok(()), + Some(token) => Err(Error::UnexpectedToken(token)), + None => Err(Error::EndOfStreamError), + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len.unwrap_or(0); + (len, self.len) + } +} + +////////////////////////////////////////////////////////////////////////// + struct DeserializerVariantVisitor<'a, I: 'a> where I: Iterator> { de: &'a mut Deserializer, } @@ -693,7 +930,7 @@ impl<'a, I> de::VariantVisitor for DeserializerVariantVisitor<'a, I> { match self.de.tokens.peek() { Some(&Token::EnumUnit(_, v)) - | Some(&Token::EnumNewtype(_, v)) + | Some(&Token::EnumNewType(_, v)) | Some(&Token::EnumSeqStart(_, v, _)) | Some(&Token::EnumMapStart(_, v, _)) => { let mut de = ValueDeserializer::::into_deserializer(v); @@ -724,7 +961,7 @@ impl<'a, I> de::VariantVisitor for DeserializerVariantVisitor<'a, I> where T: de::Deserialize, { match self.de.tokens.peek() { - Some(&Token::EnumNewtype(_, _)) => { + Some(&Token::EnumNewType(_, _)) => { self.de.tokens.next(); de::Deserialize::deserialize(self.de) } @@ -744,6 +981,15 @@ impl<'a, I> de::VariantVisitor for DeserializerVariantVisitor<'a, I> Some(&Token::EnumSeqStart(_, _, Some(enum_len))) => { let token = self.de.tokens.next().unwrap(); + if len == enum_len { + self.de.visit_variant_seq(Some(len), visitor) + } else { + Err(Error::UnexpectedToken(token)) + } + } + Some(&Token::SeqStart(Some(enum_len))) => { + let token = self.de.tokens.next().unwrap(); + if len == enum_len { self.de.visit_seq(Some(len), visitor) } else { @@ -766,6 +1012,15 @@ impl<'a, I> de::VariantVisitor for DeserializerVariantVisitor<'a, I> Some(&Token::EnumMapStart(_, _, Some(enum_len))) => { let token = self.de.tokens.next().unwrap(); + if fields.len() == enum_len { + self.de.visit_variant_map(Some(fields.len()), visitor) + } else { + Err(Error::UnexpectedToken(token)) + } + } + Some(&Token::MapStart(Some(enum_len))) => { + let token = self.de.tokens.next().unwrap(); + if fields.len() == enum_len { self.de.visit_map(Some(fields.len()), visitor) } else { @@ -780,6 +1035,57 @@ impl<'a, I> de::VariantVisitor for DeserializerVariantVisitor<'a, I> } } +////////////////////////////////////////////////////////////////////////// + +struct DeserializerVariantMapVisitor<'a, I: 'a> where I: Iterator> { + de: &'a mut Deserializer, + len: Option, +} + +impl<'a, I> de::MapVisitor for DeserializerVariantMapVisitor<'a, I> + where I: Iterator>, +{ + type Error = Error; + + fn visit_key(&mut self) -> Result, Error> + where K: de::Deserialize, + { + match self.de.tokens.peek() { + Some(&Token::EnumMapSep) => { + self.de.tokens.next(); + self.len = self.len.map(|len| if len > 0 { len - 1} else { 0 }); + Ok(Some(try!(de::Deserialize::deserialize(self.de)))) + } + Some(&Token::EnumMapEnd) => Ok(None), + Some(_) => { + let token = self.de.tokens.next().unwrap(); + Err(Error::UnexpectedToken(token)) + } + None => Err(Error::EndOfStreamError), + } + } + + fn visit_value(&mut self) -> Result + where V: de::Deserialize, + { + Ok(try!(de::Deserialize::deserialize(self.de))) + } + + fn end(&mut self) -> Result<(), Error> { + //assert_eq!(self.len.unwrap_or(0), 0); + match self.de.tokens.next() { + Some(Token::EnumMapEnd) => Ok(()), + Some(token) => Err(Error::UnexpectedToken(token)), + None => Err(Error::EndOfStreamError), + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len.unwrap_or(0); + (len, self.len) + } +} + ////////////////////////////////////////////////////////////////////////////// pub fn assert_ser_tokens(value: &T, tokens: &[Token]) From 001cb7ab011250e4e417de5ec5fd39aa1a91f5c9 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 15 Feb 2016 17:39:46 -0800 Subject: [PATCH 4/9] feat(codegen): Add `#[serde(serialize_with="...")]` This allows a field to be serialized with an expression instead of the default serializer. This simplifies serializing a struct or enum that contains an external type that doesn't implement `serde::Serialize`. This expression is passed a variable `serializer` that needs to be used to serialize the expression. --- serde_codegen/src/attr.rs | 70 +++++++++++++++ serde_codegen/src/ser.rs | 7 +- serde_tests/tests/test_annotations.rs | 120 ++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index b440af7e..1ef63530 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -170,6 +170,7 @@ pub struct FieldAttrs { skip_serializing_field_if_empty: bool, skip_serializing_field_if_none: bool, default_expr_if_missing: Option>, + serialize_with: Option>, } impl FieldAttrs { @@ -194,6 +195,7 @@ impl FieldAttrs { skip_serializing_field_if_empty: false, skip_serializing_field_if_none: false, default_expr_if_missing: None, + serialize_with: None, }; for meta_items in field.node.attrs.iter().filter_map(get_serde_meta_items) { @@ -257,6 +259,18 @@ impl FieldAttrs { field_attrs.skip_serializing_field_if_empty = true; } + // Parse `#[serde(serialize_with="...")]` + ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => { + let expr = wrap_serialize_with( + cx, + container_ty, + generics, + try!(parse_lit_into_expr(cx, name, lit)), + ); + + field_attrs.serialize_with = Some(expr); + } + _ => { cx.span_err( meta_item.span, @@ -324,6 +338,10 @@ impl FieldAttrs { pub fn skip_serializing_field_if_none(&self) -> bool { self.skip_serializing_field_if_none } + + pub fn serialize_with(&self) -> Option<&P> { + self.serialize_with.as_ref() + } } @@ -445,3 +463,55 @@ fn wrap_skip_serializing(cx: &ExtCtxt, self.value.__serde_should_skip_serializing() }) } + +/// This function wraps the expression in `#[serde(serialize_with="...")]` in a trait to +/// prevent it from accessing the internal `Serialize` state. +fn wrap_serialize_with(cx: &ExtCtxt, + container_ty: &P, + generics: &ast::Generics, + expr: P) -> P { + let where_clause = &generics.where_clause; + + quote_expr!(cx, { + trait __SerdeSerializeWith { + fn __serde_serialize_with(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ::serde::ser::Serializer; + } + + impl<'a, T> __SerdeSerializeWith for &'a T + where T: 'a + __SerdeSerializeWith, + { + fn __serde_serialize_with(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ::serde::ser::Serializer + { + (**self).__serde_serialize_with(serializer) + } + } + + impl $generics __SerdeSerializeWith for $container_ty $where_clause { + fn __serde_serialize_with(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ::serde::ser::Serializer + { + $expr + } + } + + struct __SerdeSerializeWithStruct<'a, T: 'a> { + value: &'a T, + } + + impl<'a, T> ::serde::ser::Serialize for __SerdeSerializeWithStruct<'a, T> + where T: 'a + __SerdeSerializeWith + { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ::serde::ser::Serializer + { + self.value.__serde_serialize_with(serializer) + } + } + + __SerdeSerializeWithStruct { + value: &self.value, + } + }) +} diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index a604d298..fdf42883 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -672,8 +672,13 @@ fn serialize_struct_visitor( } }; + let field_expr = match field_attr.serialize_with() { + Some(expr) => expr.clone(), + None => quote_expr!(cx, &self.value.$name), + }; + let expr = quote_expr!(cx, - serializer.$serializer_method($key_expr, &self.value.$name) + serializer.$serializer_method($key_expr, $field_expr) ); quote_arm!(cx, diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 831c32cd..bd640a62 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -1,3 +1,6 @@ +use std::default::Default; +use serde::{Serialize, Serializer}; + use token::{ Error, Token, @@ -11,12 +14,25 @@ trait Trait { fn my_default() -> Self; fn should_skip(&self) -> bool; + + fn serialize_with(&self, ser: &mut S) -> Result<(), S::Error> + where S: Serializer; } impl Trait for i32 { fn my_default() -> Self { 123 } fn should_skip(&self) -> bool { *self == 123 } + + fn serialize_with(&self, ser: &mut S) -> Result<(), S::Error> + where S: Serializer + { + if *self == 123 { + true.serialize(ser) + } else { + false.serialize(ser) + } + } } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -502,3 +518,107 @@ fn test_skip_serializing_enum() { ] ); } + +#[derive(Debug, PartialEq, Serialize)] +struct SerializeWithStruct<'a, B> where B: Trait { + a: &'a i8, + #[serde(serialize_with="self.b.serialize_with(serializer)")] + b: B, +} + +#[test] +fn test_serialize_with_struct() { + let a = 1; + assert_ser_tokens( + &SerializeWithStruct { + a: &a, + b: 2, + }, + &[ + Token::StructStart("SerializeWithStruct", Some(2)), + + Token::StructSep, + Token::Str("a"), + Token::I8(1), + + Token::StructSep, + Token::Str("b"), + Token::Bool(false), + + Token::StructEnd, + ] + ); + + assert_ser_tokens( + &SerializeWithStruct { + a: &a, + b: 123, + }, + &[ + Token::StructStart("SerializeWithStruct", Some(2)), + + Token::StructSep, + Token::Str("a"), + Token::I8(1), + + Token::StructSep, + Token::Str("b"), + Token::Bool(true), + + Token::StructEnd, + ] + ); +} + +#[derive(Debug, PartialEq, Serialize)] +enum SerializeWithEnum<'a, B> where B: Trait { + Struct { + a: &'a i8, + #[serde(serialize_with="self.b.serialize_with(serializer)")] + b: B, + } +} + +#[test] +fn test_serialize_with_enum() { + let a = 1; + assert_ser_tokens( + &SerializeWithEnum::Struct { + a: &a, + b: 2, + }, + &[ + Token::EnumMapStart("SerializeWithEnum", "Struct", Some(2)), + + Token::EnumMapSep, + Token::Str("a"), + Token::I8(1), + + Token::EnumMapSep, + Token::Str("b"), + Token::Bool(false), + + Token::EnumMapEnd, + ] + ); + + assert_ser_tokens( + &SerializeWithEnum::Struct { + a: &a, + b: 123, + }, + &[ + Token::EnumMapStart("SerializeWithEnum", "Struct", Some(2)), + + Token::EnumMapSep, + Token::Str("a"), + Token::I8(1), + + Token::EnumMapSep, + Token::Str("b"), + Token::Bool(true), + + Token::EnumMapEnd, + ] + ); +} From 40f8e6b061250d2d78d54698739b0e40ebe28c24 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 15 Feb 2016 20:43:11 -0800 Subject: [PATCH 5/9] feat(codegen): Add `#[serde(deserialize_with="...")]` This allows a field to be deserialized with an expression instead of the default deserializer. This simplifies deserializing a struct or enum that contains an external type that doesn't implement `serde::Deserialize`. This expression is passed a variable `deserializer` that needs to be used to deserialize the expression. --- serde_codegen/src/attr.rs | 73 ++++++++++++++++ serde_codegen/src/de.rs | 30 ++++--- serde_tests/tests/test_annotations.rs | 119 +++++++++++++++++++++++++- 3 files changed, 207 insertions(+), 15 deletions(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 1ef63530..975ca303 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -171,6 +171,7 @@ pub struct FieldAttrs { skip_serializing_field_if_none: bool, default_expr_if_missing: Option>, serialize_with: Option>, + deserialize_with: Option>, } impl FieldAttrs { @@ -196,6 +197,7 @@ impl FieldAttrs { skip_serializing_field_if_none: false, default_expr_if_missing: None, serialize_with: None, + deserialize_with: None, }; for meta_items in field.node.attrs.iter().filter_map(get_serde_meta_items) { @@ -271,6 +273,18 @@ impl FieldAttrs { field_attrs.serialize_with = Some(expr); } + // Parse `#[serde(deserialize_with="...")]` + ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => { + let expr = wrap_deserialize_with( + cx, + &field.node.ty, + generics, + try!(parse_lit_into_expr(cx, name, lit)), + ); + + field_attrs.deserialize_with = Some(expr); + } + _ => { cx.span_err( meta_item.span, @@ -342,6 +356,10 @@ impl FieldAttrs { pub fn serialize_with(&self) -> Option<&P> { self.serialize_with.as_ref() } + + pub fn deserialize_with(&self) -> Option<&P> { + self.deserialize_with.as_ref() + } } @@ -515,3 +533,58 @@ fn wrap_serialize_with(cx: &ExtCtxt, } }) } + +/// This function wraps the expression in `#[serde(deserialize_with="...")]` in a trait to prevent +/// it from accessing the internal `Deserialize` state. +fn wrap_deserialize_with(cx: &ExtCtxt, + field_ty: &P, + generics: &ast::Generics, + expr: P) -> P { + let builder = AstBuilder::new(); + + let fn_generics = builder.from_generics(generics.clone()) + .ty_param("__D") + .bound() + .trait_( + builder.path() + .global() + .ids(&["serde", "de", "Deserializer"]) + .build() + ) + .build() + .build() + .build(); + + // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. + let ty_path = AstBuilder::new().path() + .segment("__SerdeDeserializeWithStruct") + .with_generics(generics.clone()) + .build() + .build(); + + let fn_where_clause = &fn_generics.where_clause; + let where_clause = &generics.where_clause; + + quote_expr!(cx, { + fn __serde_deserialize_with $fn_generics(deserializer: &mut __D) + -> Result<$field_ty, __D::Error> $fn_where_clause { + $expr + } + + struct __SerdeDeserializeWithStruct $generics $where_clause { + value: $field_ty, + } + + impl $generics ::serde::de::Deserialize for $ty_path $where_clause { + fn deserialize(deserializer: &mut D) -> Result + where D: ::serde::de::Deserializer + { + let value = try!(__serde_deserialize_with(deserializer)); + Ok(__SerdeDeserializeWithStruct { value: value }) + } + } + + let value: $ty_path = try!(visitor.visit_value()); + Ok(value.value) + }) +} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index e95fe2dc..14174542 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -991,6 +991,12 @@ fn deserialize_map( .map(|i| builder.id(format!("__field{}", i))) .collect(); + let field_attrs: Vec<_> = try!( + fields.iter() + .map(|field| attr::FieldAttrs::from_field(cx, container_ty, generics, field)) + .collect() + ); + // Declare each field. let let_values: Vec = field_names.iter() .map(|field_name| quote_stmt!(cx, let mut $field_name = None;).unwrap()) @@ -1007,26 +1013,24 @@ fn deserialize_map( }; // Match arms to extract a value for a field. - let value_arms: Vec = field_names.iter() - .map(|field_name| { + let value_arms = field_attrs.iter().zip(field_names.iter()) + .map(|(field_attr, field_name)| { + let expr = match field_attr.deserialize_with() { + Some(expr) => expr.clone(), + None => quote_expr!(cx, visitor.visit_value()), + }; + quote_arm!(cx, __Field::$field_name => { - $field_name = Some(try!(visitor.visit_value())); + $field_name = Some(try!($expr)); } ) }) .chain(ignored_arm.into_iter()) - .collect(); + .collect::>(); - let extract_values = fields.iter() - .zip(field_names.iter()) - .map(|(field, field_name)| { - let field_attr = try!( - attr::FieldAttrs::from_field(cx, - container_ty, - generics, - field) - ); + let extract_values = field_attrs.iter().zip(field_names.iter()) + .map(|(field_attr, field_name)| { let missing_expr = field_attr.expr_is_missing(); Ok(quote_stmt!(cx, diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index bd640a62..5521c1d0 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -1,5 +1,5 @@ use std::default::Default; -use serde::{Serialize, Serializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; use token::{ Error, @@ -10,13 +10,16 @@ use token::{ assert_de_tokens_error }; -trait Trait { +trait Trait: Sized { fn my_default() -> Self; fn should_skip(&self) -> bool; fn serialize_with(&self, ser: &mut S) -> Result<(), S::Error> where S: Serializer; + + fn deserialize_with(de: &mut D) -> Result + where D: Deserializer; } impl Trait for i32 { @@ -33,6 +36,16 @@ impl Trait for i32 { false.serialize(ser) } } + + fn deserialize_with(de: &mut D) -> Result + where D: Deserializer + { + if try!(Deserialize::deserialize(de)) { + Ok(123) + } else { + Ok(2) + } + } } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -622,3 +635,105 @@ fn test_serialize_with_enum() { ] ); } + +#[derive(Debug, PartialEq, Deserialize)] +struct DeserializeWithStruct where B: Trait { + a: i8, + #[serde(deserialize_with="Trait::deserialize_with(deserializer)")] + b: B, +} + +#[test] +fn test_deserialize_with_struct() { + assert_de_tokens( + &DeserializeWithStruct { + a: 1, + b: 2, + }, + vec![ + Token::StructStart("DeserializeWithStruct", Some(2)), + + Token::StructSep, + Token::Str("a"), + Token::I8(1), + + Token::StructSep, + Token::Str("b"), + Token::Bool(false), + + Token::StructEnd, + ] + ); + + assert_de_tokens( + &DeserializeWithStruct { + a: 1, + b: 123, + }, + vec![ + Token::StructStart("DeserializeWithStruct", Some(2)), + + Token::StructSep, + Token::Str("a"), + Token::I8(1), + + Token::StructSep, + Token::Str("b"), + Token::Bool(true), + + Token::StructEnd, + ] + ); +} + +#[derive(Debug, PartialEq, Deserialize)] +enum DeserializeWithEnum where B: Trait { + Struct { + a: i8, + #[serde(deserialize_with="Trait::deserialize_with(deserializer)")] + b: B, + } +} + +#[test] +fn test_deserialize_with_enum() { + assert_de_tokens( + &DeserializeWithEnum::Struct { + a: 1, + b: 2, + }, + vec![ + Token::EnumMapStart("DeserializeWithEnum", "Struct", Some(2)), + + Token::EnumMapSep, + Token::Str("a"), + Token::I8(1), + + Token::EnumMapSep, + Token::Str("b"), + Token::Bool(false), + + Token::EnumMapEnd, + ] + ); + + assert_de_tokens( + &DeserializeWithEnum::Struct { + a: 1, + b: 123, + }, + vec![ + Token::EnumMapStart("DeserializeWithEnum", "Struct", Some(2)), + + Token::EnumMapSep, + Token::Str("a"), + Token::I8(1), + + Token::EnumMapSep, + Token::Str("b"), + Token::Bool(true), + + Token::EnumMapEnd, + ] + ); +} From c268eb22580dffd63f3f8228cd81264188c396d4 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 18 Feb 2016 19:31:43 -0800 Subject: [PATCH 6/9] feat(codegen): Remove #[serde(skip_serializing_if_{none,empty})] `#[serde(skip_serializing_if="...")]` can replace this functionality. --- serde_codegen/src/attr.rs | 22 ------------ serde_codegen/src/ser.rs | 44 +++++++----------------- serde_tests/tests/test_annotations.rs | 48 +++------------------------ 3 files changed, 16 insertions(+), 98 deletions(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 975ca303..cb95014f 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -167,8 +167,6 @@ pub struct FieldAttrs { deserialize_name: Option, skip_serializing_field: bool, skip_serializing_field_if: Option>, - skip_serializing_field_if_empty: bool, - skip_serializing_field_if_none: bool, default_expr_if_missing: Option>, serialize_with: Option>, deserialize_with: Option>, @@ -193,8 +191,6 @@ impl FieldAttrs { deserialize_name: None, skip_serializing_field: false, skip_serializing_field_if: None, - skip_serializing_field_if_empty: false, - skip_serializing_field_if_none: false, default_expr_if_missing: None, serialize_with: None, deserialize_with: None, @@ -251,16 +247,6 @@ impl FieldAttrs { field_attrs.skip_serializing_field_if = Some(expr); } - // Parse `#[serde(skip_serializing_if_none)]` - ast::MetaItemKind::Word(ref name) if name == &"skip_serializing_if_none" => { - field_attrs.skip_serializing_field_if_none = true; - } - - // Parse `#[serde(skip_serializing_if_empty)]` - ast::MetaItemKind::Word(ref name) if name == &"skip_serializing_if_empty" => { - field_attrs.skip_serializing_field_if_empty = true; - } - // Parse `#[serde(serialize_with="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => { let expr = wrap_serialize_with( @@ -345,14 +331,6 @@ impl FieldAttrs { self.skip_serializing_field_if.as_ref() } - pub fn skip_serializing_field_if_empty(&self) -> bool { - self.skip_serializing_field_if_empty - } - - pub fn skip_serializing_field_if_none(&self) -> bool { - self.skip_serializing_field_if_none - } - pub fn serialize_with(&self) -> Option<&P> { self.serialize_with.as_ref() } diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index fdf42883..de1693e9 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -657,19 +657,10 @@ fn serialize_struct_visitor( let key_expr = field_attr.serialize_name_expr(); - let stmt = match field_attr.skip_serializing_field_if() { - Some(expr) => { + let stmt = if let Some(expr) = field_attr.skip_serializing_field_if() { Some(quote_stmt!(cx, if $expr { continue; })) - } - None => { - if field_attr.skip_serializing_field_if_empty() { - Some(quote_stmt!(cx, if self.value.$name.is_empty() { continue; })) - } else if field_attr.skip_serializing_field_if_none() { - Some(quote_stmt!(cx, if self.value.$name.is_none() { continue; })) - } else { - None - } - } + } else { + None }; let field_expr = match field_attr.serialize_with() { @@ -702,26 +693,15 @@ fn serialize_struct_visitor( .strip_bounds() .build(); - let len = fields.iter().zip(field_attrs.iter()) - .map(|(field, field_attr)| { - if field_attr.skip_serializing_field() { - quote_expr!(cx, 0) - } else { - match field_attr.skip_serializing_field_if() { - Some(expr) => { - quote_expr!(cx, if $expr { 0 } else { 1 }) - } - None => { - let name = field.node.ident().expect("struct has unnamed field"); - - if field_attr.skip_serializing_field_if_empty() { - quote_expr!(cx, if self.value.$name.is_empty() { 0 } else { 1 }) - } else if field_attr.skip_serializing_field_if_none() { - quote_expr!(cx, if self.value.$name.is_none() { 0 } else { 1 }) - } else { - quote_expr!(cx, 1) - } - } + let len = field_attrs.iter() + .filter(|field_attr| !field_attr.skip_serializing_field()) + .map(|field_attr| { + match field_attr.skip_serializing_field_if() { + Some(expr) => { + quote_expr!(cx, if $expr { 0 } else { 1 }) + } + None => { + quote_expr!(cx, 1) } } }) diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 5521c1d0..031b6bb5 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -389,16 +389,12 @@ fn test_rename_enum() { } #[derive(Debug, PartialEq, Serialize)] -struct SkipSerializingStruct<'a, B, C, D, E> where C: Trait { +struct SkipSerializingStruct<'a, B, C> where C: Trait { a: &'a i8, #[serde(skip_serializing)] b: B, #[serde(skip_serializing_if="self.c.should_skip()")] c: C, - #[serde(skip_serializing_if_none)] - d: Option, - #[serde(skip_serializing_if_empty)] - e: Vec, } #[test] @@ -409,11 +405,9 @@ fn test_skip_serializing_struct() { a: &a, b: 2, c: 3, - d: Some(4), - e: vec![5], }, &[ - Token::StructStart("SkipSerializingStruct", Some(4)), + Token::StructStart("SkipSerializingStruct", Some(2)), Token::StructSep, Token::Str("a"), @@ -423,18 +417,6 @@ fn test_skip_serializing_struct() { Token::Str("c"), Token::I32(3), - Token::StructSep, - Token::Str("d"), - Token::Option(true), - Token::I32(4), - - Token::StructSep, - Token::Str("e"), - Token::SeqStart(Some(1)), - Token::SeqSep, - Token::I32(5), - Token::SeqEnd, - Token::StructEnd, ] ); @@ -444,8 +426,6 @@ fn test_skip_serializing_struct() { a: &a, b: 2, c: 123, - d: None::, - e: Vec::::new(), }, &[ Token::StructStart("SkipSerializingStruct", Some(1)), @@ -460,17 +440,13 @@ fn test_skip_serializing_struct() { } #[derive(Debug, PartialEq, Serialize)] -enum SkipSerializingEnum<'a, B, C, D, E> where C: Trait { +enum SkipSerializingEnum<'a, B, C> where C: Trait { Struct { a: &'a i8, #[serde(skip_serializing)] _b: B, #[serde(skip_serializing_if="self.c.should_skip()")] c: C, - #[serde(skip_serializing_if_none)] - d: Option, - #[serde(skip_serializing_if_empty)] - e: Vec, } } @@ -482,11 +458,9 @@ fn test_skip_serializing_enum() { a: &a, _b: 2, c: 3, - d: Some(4), - e: vec![5], }, &[ - Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(4)), + Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(2)), Token::EnumMapSep, Token::Str("a"), @@ -496,18 +470,6 @@ fn test_skip_serializing_enum() { Token::Str("c"), Token::I32(3), - Token::EnumMapSep, - Token::Str("d"), - Token::Option(true), - Token::I32(4), - - Token::EnumMapSep, - Token::Str("e"), - Token::SeqStart(Some(1)), - Token::SeqSep, - Token::I32(5), - Token::SeqEnd, - Token::EnumMapEnd, ] ); @@ -517,8 +479,6 @@ fn test_skip_serializing_enum() { a: &a, _b: 2, c: 123, - d: None::, - e: Vec::::new(), }, &[ Token::EnumMapStart("SkipSerializingEnum", "Struct", Some(1)), From dbd05ae9cc18753f1ae090b9f60c691e5efc2804 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 18 Feb 2016 19:32:10 -0800 Subject: [PATCH 7/9] doc(annotations): Add docs for the latest documentation --- README.md | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 722e1d6c..b4c21952 100644 --- a/README.md +++ b/README.md @@ -670,22 +670,36 @@ Annotations `serde_codegen` and `serde_macros` support annotations that help to customize how types are serialized. Here are the supported annotations: +Container Annotations: + +| Annotation | Function | +| ---------- | -------- | +| `#[serde(rename="name")` | Serialize and deserialize this container with the given name | +| `#[serde(rename(serialize="name1"))` | Serialize this container with the given name | +| `#[serde(rename(deserialize="name1"))` | Deserialize this container with the given name | +| `#[serde(deny_unknown_fields)` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. | + +Variant Annotations: + +| Annotation | Function | +| ---------- | -------- | +| `#[serde(rename="name")` | Serialize and deserialize this variant with the given name | +| `#[serde(rename(serialize="name1"))` | Serialize this variant with the given name | +| `#[serde(rename(deserialize="name1"))` | Deserialize this variant with the given name | + Field Annotations: -| Annotation | Function | -| ---------- | -------- | -| `#[serde(rename(json="name1", xml="name2"))` | Serialize this field with the given name for the given formats | -| `#[serde(default)` | If the value is not specified, use the `Default::default()` | -| `#[serde(rename="name")` | Serialize this field with the given name | -| `#[serde(skip_serializing)` | Do not serialize this value | -| `#[serde(skip_serializing_if_empty)` | Do not serialize this value if `$value.is_empty()` is `true` | -| `#[serde(skip_serializing_if_none)` | Do not serialize this value if `$value.is_none()` is `true` | - -Structure Annotations: - -| Annotation | Function | -| ---------- | -------- | -| `#[serde(deny_unknown_fields)` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. | +| Annotation | Function | +| ---------- | -------- | +| `#[serde(rename="name")` | Serialize and deserialize this field with the given name | +| `#[serde(rename(serialize="name1"))` | Serialize this field with the given name | +| `#[serde(rename(deserialize="name1"))` | Deserialize this field with the given name | +| `#[serde(default)` | If the value is not specified, use the `Default::default()` | +| `#[serde(default="$expr")` | If the value is not specified, use the `$expr` expression | +| `#[serde(skip_serializing)` | Do not serialize this value | +| `#[serde(skip_serializing_if="$expr")` | Do not serialize this value if the `$expr` expression returns true | +| `#[serde(serialize_with="$expr")` | Use the `$expr` expression to serialize this field | +| `#[serde(deserialize_with="$expr")` | Use the `$expr` expression to deserialize this field | Serialization Formats Using Serde From 0c35ce0a4f2d29fe0d4bd1f8145713643605b368 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 21 Feb 2016 09:54:03 -0800 Subject: [PATCH 8/9] feat(codegen): Improve error messages in annotations --- serde_codegen/src/attr.rs | 99 +++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index cb95014f..a5b13c38 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -1,8 +1,12 @@ -use syntax::ast; +use std::rc::Rc; +use syntax::ast::{self, TokenTree}; use syntax::attr; +use syntax::codemap::Span; use syntax::ext::base::ExtCtxt; -use syntax::print::pprust::{lit_to_string, meta_item_to_string}; +use syntax::fold::Folder; +use syntax::parse::token; use syntax::parse; +use syntax::print::pprust::{lit_to_string, meta_item_to_string}; use syntax::ptr::P; use aster::AstBuilder; @@ -390,13 +394,60 @@ fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P]> { } } +/// This syntax folder rewrites tokens to say their spans are coming from a macro context. +struct Respanner<'a, 'b: 'a> { + cx: &'a ExtCtxt<'b>, +} + +impl<'a, 'b> Folder for Respanner<'a, 'b> { + fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree { + match *tt { + TokenTree::Token(span, ref tok) => { + TokenTree::Token( + self.new_span(span), + self.fold_token(tok.clone()) + ) + } + TokenTree::Delimited(span, ref delimed) => { + TokenTree::Delimited( + self.new_span(span), + Rc::new(ast::Delimited { + delim: delimed.delim, + open_span: delimed.open_span, + tts: self.fold_tts(&delimed.tts), + close_span: delimed.close_span, + }) + ) + } + TokenTree::Sequence(span, ref seq) => { + TokenTree::Sequence( + self.new_span(span), + Rc::new(ast::SequenceRepetition { + tts: self.fold_tts(&seq.tts), + separator: seq.separator.clone().map(|tok| self.fold_token(tok)), + ..**seq + }) + ) + } + } + } + + fn new_span(&mut self, span: Span) -> Span { + Span { + lo: span.lo, + hi: span.hi, + expn_id: self.cx.backtrace(), + } + } +} + fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result, Error> { - let s: &str = match lit.node { - ast::LitKind::Str(ref s, ast::StrStyle::Cooked) => &s, + let source: &str = match lit.node { + ast::LitKind::Str(ref source, _) => &source, _ => { cx.span_err( lit.span, - &format!("{} literal `{}` must be a string", + &format!("serde annotation `{}` must be a string, not `{}`", name, lit_to_string(lit))); @@ -404,10 +455,40 @@ fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result".to_string(), - s.to_owned(), - cx.cfg(), - cx.parse_sess()); + // If we just parse the string into an expression, any syntax errors in the source will only + // have spans that point inside the string, and not back to the attribute. So to have better + // error reporting, we'll first parse the string into a token tree. Then we'll update those + // spans to say they're coming from a macro context that originally came from the attribute, + // and then finally parse them into an expression. + let tts = parse::parse_tts_from_source_str( + format!("", name), + source.to_owned(), + cx.cfg(), + cx.parse_sess()); + + // Respan the spans to say they are all coming from this macro. + let tts = Respanner { cx: cx }.fold_tts(&tts); + + let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); + + let expr = parser.parse_expr(); + + let expr = match expr { + Ok(expr) => expr, + Err(mut e) => { + e.emit(); + return Err(Error); + } + }; + + // Make sure to error out if there are trailing characters in the stream. + match parser.expect(&token::Eof) { + Ok(()) => { } + Err(mut e) => { + e.emit(); + return Err(Error); + } + } Ok(expr) } From 78cf29d1d1d72fbbd8d64b9a6bbe0ddfa6823618 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 21 Feb 2016 15:28:25 -0800 Subject: [PATCH 9/9] feat(codegen): Switch attributes to using using paths not expressions --- README.md | 23 ++--- serde_codegen/src/attr.rs | 143 ++++++++++++-------------- serde_codegen/src/de.rs | 12 ++- serde_codegen/src/ser.rs | 5 +- serde_tests/tests/test_annotations.rs | 16 +-- 5 files changed, 95 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index b4c21952..58f152e3 100644 --- a/README.md +++ b/README.md @@ -689,18 +689,17 @@ Variant Annotations: Field Annotations: -| Annotation | Function | -| ---------- | -------- | -| `#[serde(rename="name")` | Serialize and deserialize this field with the given name | -| `#[serde(rename(serialize="name1"))` | Serialize this field with the given name | -| `#[serde(rename(deserialize="name1"))` | Deserialize this field with the given name | -| `#[serde(default)` | If the value is not specified, use the `Default::default()` | -| `#[serde(default="$expr")` | If the value is not specified, use the `$expr` expression | -| `#[serde(skip_serializing)` | Do not serialize this value | -| `#[serde(skip_serializing_if="$expr")` | Do not serialize this value if the `$expr` expression returns true | -| `#[serde(serialize_with="$expr")` | Use the `$expr` expression to serialize this field | -| `#[serde(deserialize_with="$expr")` | Use the `$expr` expression to deserialize this field | - +| Annotation | Function | +| ---------- | -------- | +| `#[serde(rename="name")` | Serialize and deserialize this field with the given name | +| `#[serde(rename(serialize="name1"))` | Serialize this field with the given name | +| `#[serde(rename(deserialize="name1"))` | Deserialize this field with the given name | +| `#[serde(default)` | If the value is not specified, use the `Default::default()` | +| `#[serde(default="$path")` | Call the path to a function `fn() -> T` to build the value | +| `#[serde(skip_serializing)` | Do not serialize this value | +| `#[serde(skip_serializing_if="$path")` | Do not serialize this value if this function `fn(&T) -> bool` returns `false` | +| `#[serde(serialize_with="$path")` | Call a function `fn(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value | +| `#[serde(deserialize_with="$path")` | Call a function `fn(&mut D) -> Result where D: Deserializer` to deserialize this value | Serialization Formats Using Serde ================================= diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index a5b13c38..a082fcbf 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -4,6 +4,7 @@ use syntax::attr; use syntax::codemap::Span; use syntax::ext::base::ExtCtxt; use syntax::fold::Folder; +use syntax::parse::parser::PathParsingMode; use syntax::parse::token; use syntax::parse; use syntax::print::pprust::{lit_to_string, meta_item_to_string}; @@ -181,7 +182,8 @@ impl FieldAttrs { pub fn from_field(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, - field: &ast::StructField) -> Result { + field: &ast::StructField, + is_enum: bool) -> Result { let builder = AstBuilder::new(); let field_ident = match field.node.ident() { @@ -225,10 +227,7 @@ impl FieldAttrs { // Parse `#[serde(default="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { let wrapped_expr = wrap_default( - cx, - &field.node.ty, - generics, - try!(parse_lit_into_expr(cx, name, lit)), + try!(parse_lit_into_path(cx, name, lit)), ); field_attrs.default_expr_if_missing = Some(wrapped_expr); @@ -242,10 +241,9 @@ impl FieldAttrs { // Parse `#[serde(skip_serializing_if="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => { let expr = wrap_skip_serializing( - cx, - container_ty, - generics, - try!(parse_lit_into_expr(cx, name, lit)), + field_ident, + try!(parse_lit_into_path(cx, name, lit)), + is_enum, ); field_attrs.skip_serializing_field_if = Some(expr); @@ -257,7 +255,9 @@ impl FieldAttrs { cx, container_ty, generics, - try!(parse_lit_into_expr(cx, name, lit)), + field_ident, + try!(parse_lit_into_path(cx, name, lit)), + is_enum, ); field_attrs.serialize_with = Some(expr); @@ -269,7 +269,7 @@ impl FieldAttrs { cx, &field.node.ty, generics, - try!(parse_lit_into_expr(cx, name, lit)), + try!(parse_lit_into_path(cx, name, lit)), ); field_attrs.deserialize_with = Some(expr); @@ -349,9 +349,10 @@ impl FieldAttrs { pub fn get_struct_field_attrs(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, - fields: &[ast::StructField]) -> Result, Error> { + fields: &[ast::StructField], + is_enum: bool) -> Result, Error> { fields.iter() - .map(|field| FieldAttrs::from_field(cx, container_ty, generics, field)) + .map(|field| FieldAttrs::from_field(cx, container_ty, generics, field, is_enum)) .collect() } @@ -441,7 +442,7 @@ impl<'a, 'b> Folder for Respanner<'a, 'b> { } } -fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result, Error> { +fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { let source: &str = match lit.node { ast::LitKind::Str(ref source, _) => &source, _ => { @@ -471,10 +472,8 @@ fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result expr, + let path = match parser.parse_path(PathParsingMode::LifetimeAndTypesWithoutColons) { + Ok(path) => path, Err(mut e) => { e.emit(); return Err(Error); @@ -490,55 +489,39 @@ fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result, - generics: &ast::Generics, - expr: P) -> P { - let builder = AstBuilder::new(); - - // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. - let fn_path = builder.path() - .segment("__serde_default") - .with_generics(generics.clone()) - .build() - .build(); - - let where_clause = &generics.where_clause; - - quote_expr!(cx, { - fn __serde_default $generics() -> $field_ty $where_clause { - $expr - } - $fn_path() - }) +fn wrap_default(path: ast::Path) -> P { + AstBuilder::new().expr().call() + .build_path(path) + .build() } /// This function wraps the expression in `#[serde(skip_serializing_if="...")]` in a trait to /// prevent it from accessing the internal `Serialize` state. -fn wrap_skip_serializing(cx: &ExtCtxt, - container_ty: &P, - generics: &ast::Generics, - expr: P) -> P { - let where_clause = &generics.where_clause; +fn wrap_skip_serializing(field_ident: ast::Ident, + path: ast::Path, + is_enum: bool) -> P { + let builder = AstBuilder::new(); - quote_expr!(cx, { - trait __SerdeShouldSkipSerializing { - fn __serde_should_skip_serializing(&self) -> bool; - } + let expr = builder.expr() + .field(field_ident) + .field("value") + .self_(); - impl $generics __SerdeShouldSkipSerializing for $container_ty $where_clause { - fn __serde_should_skip_serializing(&self) -> bool { - $expr - } - } + let expr = if is_enum { + expr + } else { + builder.expr().ref_().build(expr) + }; - self.value.__serde_should_skip_serializing() - }) + builder.expr().call() + .build_path(path) + .arg().build(expr) + .build() } /// This function wraps the expression in `#[serde(serialize_with="...")]` in a trait to @@ -546,7 +529,28 @@ fn wrap_skip_serializing(cx: &ExtCtxt, fn wrap_serialize_with(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, - expr: P) -> P { + field_ident: ast::Ident, + path: ast::Path, + is_enum: bool) -> P { + let builder = AstBuilder::new(); + + let expr = builder.expr() + .field(field_ident) + .self_(); + + let expr = if is_enum { + expr + } else { + builder.expr().ref_().build(expr) + }; + + let expr = builder.expr().call() + .build_path(path) + .arg().build(expr) + .arg() + .id("serializer") + .build(); + let where_clause = &generics.where_clause; quote_expr!(cx, { @@ -598,22 +602,7 @@ fn wrap_serialize_with(cx: &ExtCtxt, fn wrap_deserialize_with(cx: &ExtCtxt, field_ty: &P, generics: &ast::Generics, - expr: P) -> P { - let builder = AstBuilder::new(); - - let fn_generics = builder.from_generics(generics.clone()) - .ty_param("__D") - .bound() - .trait_( - builder.path() - .global() - .ids(&["serde", "de", "Deserializer"]) - .build() - ) - .build() - .build() - .build(); - + path: ast::Path) -> P { // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. let ty_path = AstBuilder::new().path() .segment("__SerdeDeserializeWithStruct") @@ -621,15 +610,9 @@ fn wrap_deserialize_with(cx: &ExtCtxt, .build() .build(); - let fn_where_clause = &fn_generics.where_clause; let where_clause = &generics.where_clause; quote_expr!(cx, { - fn __serde_deserialize_with $fn_generics(deserializer: &mut __D) - -> Result<$field_ty, __D::Error> $fn_where_clause { - $expr - } - struct __SerdeDeserializeWithStruct $generics $where_clause { value: $field_ty, } @@ -638,7 +621,7 @@ fn wrap_deserialize_with(cx: &ExtCtxt, fn deserialize(deserializer: &mut D) -> Result where D: ::serde::de::Deserializer { - let value = try!(__serde_deserialize_with(deserializer)); + let value = try!($path(deserializer)); Ok(__SerdeDeserializeWithStruct { value: value }) } } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 14174542..bc53153e 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -506,7 +506,8 @@ fn deserialize_struct( &ty, impl_generics, fields, - container_attrs + container_attrs, + false, )); let type_name = container_attrs.deserialize_name_expr(); @@ -762,6 +763,7 @@ fn deserialize_struct_variant( generics, fields, container_attrs, + true, )); let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( @@ -926,6 +928,7 @@ fn deserialize_struct_visitor( generics: &ast::Generics, fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, + is_enum: bool, ) -> Result<(Vec>, ast::Stmt, P), Error> { let field_exprs = fields.iter() .map(|field| { @@ -933,7 +936,8 @@ fn deserialize_struct_visitor( attr::FieldAttrs::from_field(cx, container_ty, generics, - field) + field, + is_enum) ); Ok(field_attrs.deserialize_name_expr()) }) @@ -954,6 +958,7 @@ fn deserialize_struct_visitor( generics, fields, container_attrs, + is_enum, )); let fields_expr = builder.expr().ref_().slice() @@ -985,6 +990,7 @@ fn deserialize_map( generics: &ast::Generics, fields: &[ast::StructField], container_attrs: &attr::ContainerAttrs, + is_enum: bool, ) -> Result, Error> { // Create the field names for the fields. let field_names: Vec = (0 .. fields.len()) @@ -993,7 +999,7 @@ fn deserialize_map( let field_attrs: Vec<_> = try!( fields.iter() - .map(|field| attr::FieldAttrs::from_field(cx, container_ty, generics, field)) + .map(|field| attr::FieldAttrs::from_field(cx, container_ty, generics, field, is_enum)) .collect() ); diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index de1693e9..b1e1641f 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -256,6 +256,7 @@ fn serialize_struct( builder.id("serialize_struct_elt"), fields, impl_generics, + false, )); let type_name = container_attrs.serialize_name_expr(); @@ -547,6 +548,7 @@ fn serialize_struct_variant( builder.id("serialize_struct_variant_elt"), fields, &variant_generics, + true, )); let container_name = container_attrs.serialize_name_expr(); @@ -644,9 +646,10 @@ fn serialize_struct_visitor( serializer_method: ast::Ident, fields: &[ast::StructField], generics: &ast::Generics, + is_enum: bool, ) -> Result<(P, P), Error> { let field_attrs = try!( - attr::get_struct_field_attrs(cx, &structure_ty, generics, fields) + attr::get_struct_field_attrs(cx, &structure_ty, generics, fields, is_enum) ); let arms: Vec = fields.iter().zip(field_attrs.iter()) diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index 031b6bb5..cdefac86 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -53,7 +53,7 @@ struct DefaultStruct where C: Trait { a1: A, #[serde(default)] a2: B, - #[serde(default="Trait::my_default()")] + #[serde(default="Trait::my_default")] a3: C, } @@ -100,7 +100,7 @@ enum DefaultEnum where C: Trait { a1: A, #[serde(default)] a2: B, - #[serde(default="Trait::my_default()")] + #[serde(default="Trait::my_default")] a3: C, } } @@ -393,7 +393,7 @@ struct SkipSerializingStruct<'a, B, C> where C: Trait { a: &'a i8, #[serde(skip_serializing)] b: B, - #[serde(skip_serializing_if="self.c.should_skip()")] + #[serde(skip_serializing_if="Trait::should_skip")] c: C, } @@ -445,7 +445,7 @@ enum SkipSerializingEnum<'a, B, C> where C: Trait { a: &'a i8, #[serde(skip_serializing)] _b: B, - #[serde(skip_serializing_if="self.c.should_skip()")] + #[serde(skip_serializing_if="Trait::should_skip")] c: C, } } @@ -495,7 +495,7 @@ fn test_skip_serializing_enum() { #[derive(Debug, PartialEq, Serialize)] struct SerializeWithStruct<'a, B> where B: Trait { a: &'a i8, - #[serde(serialize_with="self.b.serialize_with(serializer)")] + #[serde(serialize_with="Trait::serialize_with")] b: B, } @@ -547,7 +547,7 @@ fn test_serialize_with_struct() { enum SerializeWithEnum<'a, B> where B: Trait { Struct { a: &'a i8, - #[serde(serialize_with="self.b.serialize_with(serializer)")] + #[serde(serialize_with="Trait::serialize_with")] b: B, } } @@ -599,7 +599,7 @@ fn test_serialize_with_enum() { #[derive(Debug, PartialEq, Deserialize)] struct DeserializeWithStruct where B: Trait { a: i8, - #[serde(deserialize_with="Trait::deserialize_with(deserializer)")] + #[serde(deserialize_with="Trait::deserialize_with")] b: B, } @@ -650,7 +650,7 @@ fn test_deserialize_with_struct() { enum DeserializeWithEnum where B: Trait { Struct { a: i8, - #[serde(deserialize_with="Trait::deserialize_with(deserializer)")] + #[serde(deserialize_with="Trait::deserialize_with")] b: B, } }