diff --git a/serde_macros/src/attr.rs b/serde_macros/src/attr.rs new file mode 100644 index 00000000..8d04b068 --- /dev/null +++ b/serde_macros/src/attr.rs @@ -0,0 +1,108 @@ +use std::collections::HashMap; +use std::collections::HashSet; + +use syntax::ast; +use syntax::ext::base::ExtCtxt; +use syntax::ptr::P; + +/// Represents field name information +pub enum FieldNames { + Global(P), + Format{ + formats: HashMap, P>, + default: P, + } +} + +/// Represents field attribute information +pub struct FieldAttrs { + names: FieldNames, + use_default: bool, +} + +impl FieldAttrs { + + /// Create a FieldAttr with a single default field name + pub fn new(default_value: bool, name: P) -> FieldAttrs { + FieldAttrs { + names: FieldNames::Global(name), + use_default: default_value, + } + } + + /// Create a FieldAttr with format specific field names + pub fn new_with_formats( + default_value: bool, + default_name: P, + formats: HashMap, P>, + ) -> FieldAttrs { + FieldAttrs { + names: FieldNames::Format { + formats: formats, + default: default_name, + }, + use_default: default_value, + } + } + + /// Return a set of formats that the field has attributes for. + pub fn formats(&self) -> HashSet> { + match self.names { + FieldNames::Format{ref formats, default: _} => { + let mut set = HashSet::new(); + for (fmt, _) in formats.iter() { + set.insert(fmt.clone()); + }; + set + }, + _ => HashSet::new() + } + } + + /// Return an expression for the field key name for serialisation. + /// + /// The resulting expression assumes that `S` refers to a type + /// that implements `Serializer`. + pub fn serializer_key_expr(self, cx: &ExtCtxt) -> P { + match self.names { + FieldNames::Global(x) => x, + FieldNames::Format{formats, default} => { + let arms = formats.iter() + .map(|(fmt, lit)| { + quote_arm!(cx, $fmt => { $lit }) + }) + .collect::>(); + quote_expr!(cx, + { + match S::format() { + $arms + _ => { $default } + } + }) + }, + } + } + + /// Return the default field name for the field. + pub fn default_key_expr(&self) -> &P { + match self.names { + FieldNames::Global(ref expr) => expr, + FieldNames::Format{formats: _, ref default} => default + } + } + + /// Return the field name for the field in the specified format. + pub fn key_expr(&self, format: &P) -> &P { + match self.names { + FieldNames::Global(ref expr) => + expr, + FieldNames::Format{ref formats, ref default} => + formats.get(format).unwrap_or(default) + } + } + + /// Predicate for using a field's default value + pub fn use_default(&self) -> bool { + self.use_default + } +} diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 66fe052b..37d0e1d7 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use syntax::ast::{ Ident, MetaItem, @@ -10,10 +12,12 @@ use syntax::ast; use syntax::codemap::Span; use syntax::ext::base::ExtCtxt; use syntax::ext::build::AstBuilder; +use syntax::owned_slice::OwnedSlice; use syntax::ptr::P; use aster; +use attr; use field; pub fn expand_derive_deserialize( @@ -154,14 +158,25 @@ fn deserialize_item_struct( fn deserialize_visitor( builder: &aster::AstBuilder, trait_generics: &ast::Generics, -) -> (P, P, P) { - if trait_generics.ty_params.is_empty() { + forward_ty_params: Vec, + forward_tys: Vec> +) -> (P, P, P, ast::Generics) { + if trait_generics.ty_params.is_empty() && forward_tys.is_empty() { ( builder.item().tuple_struct("__Visitor").build(), builder.ty().id("__Visitor"), builder.expr().id("__Visitor"), + trait_generics.clone(), ) } else { + let placeholders : Vec<_> = trait_generics.ty_params.iter() + .map(|t| builder.ty().id(t.ident)) + .collect(); + let mut trait_generics = trait_generics.clone(); + let mut ty_params = forward_ty_params.clone(); + ty_params.extend(trait_generics.ty_params.into_vec()); + trait_generics.ty_params = OwnedSlice::from_vec(ty_params); + ( builder.item().tuple_struct("__Visitor") .generics().with(trait_generics.clone()).build() @@ -174,17 +189,38 @@ fn deserialize_visitor( builder.ty().path() .segment("__Visitor").with_generics(trait_generics.clone()).build() .build(), - builder.expr().call().id("__Visitor") + builder.expr().call() + .path().segment("__Visitor") + .with_tys(forward_tys) + .with_tys(placeholders) + .build().build() .with_args( trait_generics.ty_params.iter().map(|_| { builder.expr().phantom_data() }) ) .build(), + trait_generics, ) } } +fn deserializer_ty_param(builder: &aster::AstBuilder) -> ast::TyParam { + builder.ty_param("__D") + .trait_bound(builder.path() + .global() + .segment("serde").build() + .segment("de").build() + .id("Deserializer") + .build()) + .build() + .build() +} + +fn deserializer_ty_arg(builder: &aster::AstBuilder) -> P{ + builder.ty().id("__D") +} + fn deserialize_unit_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, @@ -228,10 +264,13 @@ fn deserialize_tuple_struct( ) -> P { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - impl_generics, - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = + deserialize_visitor( + builder, + impl_generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + ); let visit_seq_expr = deserialize_seq( cx, @@ -245,7 +284,7 @@ fn deserialize_tuple_struct( quote_expr!(cx, { $visitor_item - impl $impl_generics ::serde::de::Visitor for $visitor_ty $where_clause { + impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { type Value = $ty; fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> @@ -270,7 +309,7 @@ fn deserialize_seq( let name = builder.id(format!("__field{}", i)); quote_stmt!(cx, let $name = match try!(visitor.visit()) { - Some(value) => value, + Some(value) => { value }, None => { return Err(::serde::de::Error::end_of_stream_error()); } @@ -303,10 +342,13 @@ fn deserialize_struct( ) -> P { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - impl_generics, - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = + deserialize_visitor( + builder, + &impl_generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + ); let (field_visitor, visit_map_expr) = deserialize_struct_visitor( cx, @@ -322,7 +364,7 @@ fn deserialize_struct( $visitor_item - impl $impl_generics ::serde::de::Visitor for $visitor_ty $where_clause { + impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { type Value = $ty; #[inline] @@ -353,7 +395,10 @@ fn deserialize_item_enum( cx, builder, enum_def.variants.iter() - .map(|variant| builder.expr().str(variant.node.name)) + .map(|variant| + attr::FieldAttrs::new( + true, + builder.expr().str(variant.node.name))) .collect() ); @@ -378,17 +423,20 @@ fn deserialize_item_enum( }) .collect(); - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - impl_generics, - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = + deserialize_visitor( + builder, + impl_generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + ); quote_expr!(cx, { $variant_visitor $visitor_item - impl $impl_generics ::serde::de::EnumVisitor for $visitor_ty $where_clause { + impl $visitor_generics ::serde::de::EnumVisitor for $visitor_ty $where_clause { type Value = $ty; fn visit<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> @@ -457,10 +505,13 @@ fn deserialize_tuple_variant( ) -> P { let where_clause = &generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - generics, - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = + deserialize_visitor( + builder, + generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + ); let visit_seq_expr = deserialize_seq( cx, @@ -472,7 +523,7 @@ fn deserialize_tuple_variant( quote_expr!(cx, { $visitor_item - impl $generics ::serde::de::Visitor for $visitor_ty $where_clause { + impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { type Value = $ty; fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> @@ -504,17 +555,20 @@ fn deserialize_struct_variant( builder.path().id(type_ident).id(variant_ident).build(), ); - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - generics, - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = + deserialize_visitor( + builder, + generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + ); quote_expr!(cx, { $field_visitor $visitor_item - impl $generics ::serde::de::Visitor for $visitor_ty $where_clause { + impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { type Value = $ty; fn visit_map<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> @@ -531,10 +585,10 @@ fn deserialize_struct_variant( fn deserialize_field_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, - field_exprs: Vec>, + field_attrs: Vec, ) -> Vec> { // Create the field names for the fields. - let field_idents: Vec = (0 .. field_exprs.len()) + let field_idents: Vec = (0 .. field_attrs.len()) .map(|i| builder.id(format!("__field{}", i))) .collect(); @@ -548,14 +602,65 @@ fn deserialize_field_visitor( ) .build(); + // A set of all the formats that have specialized field attributes + let formats = field_attrs.iter() + .fold(HashSet::new(), |mut set, field_expr| { + set.extend(field_expr.formats()); + set + }); + // Match arms to extract a field from a string - let field_arms: Vec<_> = field_idents.iter() - .zip(field_exprs.into_iter()) + let default_field_arms: Vec<_> = field_idents.iter() + .zip(field_attrs.iter()) .map(|(field_ident, field_expr)| { - quote_arm!(cx, $field_expr => { Ok(__Field::$field_ident) }) + let expr = field_expr.default_key_expr(); + quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }) }) .collect(); + let body = if formats.is_empty() { + // No formats specific attributes, so no match on format required + quote_expr!(cx, + match value { + $default_field_arms + _ => { Err(::serde::de::Error::unknown_field_error(value)) } + }) + } else { + let field_arms : Vec<_> = formats.iter() + .map(|fmt| { + field_idents.iter() + .zip(field_attrs.iter()) + .map(|(field_ident, field_expr)| { + let expr = field_expr.key_expr(fmt); + quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }) + }) + .collect::>() + }) + .collect(); + + let fmt_matches : Vec<_> = formats.iter() + .zip(field_arms.iter()) + .map(|(ref fmt, ref arms)| { + quote_arm!(cx, $fmt => { + match value { + $arms + _ => { + Err(::serde::de::Error::unknown_field_error(value)) + } + }}) + }) + .collect(); + + quote_expr!(cx, + match __D::format() { + $fmt_matches + _ => match value { + $default_field_arms + _ => { Err(::serde::de::Error::unknown_field_error(value)) } + } + }) + }; + vec![ field_enum, @@ -565,22 +670,26 @@ fn deserialize_field_visitor( fn deserialize(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error> where D: ::serde::de::Deserializer, { - struct __FieldVisitor; + use std::marker::PhantomData; - impl ::serde::de::Visitor for __FieldVisitor { + struct __FieldVisitor { + phantom: PhantomData + } + + impl<__D> ::serde::de::Visitor for __FieldVisitor<__D> + where __D: ::serde::de::Deserializer + { type Value = __Field; fn visit_str(&mut self, value: &str) -> ::std::result::Result<__Field, E> where E: ::serde::de::Error, { - match value { - $field_arms - _ => Err(::serde::de::Error::unknown_field_error(value)), - } + $body } } - deserializer.visit(__FieldVisitor) + deserializer.visit( + __FieldVisitor::{ phantom: PhantomData }) } } ).unwrap(), @@ -596,7 +705,7 @@ fn deserialize_struct_visitor( let field_visitor = deserialize_field_visitor( cx, builder, - field::struct_field_strs(cx, builder, struct_def, field::Direction::Deserialize), + field::struct_field_attrs(cx, builder, struct_def), ); let visit_map_expr = deserialize_map( @@ -637,25 +746,36 @@ fn deserialize_map( .collect(); let extract_values: Vec> = field_names.iter() - .zip(struct_def.fields.iter()) - .map(|(field_name, field)| { - let rename = field::field_rename(field, &field::Direction::Deserialize); - let name_str = match (rename, field.node.kind) { - (Some(rename), _) => builder.expr().build_lit(P(rename.clone())), - (None, ast::NamedField(name, _)) => builder.expr().str(name), - (None, ast::UnnamedField(_)) => panic!("struct contains unnamed fields"), - }; - - let missing_expr = if field::default_value(field) { + .zip(field::struct_field_attrs(cx, builder, struct_def).iter()) + .map(|(field_name, field_attr)| { + let missing_expr = if field_attr.use_default() { quote_expr!(cx, ::std::default::Default::default()) } else { - quote_expr!(cx, try!(visitor.missing_field($name_str))) + let formats = field_attr.formats(); + let arms : Vec<_> = formats.iter() + .map(|format| { + let key_expr = field_attr.key_expr(format); + quote_arm!(cx, $format => { $key_expr }) + }) + .collect(); + let default = field_attr.default_key_expr(); + if arms.is_empty() { + quote_expr!(cx, try!(visitor.missing_field($default))) + } else { + quote_expr!( + cx, + try!(visitor.missing_field( + match __D::format() { + $arms + _ => { $default } + }))) + } }; quote_stmt!(cx, let $field_name = match $field_name { Some($field_name) => $field_name, - None => $missing_expr, + None => $missing_expr }; ).unwrap() }) diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs index c35e694b..30a8bf19 100644 --- a/serde_macros/src/field.rs +++ b/serde_macros/src/field.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use syntax::ast; use syntax::attr; use syntax::ext::base::ExtCtxt; @@ -5,19 +7,61 @@ use syntax::ptr::P; use aster; -pub enum Direction { - Serialize, - Deserialize, +use attr::FieldAttrs; + +enum Rename<'a> { + None, + Global(&'a ast::Lit), + Format(HashMap, &'a ast::Lit>) } -pub fn field_rename<'a>( +fn rename<'a>( + builder: &aster::AstBuilder, + mi: &'a ast::MetaItem, + ) -> Option> +{ + match mi.node { + ast::MetaNameValue(ref n, ref lit) => { + if n == &"rename" { + Some(Rename::Global(lit)) + } else { + None + } + }, + ast::MetaList(ref n, ref items) => { + if n == &"rename" { + let mut m = HashMap::new(); + m.extend( + items.iter() + .filter_map( + |item| + match item.node { + ast::MetaNameValue(ref n, ref lit) => + Some((builder.expr().str(n), + lit)), + _ => None + })); + Some(Rename::Format(m)) + } else { + None + } + }, + _ => None + } +} + +pub fn default_value(mi: &ast::MetaItem) -> bool { + if let ast::MetaItem_::MetaWord(ref n) = mi.node { + n == &"default" + } else { + false + } +} + +fn field_attrs<'a>( + builder: &aster::AstBuilder, field: &'a ast::StructField, - direction: &Direction, -) -> Option<&'a ast::Lit> { - let dir_attr = match *direction { - Direction::Serialize => "rename_serialize", - Direction::Deserialize => "rename_deserialize", - }; +) -> (Rename<'a>, bool) { field.node.attrs.iter() .find(|sa| { if let ast::MetaList(ref n, _) = sa.node.value.node { @@ -29,68 +73,61 @@ pub fn field_rename<'a>( .and_then(|sa| { if let ast::MetaList(_, ref vals) = sa.node.value.node { attr::mark_used(&sa); - vals.iter().fold(None, |v, mi| { - if let ast::MetaNameValue(ref n, ref lit) = mi.node { - if n == &"rename" || n == &dir_attr { - Some(lit) - } else { - v - } - } else { - v - } - }) + Some((vals.iter() + .fold(None, |v, mi| v.or(rename(builder, mi))) + .unwrap_or(Rename::None), + vals.iter().any(|mi| default_value(mi)))) } else { - None + Some((Rename::None, false)) } }) + .unwrap_or((Rename::None, false)) } -pub fn struct_field_strs( +pub fn struct_field_attrs( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_def: &ast::StructDef, - direction: Direction, -) -> Vec> { +) -> Vec { struct_def.fields.iter() .map(|field| { - match field_rename(field, &direction) { - Some(rename) => builder.expr().build_lit(P(rename.clone())), - None => { - match field.node.kind { - ast::NamedField(name, _) => { - builder.expr().str(name) - } - ast::UnnamedField(_) => { - cx.bug("struct has named and unnamed fields") - } - } + match field_attrs(builder, field) { + (Rename::Global(rename), default_value) => + FieldAttrs::new( + default_value, + builder.expr().build_lit(P(rename.clone()))), + (Rename::Format(renames), default_value) => { + let mut res = HashMap::new(); + res.extend( + renames.into_iter() + .map(|(k,v)| + (k, builder.expr().build_lit(P(v.clone()))))); + FieldAttrs::new_with_formats( + default_value, + default_field_name(cx, builder, field.node.kind), + res) + }, + (Rename::None, default_value) => { + FieldAttrs::new( + default_value, + default_field_name(cx, builder, field.node.kind)) } } }) .collect() } -pub fn default_value(field: &ast::StructField) -> bool { - field.node.attrs.iter() - .any(|sa| { - if let ast::MetaItem_::MetaList(ref n, ref vals) = sa.node.value.node { - if n == &"serde" { - attr::mark_used(&sa); - vals.iter() - .map(|mi| - if let ast::MetaItem_::MetaWord(ref n) = mi.node { - n == &"default" - } else { - false - }) - .any(|x| x) - } else { - false - } - } - else { - false - } - }) +fn default_field_name( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + kind: ast::StructFieldKind, +) -> P { + match kind { + ast::NamedField(name, _) => { + builder.expr().str(name) + } + ast::UnnamedField(_) => { + cx.bug("struct has named and unnamed fields") + } + } } diff --git a/serde_macros/src/lib.rs b/serde_macros/src/lib.rs index 6af23dde..afc37a34 100644 --- a/serde_macros/src/lib.rs +++ b/serde_macros/src/lib.rs @@ -10,9 +10,10 @@ use syntax::ext::base::Decorator; use syntax::parse::token; use rustc::plugin::Registry; -mod ser; -mod de; +mod attr; mod field; +mod de; +mod ser; #[plugin_registrar] #[doc(hidden)] diff --git a/serde_macros/src/ser.rs b/serde_macros/src/ser.rs index d9c58d02..70699d8d 100644 --- a/serde_macros/src/ser.rs +++ b/serde_macros/src/ser.rs @@ -13,7 +13,7 @@ use syntax::ptr::P; use aster; -use field::{Direction, struct_field_strs}; +use field::struct_field_attrs; pub fn expand_derive_serialize( cx: &mut ExtCtxt, @@ -276,7 +276,7 @@ fn serialize_variant( $type_name, $variant_name, ) - }, + } ) } ast::TupleVariantKind(ref args) => { @@ -488,11 +488,11 @@ fn serialize_tuple_struct_visitor( $where_clause { #[inline] fn visit(&mut self, serializer: &mut S) -> ::std::result::Result, S::Error> - where S: ::serde::ser::Serializer, + where S: ::serde::ser::Serializer { match self.state { $arms - _ => Ok(None), + _ => Ok(None) } } @@ -517,12 +517,13 @@ fn serialize_struct_visitor( { let len = struct_def.fields.len(); - let key_exprs = struct_field_strs(cx, builder, struct_def, Direction::Serialize); + let field_attrs = struct_field_attrs(cx, builder, struct_def); - let arms: Vec = key_exprs.iter() + let arms: Vec = field_attrs.into_iter() .zip(value_exprs) .enumerate() - .map(|(i, (key_expr, value_expr))| { + .map(|(i, (field, value_expr))| { + let key_expr = field.serializer_key_expr(cx); quote_arm!(cx, $i => { self.state += 1; @@ -571,7 +572,7 @@ fn serialize_struct_visitor( { match self.state { $arms - _ => Ok(None), + _ => Ok(None) } } diff --git a/src/de/mod.rs b/src/de/mod.rs index b10f12c0..fc2907a3 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -113,6 +113,15 @@ pub trait Deserializer { { self.visit(visitor) } + + /// Specify a format string for the deserializer. + /// + /// The deserializer format is used to determine which format + /// specific field attributes should be used with the + /// deserializer. + fn format() -> &'static str { + "" + } } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/json/de.rs b/src/json/de.rs index 532d46e3..e838ec75 100644 --- a/src/json/de.rs +++ b/src/json/de.rs @@ -463,6 +463,11 @@ impl de::Deserializer for Deserializer Err(self.error(ErrorCode::ExpectedSomeValue)) } } + + #[inline] + fn format() -> &'static str { + "json" + } } struct SeqVisitor<'a, Iter: 'a + Iterator>> { diff --git a/src/json/ser.rs b/src/json/ser.rs index 824af96a..deaace30 100644 --- a/src/json/ser.rs +++ b/src/json/ser.rs @@ -256,6 +256,11 @@ impl ser::Serializer for Serializer try!(self.formatter.colon(&mut self.writer)); value.serialize(self) } + + #[inline] + fn format() -> &'static str { + "json" + } } pub trait Formatter { diff --git a/src/json/value.rs b/src/json/value.rs index 6d041343..bfec0474 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -571,6 +571,11 @@ impl ser::Serializer for Serializer { Ok(()) } + + #[inline] + fn format() -> &'static str { + "json" + } } pub struct Deserializer { @@ -677,6 +682,11 @@ impl de::Deserializer for Deserializer { None => Ok(value) } } + + #[inline] + fn format() -> &'static str { + "json" + } } struct SeqDeserializer<'a> { diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 5c317398..e3a2b179 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -104,7 +104,7 @@ pub trait Serializer { fn visit_str(&mut self, value: &str) -> Result<(), Self::Error>; /// `visit_bytes` is a hook that enables those serialization formats that support serializing - /// byte slices separately from generic arrays. By default it serializes as a regular array. + /// byte slices separately from generic arrays. By default it serializes as a regular array. #[inline] fn visit_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error> { self.visit_seq(impls::SeqIteratorVisitor::new(value.iter(), Some(value.len()))) @@ -179,6 +179,14 @@ pub trait Serializer { fn visit_map_elt(&mut self, key: K, value: V) -> Result<(), Self::Error> where K: Serialize, V: Serialize; + + /// Specify a format string for the serializer. + /// + /// The serializer format is used to determine which format + /// specific field attributes should be used with the serializer. + fn format() -> &'static str { + "" + } } pub trait SeqVisitor { diff --git a/tests/test_annotations.rs b/tests/test_annotations.rs index 2bc9a336..0333d352 100644 --- a/tests/test_annotations.rs +++ b/tests/test_annotations.rs @@ -21,12 +21,22 @@ struct Rename { } #[derive(Debug, PartialEq, Serialize, Deserialize)] -struct DirectionRename { +struct FormatRename { a1: i32, - #[serde(rename_serialize="a3", rename_deserialize="a4")] + #[serde(rename(xml= "a4", json="a5"))] a2: i32, } +#[derive(Debug, PartialEq, Deserialize, Serialize)] +enum SerEnum { + Map { + a: i8, + #[serde(rename(xml= "c", json="d"))] + b: A, + }, +} + + #[test] fn test_default() { let deserialized_value: Default = json::from_str(&"{\"a1\":1,\"a2\":2}").unwrap(); @@ -47,11 +57,23 @@ fn test_rename() { } #[test] -fn test_direction_rename() { - let value = DirectionRename { a1: 1, a2: 2 }; +fn test_format_rename() { + let value = FormatRename { a1: 1, a2: 2 }; let serialized_value = json::to_string(&value).unwrap(); - assert_eq!(serialized_value, "{\"a1\":1,\"a3\":2}"); + assert_eq!(serialized_value, "{\"a1\":1,\"a5\":2}"); - let deserialized_value = json::from_str("{\"a1\":1,\"a4\":2}").unwrap(); + let deserialized_value = json::from_str("{\"a1\":1,\"a5\":2}").unwrap(); + assert_eq!(value, deserialized_value); +} + +#[test] +fn test_enum_format_rename() { + let s1 = String::new(); + let value = SerEnum::Map { a: 0i8, b: s1 }; + let serialized_value = json::to_string(&value).unwrap(); + let ans = "{\"Map\":{\"a\":0,\"d\":\"\"}}"; + assert_eq!(serialized_value, ans); + + let deserialized_value = json::from_str(ans).unwrap(); assert_eq!(value, deserialized_value); } diff --git a/tests/test_json.rs b/tests/test_json.rs index ba45d974..f6ca5a47 100644 --- a/tests/test_json.rs +++ b/tests/test_json.rs @@ -1024,7 +1024,7 @@ fn test_missing_field() { fn test_missing_renamed_field() { #[derive(Debug, PartialEq, Deserialize)] struct Foo { - #[serde(rename_deserialize="y")] + #[serde(rename="y")] x: Option, } @@ -1042,3 +1042,26 @@ fn test_missing_renamed_field() { ))).unwrap(); assert_eq!(value, Foo { x: Some(5) }); } + +#[test] +fn test_missing_fmt_renamed_field() { + #[derive(Debug, PartialEq, Deserialize)] + struct Foo { + #[serde(rename(json="y"))] + x: Option, + } + + let value: Foo = from_str("{}").unwrap(); + assert_eq!(value, Foo { x: None }); + + let value: Foo = from_str("{\"y\": 5}").unwrap(); + assert_eq!(value, Foo { x: Some(5) }); + + let value: Foo = from_value(Value::Object(treemap!())).unwrap(); + assert_eq!(value, Foo { x: None }); + + let value : Foo = from_value(Value::Object(treemap!( + "y".to_string() => Value::I64(5) + ))).unwrap(); + assert_eq!(value, Foo { x: Some(5) }); +}