From a935ebe8b9bd99231bc5f0d3819535e5fea00b5a Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Mon, 27 Apr 2015 18:05:54 -0400 Subject: [PATCH 01/13] Adds serializer format specific field names Allows different field names to be used for different external formats. Field names are specified using the `rename` field attribute, e.g: #[serde(rename(xml= "a4", json="a5"))] Reverts #62 Addresses #61 --- serde_macros/src/de.rs | 114 ++++++++++++++++++++++++++++++++------ serde_macros/src/field.rs | 110 ++++++++++++++++++++++++++---------- serde_macros/src/ser.rs | 25 +++++++-- src/de/mod.rs | 4 ++ src/json/de.rs | 5 ++ src/json/ser.rs | 5 ++ src/json/value.rs | 10 ++++ src/ser/mod.rs | 4 ++ tests/test_annotations.rs | 12 ++-- tests/test_json.rs | 25 ++++++++- 10 files changed, 256 insertions(+), 58 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 66fe052b..35853243 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, @@ -353,7 +355,8 @@ fn deserialize_item_enum( cx, builder, enum_def.variants.iter() - .map(|variant| builder.expr().str(variant.node.name)) + .map(|variant| + field::FieldLit::Global(builder.expr().str(variant.node.name))) .collect() ); @@ -531,7 +534,7 @@ fn deserialize_struct_variant( fn deserialize_field_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, - field_exprs: Vec>, + field_exprs: Vec, ) -> Vec> { // Create the field names for the fields. let field_idents: Vec = (0 .. field_exprs.len()) @@ -548,14 +551,80 @@ fn deserialize_field_visitor( ) .build(); + + let fmts = field_exprs.iter() + .fold(HashSet::new(), |mut set, field_expr| + match field_expr { + &field::FieldLit::Format{ref formats, default: _} => { + for (fmt, _) in formats.iter() { + set.insert(fmt.clone()); + }; + set + }, + _ => 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_exprs.iter()) .map(|(field_ident, field_expr)| { - quote_arm!(cx, $field_expr => { Ok(__Field::$field_ident) }) + match field_expr { + &field::FieldLit::Global(ref expr) => + quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }), + &field::FieldLit::Format{formats: _, ref default} => + quote_arm!(cx, $default => { Ok(__Field::$field_ident)}) + } }) .collect(); + let body = if fmts.is_empty() { + quote_expr!(cx, + match value { + $default_field_arms, + _ => Err(::serde::de::Error::unknown_field_error(value)), + }) + } else { + let field_arms : Vec<_> = fmts.iter() + .map(|fmt| { + field_idents.iter() + .zip(field_exprs.iter()) + .map(|(field_ident, field_expr)| { + match field_expr { + &field::FieldLit::Global(ref expr) => + quote_arm!(cx, + $expr => { Ok(__Field::$field_ident) }), + &field::FieldLit::Format{ref formats, ref default} => { + let expr = formats.get(fmt).unwrap_or(default); + quote_arm!(cx, + $expr => { Ok(__Field::$field_ident) })}} + }) + .collect::>() + }) + .collect(); + + let fmt_matches : Vec<_> = fmts.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::fmt() { + $fmt_matches, + _ => match value { + $default_field_arms, + _ => Err(::serde::de::Error::unknown_field_error(value)), + } + }) + }; + vec![ field_enum, @@ -565,26 +634,30 @@ 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 ::serde::de::Visitor for __FieldVisitor + 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(), - ] + ] } fn deserialize_struct_visitor( @@ -596,7 +669,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_strs(cx, builder, struct_def), ); let visit_map_expr = deserialize_map( @@ -639,11 +712,16 @@ fn deserialize_map( 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 rename = field::field_rename(builder, field); 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"), + (field::Rename::Global(rename), _) + => builder.expr().build_lit(P(rename.clone())), + (field::Rename::None, ast::NamedField(name, _)) + => builder.expr().str(name), + (field::Rename::None, ast::UnnamedField(_)) + => panic!("struct contains unnamed fields"), + (field::Rename::Format(renames), _) + => builder.expr().str("fixme"), }; let missing_expr = if field::default_value(field) { diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs index c35e694b..95ae8d6b 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,17 @@ use syntax::ptr::P; use aster; -pub enum Direction { - Serialize, - Deserialize, +pub enum Rename<'a> { + None, + Global(&'a ast::Lit), + // Format(HashMap) + Format(HashMap, &'a ast::Lit>) } pub fn field_rename<'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> { field.node.attrs.iter() .find(|sa| { if let ast::MetaList(ref n, _) = sa.node.value.node { @@ -30,47 +30,99 @@ pub fn field_rename<'a>( 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 + match mi.node { + ast::MetaNameValue(ref n, ref lit) => { + if n == &"rename" { + Some(Rename::Global(lit)) + } else { + v + } + }, + 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((// (n.to_owned(), lit) + builder.expr().str(n), + lit + )), + _ => None + })); + Some(Rename::Format(m)) + } else { + v + } + }, + _ => {v} } }) } else { None } }) + .unwrap_or(Rename::None) +} + +pub enum FieldLit { + Global(P), + Format{ + formats: HashMap, P>, + default: P, + } } pub fn struct_field_strs( 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_rename(builder, field) { + Rename::Global(rename) => + FieldLit::Global( + builder.expr().build_lit(P(rename.clone()))), + Rename::Format(renames) => { + let mut res = HashMap::new(); + res.extend( + renames.into_iter() + .map(|(k,v)| + (k, builder.expr().build_lit(P(v.clone()))))); + FieldLit::Format{ + formats: res, + default: default_field(cx, builder, field.node.kind), } + }, + Rename::None => { + FieldLit::Global( + default_field(cx, builder, field.node.kind)) } } }) .collect() } +fn default_field( + 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") + } + } +} + + pub fn default_value(field: &ast::StructField) -> bool { field.node.attrs.iter() .any(|sa| { diff --git a/serde_macros/src/ser.rs b/serde_macros/src/ser.rs index d9c58d02..be0896d7 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::{FieldLit, struct_field_strs}; pub fn expand_derive_serialize( cx: &mut ExtCtxt, @@ -517,12 +517,29 @@ fn serialize_struct_visitor( { let len = struct_def.fields.len(); - let key_exprs = struct_field_strs(cx, builder, struct_def, Direction::Serialize); + let key_exprs = struct_field_strs(cx, builder, struct_def); - let arms: Vec = key_exprs.iter() + let arms: Vec = key_exprs.into_iter() .zip(value_exprs) .enumerate() - .map(|(i, (key_expr, value_expr))| { + .map(|(i, (field, value_expr))| { + let key_expr = match field { + FieldLit::Global(x) => x, + FieldLit::Format{formats, default} => { + let arms = formats.iter() + .map(|(fmt, lit)| { + quote_arm!(cx, $fmt => { $lit }) + }) + .collect::>(); + quote_expr!(cx, + { + match S::fmt() { + $arms, + _ => $default + } + }) + }, + }; quote_arm!(cx, $i => { self.state += 1; diff --git a/src/de/mod.rs b/src/de/mod.rs index b10f12c0..091adbc6 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -113,6 +113,10 @@ pub trait Deserializer { { self.visit(visitor) } + + fn fmt() -> &'static str { + "" + } } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/json/de.rs b/src/json/de.rs index 532d46e3..dde68ed2 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 fmt() -> &'static str { + "json" + } } struct SeqVisitor<'a, Iter: 'a + Iterator>> { diff --git a/src/json/ser.rs b/src/json/ser.rs index 824af96a..5537859e 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 fmt() -> &'static str { + "json" + } } pub trait Formatter { diff --git a/src/json/value.rs b/src/json/value.rs index 6d041343..c9b8afc0 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -571,6 +571,11 @@ impl ser::Serializer for Serializer { Ok(()) } + + #[inline] + fn fmt() -> &'static str { + "value" + } } pub struct Deserializer { @@ -677,6 +682,11 @@ impl de::Deserializer for Deserializer { None => Ok(value) } } + + #[inline] + fn fmt() -> &'static str { + "value" + } } struct SeqDeserializer<'a> { diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 5c317398..432d2535 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -179,6 +179,10 @@ pub trait Serializer { fn visit_map_elt(&mut self, key: K, value: V) -> Result<(), Self::Error> where K: Serialize, V: Serialize; + + fn fmt() -> &'static str { + "" + } } pub trait SeqVisitor { diff --git a/tests/test_annotations.rs b/tests/test_annotations.rs index 2bc9a336..5f3d37b6 100644 --- a/tests/test_annotations.rs +++ b/tests/test_annotations.rs @@ -21,9 +21,9 @@ 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, } @@ -47,11 +47,11 @@ 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); } diff --git a/tests/test_json.rs b/tests/test_json.rs index ba45d974..bbead941 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", value="z"))] + 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!( + "z".to_string() => Value::I64(5) + ))).unwrap(); + assert_eq!(value, Foo { x: Some(5) }); +} From c30311153c11837bd78a964d6792cc668713ea08 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 30 Apr 2015 16:43:33 -0400 Subject: [PATCH 02/13] Rename fmt to format --- serde_macros/src/de.rs | 2 +- serde_macros/src/ser.rs | 2 +- src/de/mod.rs | 2 +- src/json/de.rs | 2 +- src/json/ser.rs | 2 +- src/json/value.rs | 4 ++-- src/ser/mod.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 35853243..7b455f61 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -616,7 +616,7 @@ fn deserialize_field_visitor( .collect(); quote_expr!(cx, - match D::fmt() { + match D::format() { $fmt_matches, _ => match value { $default_field_arms, diff --git a/serde_macros/src/ser.rs b/serde_macros/src/ser.rs index be0896d7..24c35806 100644 --- a/serde_macros/src/ser.rs +++ b/serde_macros/src/ser.rs @@ -533,7 +533,7 @@ fn serialize_struct_visitor( .collect::>(); quote_expr!(cx, { - match S::fmt() { + match S::format() { $arms, _ => $default } diff --git a/src/de/mod.rs b/src/de/mod.rs index 091adbc6..260cba90 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -114,7 +114,7 @@ pub trait Deserializer { self.visit(visitor) } - fn fmt() -> &'static str { + fn format() -> &'static str { "" } } diff --git a/src/json/de.rs b/src/json/de.rs index dde68ed2..e838ec75 100644 --- a/src/json/de.rs +++ b/src/json/de.rs @@ -465,7 +465,7 @@ impl de::Deserializer for Deserializer } #[inline] - fn fmt() -> &'static str { + fn format() -> &'static str { "json" } } diff --git a/src/json/ser.rs b/src/json/ser.rs index 5537859e..deaace30 100644 --- a/src/json/ser.rs +++ b/src/json/ser.rs @@ -258,7 +258,7 @@ impl ser::Serializer for Serializer } #[inline] - fn fmt() -> &'static str { + fn format() -> &'static str { "json" } } diff --git a/src/json/value.rs b/src/json/value.rs index c9b8afc0..68e58ae5 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -573,7 +573,7 @@ impl ser::Serializer for Serializer { } #[inline] - fn fmt() -> &'static str { + fn format() -> &'static str { "value" } } @@ -684,7 +684,7 @@ impl de::Deserializer for Deserializer { } #[inline] - fn fmt() -> &'static str { + fn format() -> &'static str { "value" } } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 432d2535..612ff059 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -180,7 +180,7 @@ pub trait Serializer { where K: Serialize, V: Serialize; - fn fmt() -> &'static str { + fn format() -> &'static str { "" } } From a1e101b513e1151a56a9cfa301228e4c7d13df63 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 30 Apr 2015 16:50:51 -0400 Subject: [PATCH 03/13] Make Value use the 'json' format string --- src/json/value.rs | 4 ++-- tests/test_json.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/json/value.rs b/src/json/value.rs index 68e58ae5..bfec0474 100644 --- a/src/json/value.rs +++ b/src/json/value.rs @@ -574,7 +574,7 @@ impl ser::Serializer for Serializer { #[inline] fn format() -> &'static str { - "value" + "json" } } @@ -685,7 +685,7 @@ impl de::Deserializer for Deserializer { #[inline] fn format() -> &'static str { - "value" + "json" } } diff --git a/tests/test_json.rs b/tests/test_json.rs index bbead941..f6ca5a47 100644 --- a/tests/test_json.rs +++ b/tests/test_json.rs @@ -1047,7 +1047,7 @@ fn test_missing_renamed_field() { fn test_missing_fmt_renamed_field() { #[derive(Debug, PartialEq, Deserialize)] struct Foo { - #[serde(rename(json="y", value="z"))] + #[serde(rename(json="y"))] x: Option, } @@ -1061,7 +1061,7 @@ fn test_missing_fmt_renamed_field() { assert_eq!(value, Foo { x: None }); let value : Foo = from_value(Value::Object(treemap!( - "z".to_string() => Value::I64(5) + "y".to_string() => Value::I64(5) ))).unwrap(); assert_eq!(value, Foo { x: Some(5) }); } From 960b68937d7af21372cafc23752365516aeec31d Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 30 Apr 2015 16:55:31 -0400 Subject: [PATCH 04/13] Remove commented code and extra newline --- serde_macros/src/de.rs | 1 - serde_macros/src/field.rs | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 7b455f61..08dfa841 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -551,7 +551,6 @@ fn deserialize_field_visitor( ) .build(); - let fmts = field_exprs.iter() .fold(HashSet::new(), |mut set, field_expr| match field_expr { diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs index 95ae8d6b..6c2a9cda 100644 --- a/serde_macros/src/field.rs +++ b/serde_macros/src/field.rs @@ -10,7 +10,6 @@ use aster; pub enum Rename<'a> { None, Global(&'a ast::Lit), - // Format(HashMap) Format(HashMap, &'a ast::Lit>) } @@ -47,10 +46,8 @@ pub fn field_rename<'a>( |item| match item.node { ast::MetaNameValue(ref n, ref lit) => - Some((// (n.to_owned(), lit) - builder.expr().str(n), - lit - )), + Some((builder.expr().str(n), + lit)), _ => None })); Some(Rename::Format(m)) From ec3af2cb6aaf1636f1eb08ca54ee21ccecdc2031 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 1 May 2015 12:53:59 -0400 Subject: [PATCH 05/13] Factor out attr module Factors out field attribute code into the attr module. --- serde_macros/src/de.rs | 199 +++++++++++++++++++++++--------------- serde_macros/src/field.rs | 18 ++-- serde_macros/src/lib.rs | 5 +- serde_macros/src/ser.rs | 20 +--- src/ser/mod.rs | 2 +- tests/test_annotations.rs | 22 +++++ 6 files changed, 154 insertions(+), 112 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 08dfa841..541c7122 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -12,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( @@ -156,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.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(|_| builder.ty().id("_")) + .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() @@ -176,17 +189,37 @@ 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() + .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, @@ -230,10 +263,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, @@ -247,7 +283,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> @@ -305,10 +341,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, @@ -324,7 +363,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] @@ -356,7 +395,7 @@ fn deserialize_item_enum( builder, enum_def.variants.iter() .map(|variant| - field::FieldLit::Global(builder.expr().str(variant.node.name))) + attr::FieldAttrs::Global(builder.expr().str(variant.node.name))) .collect() ); @@ -381,17 +420,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> @@ -460,10 +502,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, @@ -475,7 +520,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> @@ -507,17 +552,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> @@ -534,10 +582,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(); @@ -551,57 +599,43 @@ fn deserialize_field_visitor( ) .build(); - let fmts = field_exprs.iter() - .fold(HashSet::new(), |mut set, field_expr| - match field_expr { - &field::FieldLit::Format{ref formats, default: _} => { - for (fmt, _) in formats.iter() { - set.insert(fmt.clone()); - }; - set - }, - _ => set - }); + // 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 default_field_arms: Vec<_> = field_idents.iter() - .zip(field_exprs.iter()) + .zip(field_attrs.iter()) .map(|(field_ident, field_expr)| { - match field_expr { - &field::FieldLit::Global(ref expr) => - quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }), - &field::FieldLit::Format{formats: _, ref default} => - quote_arm!(cx, $default => { Ok(__Field::$field_ident)}) - } + let expr = field_expr.default_key_expr(); + quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }) }) .collect(); - let body = if fmts.is_empty() { + 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<_> = fmts.iter() + let field_arms : Vec<_> = formats.iter() .map(|fmt| { field_idents.iter() - .zip(field_exprs.iter()) + .zip(field_attrs.iter()) .map(|(field_ident, field_expr)| { - match field_expr { - &field::FieldLit::Global(ref expr) => - quote_arm!(cx, - $expr => { Ok(__Field::$field_ident) }), - &field::FieldLit::Format{ref formats, ref default} => { - let expr = formats.get(fmt).unwrap_or(default); - quote_arm!(cx, - $expr => { Ok(__Field::$field_ident) })}} + let expr = field_expr.key_expr(fmt); + quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }) }) .collect::>() }) .collect(); - let fmt_matches : Vec<_> = fmts.iter() + let fmt_matches : Vec<_> = formats.iter() .zip(field_arms.iter()) .map(|(ref fmt, ref arms)| { quote_arm!(cx, $fmt => { @@ -615,7 +649,7 @@ fn deserialize_field_visitor( .collect(); quote_expr!(cx, - match D::format() { + match __D::format() { $fmt_matches, _ => match value { $default_field_arms, @@ -639,8 +673,8 @@ fn deserialize_field_visitor( phantom: PhantomData } - impl ::serde::de::Visitor for __FieldVisitor - where D: ::serde::de::Deserializer + impl<__D> ::serde::de::Visitor for __FieldVisitor<__D> + where __D: ::serde::de::Deserializer { type Value = __Field; @@ -710,23 +744,30 @@ fn deserialize_map( let extract_values: Vec> = field_names.iter() .zip(struct_def.fields.iter()) - .map(|(field_name, field)| { - let rename = field::field_rename(builder, field); - let name_str = match (rename, field.node.kind) { - (field::Rename::Global(rename), _) - => builder.expr().build_lit(P(rename.clone())), - (field::Rename::None, ast::NamedField(name, _)) - => builder.expr().str(name), - (field::Rename::None, ast::UnnamedField(_)) - => panic!("struct contains unnamed fields"), - (field::Rename::Format(renames), _) - => builder.expr().str("fixme"), - }; - + .zip(field::struct_field_strs(cx, builder, struct_def).iter()) + .map(|((field_name, field), field_attr)| { let missing_expr = if field::default_value(field) { 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, diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs index 6c2a9cda..dde2890e 100644 --- a/serde_macros/src/field.rs +++ b/serde_macros/src/field.rs @@ -7,6 +7,8 @@ use syntax::ptr::P; use aster; +use attr::FieldAttrs; + pub enum Rename<'a> { None, Global(&'a ast::Lit), @@ -65,24 +67,16 @@ pub fn field_rename<'a>( .unwrap_or(Rename::None) } -pub enum FieldLit { - Global(P), - Format{ - formats: HashMap, P>, - default: P, - } -} - pub fn struct_field_strs( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_def: &ast::StructDef, -) -> Vec { +) -> Vec { struct_def.fields.iter() .map(|field| { match field_rename(builder, field) { Rename::Global(rename) => - FieldLit::Global( + FieldAttrs::Global( builder.expr().build_lit(P(rename.clone()))), Rename::Format(renames) => { let mut res = HashMap::new(); @@ -90,13 +84,13 @@ pub fn struct_field_strs( renames.into_iter() .map(|(k,v)| (k, builder.expr().build_lit(P(v.clone()))))); - FieldLit::Format{ + FieldAttrs::Format{ formats: res, default: default_field(cx, builder, field.node.kind), } }, Rename::None => { - FieldLit::Global( + FieldAttrs::Global( default_field(cx, builder, field.node.kind)) } } 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 24c35806..c8579313 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::{FieldLit, struct_field_strs}; +use field::struct_field_strs; pub fn expand_derive_serialize( cx: &mut ExtCtxt, @@ -523,23 +523,7 @@ fn serialize_struct_visitor( .zip(value_exprs) .enumerate() .map(|(i, (field, value_expr))| { - let key_expr = match field { - FieldLit::Global(x) => x, - FieldLit::Format{formats, default} => { - let arms = formats.iter() - .map(|(fmt, lit)| { - quote_arm!(cx, $fmt => { $lit }) - }) - .collect::>(); - quote_expr!(cx, - { - match S::format() { - $arms, - _ => $default - } - }) - }, - }; + let key_expr = field.serializer_key_expr(cx); quote_arm!(cx, $i => { self.state += 1; diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 612ff059..86ae179f 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()))) diff --git a/tests/test_annotations.rs b/tests/test_annotations.rs index 5f3d37b6..0333d352 100644 --- a/tests/test_annotations.rs +++ b/tests/test_annotations.rs @@ -27,6 +27,16 @@ struct FormatRename { 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(); @@ -55,3 +65,15 @@ fn test_format_rename() { 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); +} From cd0ee64892417088d8183bf8fe3018a99675ae13 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 1 May 2015 15:01:01 -0400 Subject: [PATCH 06/13] Add constructor functions for FieldAttrs --- serde_macros/src/de.rs | 6 +-- serde_macros/src/field.rs | 83 +++++++++++++++++++++------------------ serde_macros/src/ser.rs | 6 +-- 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 541c7122..06f16bb3 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -395,7 +395,7 @@ fn deserialize_item_enum( builder, enum_def.variants.iter() .map(|variant| - attr::FieldAttrs::Global(builder.expr().str(variant.node.name))) + attr::FieldAttrs::new(builder.expr().str(variant.node.name))) .collect() ); @@ -702,7 +702,7 @@ fn deserialize_struct_visitor( let field_visitor = deserialize_field_visitor( cx, builder, - field::struct_field_strs(cx, builder, struct_def), + field::struct_field_attrs(cx, builder, struct_def), ); let visit_map_expr = deserialize_map( @@ -744,7 +744,7 @@ fn deserialize_map( let extract_values: Vec> = field_names.iter() .zip(struct_def.fields.iter()) - .zip(field::struct_field_strs(cx, builder, struct_def).iter()) + .zip(field::struct_field_attrs(cx, builder, struct_def).iter()) .map(|((field_name, field), field_attr)| { let missing_expr = if field::default_value(field) { quote_expr!(cx, ::std::default::Default::default()) diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs index dde2890e..3e3d8be0 100644 --- a/serde_macros/src/field.rs +++ b/serde_macros/src/field.rs @@ -9,13 +9,48 @@ use aster; use attr::FieldAttrs; -pub enum Rename<'a> { +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 + } +} + +fn field_rename_attrs<'a>( builder: &aster::AstBuilder, field: &'a ast::StructField, ) -> Rename<'a> { @@ -31,34 +66,7 @@ pub fn field_rename<'a>( if let ast::MetaList(_, ref vals) = sa.node.value.node { attr::mark_used(&sa); vals.iter().fold(None, |v, mi| { - match mi.node { - ast::MetaNameValue(ref n, ref lit) => { - if n == &"rename" { - Some(Rename::Global(lit)) - } else { - v - } - }, - 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 { - v - } - }, - _ => {v} - } + v.or(rename(builder, mi)) }) } else { None @@ -67,16 +75,16 @@ pub fn field_rename<'a>( .unwrap_or(Rename::None) } -pub fn struct_field_strs( +pub fn struct_field_attrs( cx: &ExtCtxt, builder: &aster::AstBuilder, struct_def: &ast::StructDef, ) -> Vec { struct_def.fields.iter() .map(|field| { - match field_rename(builder, field) { + match field_rename_attrs(builder, field) { Rename::Global(rename) => - FieldAttrs::Global( + FieldAttrs::new( builder.expr().build_lit(P(rename.clone()))), Rename::Format(renames) => { let mut res = HashMap::new(); @@ -84,13 +92,12 @@ pub fn struct_field_strs( renames.into_iter() .map(|(k,v)| (k, builder.expr().build_lit(P(v.clone()))))); - FieldAttrs::Format{ - formats: res, - default: default_field(cx, builder, field.node.kind), - } + FieldAttrs::new_with_formats( + default_field(cx, builder, field.node.kind), + res) }, Rename::None => { - FieldAttrs::Global( + FieldAttrs::new( default_field(cx, builder, field.node.kind)) } } diff --git a/serde_macros/src/ser.rs b/serde_macros/src/ser.rs index c8579313..8dce8055 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::struct_field_strs; +use field::struct_field_attrs; pub fn expand_derive_serialize( cx: &mut ExtCtxt, @@ -517,9 +517,9 @@ fn serialize_struct_visitor( { let len = struct_def.fields.len(); - let key_exprs = struct_field_strs(cx, builder, struct_def); + let field_attrs = struct_field_attrs(cx, builder, struct_def); - let arms: Vec = key_exprs.into_iter() + let arms: Vec = field_attrs.into_iter() .zip(value_exprs) .enumerate() .map(|(i, (field, value_expr))| { From 0f7c67efa727a14977c524d2115812946495de7c Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 1 May 2015 15:36:50 -0400 Subject: [PATCH 07/13] Factor default attribute lookup into FieldAttrs --- serde_macros/src/de.rs | 9 +++--- serde_macros/src/field.rs | 65 ++++++++++++++++----------------------- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 06f16bb3..56b13927 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -395,7 +395,9 @@ fn deserialize_item_enum( builder, enum_def.variants.iter() .map(|variant| - attr::FieldAttrs::new(builder.expr().str(variant.node.name))) + attr::FieldAttrs::new( + true, + builder.expr().str(variant.node.name))) .collect() ); @@ -743,10 +745,9 @@ fn deserialize_map( .collect(); let extract_values: Vec> = field_names.iter() - .zip(struct_def.fields.iter()) .zip(field::struct_field_attrs(cx, builder, struct_def).iter()) - .map(|((field_name, field), field_attr)| { - let missing_expr = if field::default_value(field) { + .map(|(field_name, field_attr)| { + let missing_expr = if field_attr.use_default() { quote_expr!(cx, ::std::default::Default::default()) } else { let formats = field_attr.formats(); diff --git a/serde_macros/src/field.rs b/serde_macros/src/field.rs index 3e3d8be0..30a8bf19 100644 --- a/serde_macros/src/field.rs +++ b/serde_macros/src/field.rs @@ -50,10 +50,18 @@ fn rename<'a>( } } -fn field_rename_attrs<'a>( +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, -) -> Rename<'a> { +) -> (Rename<'a>, bool) { field.node.attrs.iter() .find(|sa| { if let ast::MetaList(ref n, _) = sa.node.value.node { @@ -65,14 +73,15 @@ fn field_rename_attrs<'a>( .and_then(|sa| { if let ast::MetaList(_, ref vals) = sa.node.value.node { attr::mark_used(&sa); - vals.iter().fold(None, |v, mi| { - v.or(rename(builder, mi)) - }) + 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) + .unwrap_or((Rename::None, false)) } pub fn struct_field_attrs( @@ -82,30 +91,33 @@ pub fn struct_field_attrs( ) -> Vec { struct_def.fields.iter() .map(|field| { - match field_rename_attrs(builder, field) { - Rename::Global(rename) => + match field_attrs(builder, field) { + (Rename::Global(rename), default_value) => FieldAttrs::new( + default_value, builder.expr().build_lit(P(rename.clone()))), - Rename::Format(renames) => { + (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_field(cx, builder, field.node.kind), + default_value, + default_field_name(cx, builder, field.node.kind), res) }, - Rename::None => { + (Rename::None, default_value) => { FieldAttrs::new( - default_field(cx, builder, field.node.kind)) + default_value, + default_field_name(cx, builder, field.node.kind)) } } }) .collect() } -fn default_field( +fn default_field_name( cx: &ExtCtxt, builder: &aster::AstBuilder, kind: ast::StructFieldKind, @@ -119,28 +131,3 @@ fn default_field( } } } - - -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 - } - }) -} From fe5176113b6389fbf960014300478cb67c4333d5 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 1 May 2015 15:45:58 -0400 Subject: [PATCH 08/13] Add missing attr.rs file --- serde_macros/src/attr.rs | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 serde_macros/src/attr.rs diff --git a/serde_macros/src/attr.rs b/serde_macros/src/attr.rs new file mode 100644 index 00000000..94402814 --- /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 + } +} From c5eed99c6a63a28b9ce1ffe5a22ac8201a958802 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Fri, 1 May 2015 17:59:49 -0400 Subject: [PATCH 09/13] Make forwarded trait name global --- serde_macros/src/de.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 56b13927..2372232a 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -208,6 +208,7 @@ fn deserialize_visitor( 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") From 53e6e29571b7b7467bdaea4c0b8babfc4a6cc95b Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 7 May 2015 17:47:46 -0400 Subject: [PATCH 10/13] Fix whitespace --- serde_macros/src/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 2372232a..ca9acbd3 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -163,7 +163,7 @@ fn deserialize_visitor( ) -> (P, P, P, ast::Generics) { if trait_generics.ty_params.is_empty() && forward_tys.is_empty() { ( - builder.item().tuple_struct("__Visitor").build(), + builder.item().tuple_struct("__Visitor").build(), builder.ty().id("__Visitor"), builder.expr().id("__Visitor"), trait_generics.clone(), @@ -693,7 +693,7 @@ fn deserialize_field_visitor( } } ).unwrap(), - ] + ] } fn deserialize_struct_visitor( From ec483fc07d2647368c6fdd9f1e0e939ce6332120 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 7 May 2015 17:48:15 -0400 Subject: [PATCH 11/13] Add doc string for format method --- src/de/mod.rs | 5 +++++ src/ser/mod.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/de/mod.rs b/src/de/mod.rs index 260cba90..fc2907a3 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -114,6 +114,11 @@ 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/ser/mod.rs b/src/ser/mod.rs index 86ae179f..e3a2b179 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -180,6 +180,10 @@ pub trait Serializer { 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 { "" } From bdec0b3e634f3cec80184c778ea9699edb644004 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 14 May 2015 17:28:44 -0400 Subject: [PATCH 12/13] Update commas and blocks in match arms --- serde_macros/src/attr.rs | 4 ++-- serde_macros/src/de.rs | 20 ++++++++++---------- serde_macros/src/ser.rs | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/serde_macros/src/attr.rs b/serde_macros/src/attr.rs index 94402814..8d04b068 100644 --- a/serde_macros/src/attr.rs +++ b/serde_macros/src/attr.rs @@ -75,8 +75,8 @@ impl FieldAttrs { quote_expr!(cx, { match S::format() { - $arms, - _ => $default + $arms + _ => { $default } } }) }, diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index ca9acbd3..04abdf98 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -309,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()); } @@ -622,8 +622,8 @@ fn deserialize_field_visitor( // 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)), + $default_field_arms + _ => { Err(::serde::de::Error::unknown_field_error(value)) } }) } else { let field_arms : Vec<_> = formats.iter() @@ -643,7 +643,7 @@ fn deserialize_field_visitor( .map(|(ref fmt, ref arms)| { quote_arm!(cx, $fmt => { match value { - $arms, + $arms _ => { Err(::serde::de::Error::unknown_field_error(value)) } @@ -653,10 +653,10 @@ fn deserialize_field_visitor( quote_expr!(cx, match __D::format() { - $fmt_matches, + $fmt_matches _ => match value { - $default_field_arms, - _ => Err(::serde::de::Error::unknown_field_error(value)), + $default_field_arms + _ => { Err(::serde::de::Error::unknown_field_error(value)) } } }) }; @@ -766,8 +766,8 @@ fn deserialize_map( cx, try!(visitor.missing_field( match __D::format() { - $arms, - _ => $default + $arms + _ => { $default } }))) } }; @@ -775,7 +775,7 @@ fn deserialize_map( 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/ser.rs b/serde_macros/src/ser.rs index 8dce8055..70699d8d 100644 --- a/serde_macros/src/ser.rs +++ b/serde_macros/src/ser.rs @@ -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) } } @@ -572,7 +572,7 @@ fn serialize_struct_visitor( { match self.state { $arms - _ => Ok(None), + _ => Ok(None) } } From 801f37b30564cdbc2d3e77a1330e717f33057d60 Mon Sep 17 00:00:00 2001 From: Hugo Duncan Date: Thu, 14 May 2015 17:29:14 -0400 Subject: [PATCH 13/13] Fix visitors for generic structs --- serde_macros/src/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde_macros/src/de.rs b/serde_macros/src/de.rs index 04abdf98..37d0e1d7 100644 --- a/serde_macros/src/de.rs +++ b/serde_macros/src/de.rs @@ -170,7 +170,7 @@ fn deserialize_visitor( ) } else { let placeholders : Vec<_> = trait_generics.ty_params.iter() - .map(|_| builder.ty().id("_")) + .map(|t| builder.ty().id(t.ident)) .collect(); let mut trait_generics = trait_generics.clone(); let mut ty_params = forward_ty_params.clone();