diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index cb0e25e8..3d172a06 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -424,15 +424,6 @@ pub trait Deserializer { { self.deserialize(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/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index 636517cb..57db9191 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -327,14 +327,6 @@ pub trait Serializer { { self.serialize_struct_elt(key, value) } - - /// 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 { - "" - } } /// A trait that is used by a `Serialize` to iterate through a sequence. diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index a021ef9c..9c036284 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -1,90 +1,263 @@ -use std::collections::HashMap; -use std::collections::HashSet; - use syntax::ast; use syntax::attr; use syntax::ext::base::ExtCtxt; use syntax::print::pprust::meta_item_to_string; use syntax::ptr::P; -use aster; +use aster::AstBuilder; use error::Error; -/// Represents field name information +/// Represents container (e.g. struct) attribute information #[derive(Debug)] -pub enum FieldNames { - Global(P), - Format{ - formats: HashMap, P>, - default: P, +pub struct ContainerAttrs { + ident: ast::Ident, + serialize_name: Option, + deserialize_name: Option, + deny_unknown_fields: bool, +} + +impl ContainerAttrs { + /// Extract out the `#[serde(...)]` attributes from an item. + pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result { + let mut container_attrs = ContainerAttrs { + ident: item.ident, + serialize_name: None, + deserialize_name: None, + deny_unknown_fields: false, + }; + + for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) { + for meta_item in meta_items { + match meta_item.node { + // Parse `#[serde(rename="foo")]` + ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { + container_attrs.serialize_name = Some(lit.clone()); + container_attrs.deserialize_name = Some(lit.clone()); + } + + // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` + ast::MetaList(ref name, ref meta_items) if name == &"rename" => { + let (ser_name, de_name) = try!(get_renames(cx, meta_items)); + container_attrs.serialize_name = ser_name; + container_attrs.deserialize_name = de_name; + } + + // Parse `#[serde(deny_unknown_fields)]` + ast::MetaWord(ref name) if name == &"deny_unknown_fields" => { + container_attrs.deny_unknown_fields = true; + } + + _ => { + cx.span_err( + meta_item.span, + &format!("unknown serde container attribute `{}`", + meta_item_to_string(meta_item))); + + return Err(Error); + } + } + } + } + + Ok(container_attrs) + } + + /// Return the string expression of the field ident. + pub fn ident_expr(&self) -> P { + AstBuilder::new().expr().str(self.ident) + } + + /// Return the field name for the field when serializing. + pub fn serialize_name_expr(&self) -> P { + match self.serialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } + + /// Return the field name for the field when serializing. + pub fn deserialize_name_expr(&self) -> P { + match self.deserialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } + + pub fn deny_unknown_fields(&self) -> bool { + self.deny_unknown_fields + } +} + +/// Represents variant attribute information +#[derive(Debug)] +pub struct VariantAttrs { + ident: ast::Ident, + serialize_name: Option, + deserialize_name: Option, +} + +impl VariantAttrs { + pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result { + let mut variant_attrs = VariantAttrs { + ident: variant.node.name, + serialize_name: None, + deserialize_name: None, + }; + + for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) { + for meta_item in meta_items { + match meta_item.node { + // Parse `#[serde(rename="foo")]` + ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { + variant_attrs.serialize_name = Some(lit.clone()); + variant_attrs.deserialize_name = Some(lit.clone()); + } + + // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` + ast::MetaList(ref name, ref meta_items) if name == &"rename" => { + let (ser_name, de_name) = try!(get_renames(cx, meta_items)); + variant_attrs.serialize_name = ser_name; + variant_attrs.deserialize_name = de_name; + } + + _ => { + cx.span_err( + meta_item.span, + &format!("unknown serde variant attribute `{}`", + meta_item_to_string(meta_item))); + + return Err(Error); + } + } + } + } + + Ok(variant_attrs) + } + + /// Return the string expression of the field ident. + pub fn ident_expr(&self) -> P { + AstBuilder::new().expr().str(self.ident) + } + + /// Return the field name for the field when serializing. + pub fn serialize_name_expr(&self) -> P { + match self.serialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } + + /// Return the field name for the field when serializing. + pub fn deserialize_name_expr(&self) -> P { + match self.deserialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } } } /// Represents field attribute information #[derive(Debug)] pub struct FieldAttrs { + ident: ast::Ident, + serialize_name: Option, + deserialize_name: Option, skip_serializing_field: bool, skip_serializing_field_if_empty: bool, skip_serializing_field_if_none: bool, - names: FieldNames, use_default: bool, } impl FieldAttrs { - /// Return a set of formats that the field has attributes for. - pub fn formats(&self) -> HashSet> { - match self.names { - FieldNames::Format { ref formats, .. } => { - let mut set = HashSet::new(); - for (fmt, _) in formats.iter() { - set.insert(fmt.clone()); - }; - set - }, - _ => HashSet::new() - } - } + /// Extract out the `#[serde(...)]` attributes from a struct field. + pub fn from_field(cx: &ExtCtxt, field: &ast::StructField) -> Result { + let field_ident = match field.node.ident() { + Some(ident) => ident, + None => { cx.span_bug(field.span, "struct field has no name?") } + }; - /// 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(ref name) => name.clone(), - FieldNames::Format { ref formats, ref default } => { - let arms = formats.iter() - .map(|(fmt, lit)| { - quote_arm!(cx, $fmt => { $lit }) - }) - .collect::>(); - quote_expr!(cx, - match S::format() { - $arms - _ => { $default } + let mut field_attrs = FieldAttrs { + ident: field_ident, + serialize_name: None, + deserialize_name: None, + skip_serializing_field: false, + skip_serializing_field_if_empty: false, + skip_serializing_field_if_none: false, + use_default: false, + }; + + for meta_items in field.node.attrs.iter().filter_map(get_serde_meta_items) { + for meta_item in meta_items { + match meta_item.node { + // Parse `#[serde(rename="foo")]` + ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { + field_attrs.serialize_name = Some(lit.clone()); + field_attrs.deserialize_name = Some(lit.clone()); } - ) - }, - } - } - /// 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 { ref default, .. } => default, - } - } + // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` + ast::MetaList(ref name, ref meta_items) if name == &"rename" => { + let (ser_name, de_name) = try!(get_renames(cx, meta_items)); + field_attrs.serialize_name = ser_name; + field_attrs.deserialize_name = de_name; + } - /// 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) + // Parse `#[serde(default)]` + ast::MetaWord(ref name) if name == &"default" => { + field_attrs.use_default = true; + } + + // Parse `#[serde(skip_serializing)]` + ast::MetaWord(ref name) if name == &"skip_serializing" => { + field_attrs.skip_serializing_field = true; + } + + // Parse `#[serde(skip_serializing_if_none)]` + ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => { + field_attrs.skip_serializing_field_if_none = true; + } + + // Parse `#[serde(skip_serializing_if_empty)]` + ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => { + field_attrs.skip_serializing_field_if_empty = true; + } + + _ => { + cx.span_err( + meta_item.span, + &format!("unknown serde field attribute `{}`", + meta_item_to_string(meta_item))); + + return Err(Error); + } + } } } + + Ok(field_attrs) + } + + /// Return the string expression of the field ident. + pub fn ident_expr(&self) -> P { + AstBuilder::new().expr().str(self.ident) + } + + /// Return the field name for the field when serializing. + pub fn serialize_name_expr(&self) -> P { + match self.serialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } + } + + /// Return the field name for the field when deserializing. + pub fn deserialize_name_expr(&self) -> P { + match self.deserialize_name { + Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())), + None => self.ident_expr(), + } } /// Predicate for using a field's default value @@ -106,260 +279,49 @@ impl FieldAttrs { } } -pub struct FieldAttrsBuilder<'a> { - cx: &'a ExtCtxt<'a>, - builder: &'a aster::AstBuilder, - skip_serializing_field: bool, - skip_serializing_field_if_empty: bool, - skip_serializing_field_if_none: bool, - name: Option>, - format_rename: HashMap, P>, - use_default: bool, -} - -impl<'a> FieldAttrsBuilder<'a> { - pub fn new(cx: &'a ExtCtxt<'a>, - builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> { - FieldAttrsBuilder { - cx: cx, - builder: builder, - skip_serializing_field: false, - skip_serializing_field_if_empty: false, - skip_serializing_field_if_none: false, - name: None, - format_rename: HashMap::new(), - use_default: false, - } - } - - pub fn field(mut self, field: &ast::StructField) -> Result, Error> { - match field.node.kind { - ast::NamedField(name, _) => { - self.name = Some(self.builder.expr().str(name)); - } - ast::UnnamedField(_) => { } - }; - - self.attrs(&field.node.attrs) - } - - pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result, Error> { - for attr in attrs { - self = try!(self.attr(attr)); - } - - Ok(self) - } - - pub fn attr(mut self, attr: &ast::Attribute) -> Result, Error> { - match attr.node.value.node { - ast::MetaList(ref name, ref items) if name == &"serde" => { - attr::mark_used(&attr); - for item in items { - self = try!(self.meta_item(item)); - } - - Ok(self) - } - _ => { - Ok(self) - } - } - } - - pub fn meta_item(mut self, - meta_item: &P) -> Result, Error> { - match meta_item.node { - ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { - let expr = self.builder.expr().build_lit(P(lit.clone())); - - Ok(self.name(expr)) - } - ast::MetaList(ref name, ref items) if name == &"rename" => { - for item in items { - match item.node { - ast::MetaNameValue(ref name, ref lit) => { - let name = self.builder.expr().str(name); - let expr = self.builder.expr().build_lit(P(lit.clone())); - - self = self.format_rename(name, expr); - } - _ => { } - } - } - - Ok(self) - } - ast::MetaWord(ref name) if name == &"default" => { - Ok(self.default()) - } - ast::MetaWord(ref name) if name == &"skip_serializing" => { - Ok(self.skip_serializing_field()) - } - ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => { - Ok(self.skip_serializing_field_if_empty()) - } - ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => { - Ok(self.skip_serializing_field_if_none()) - } - _ => { - self.cx.span_err( - meta_item.span, - &format!("unknown serde field attribute `{}`", - meta_item_to_string(meta_item))); - Err(Error) - } - } - } - - pub fn skip_serializing_field(mut self) -> FieldAttrsBuilder<'a> { - self.skip_serializing_field = true; - self - } - - pub fn skip_serializing_field_if_empty(mut self) -> FieldAttrsBuilder<'a> { - self.skip_serializing_field_if_empty = true; - self - } - - pub fn skip_serializing_field_if_none(mut self) -> FieldAttrsBuilder<'a> { - self.skip_serializing_field_if_none = true; - self - } - - pub fn name(mut self, name: P) -> FieldAttrsBuilder<'a> { - self.name = Some(name); - self - } - - pub fn format_rename(mut self, format: P, name: P) -> FieldAttrsBuilder<'a> { - self.format_rename.insert(format, name); - self - } - - pub fn default(mut self) -> FieldAttrsBuilder<'a> { - self.use_default = true; - self - } - - pub fn build(self) -> FieldAttrs { - let name = self.name.expect("here"); - let names = if self.format_rename.is_empty() { - FieldNames::Global(name) - } else { - FieldNames::Format { - formats: self.format_rename, - default: name, - } - }; - - FieldAttrs { - skip_serializing_field: self.skip_serializing_field, - skip_serializing_field_if_empty: self.skip_serializing_field_if_empty, - skip_serializing_field_if_none: self.skip_serializing_field_if_none, - names: names, - use_default: self.use_default, - } - } -} - -/// Represents container (e.g. struct) attribute information -#[derive(Debug)] -pub struct ContainerAttrs { - deny_unknown_fields: bool, -} - -impl ContainerAttrs { - pub fn deny_unknown_fields(&self) -> bool { - self.deny_unknown_fields - } -} - -pub struct ContainerAttrsBuilder<'a> { - cx: &'a ExtCtxt<'a>, - deny_unknown_fields: bool, -} - -impl<'a> ContainerAttrsBuilder<'a> { - pub fn new(cx: &'a ExtCtxt) -> Self { - ContainerAttrsBuilder { - cx: cx, - deny_unknown_fields: false, - } - } - - pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result { - for attr in attrs { - self = try!(self.attr(attr)); - } - - Ok(self) - } - - pub fn attr(mut self, attr: &ast::Attribute) -> Result { - match attr.node.value.node { - ast::MetaList(ref name, ref items) if name == &"serde" => { - attr::mark_used(&attr); - for item in items { - self = try!(self.meta_item(item)); - } - - Ok(self) - } - _ => { - Ok(self) - } - } - } - - pub fn meta_item(self, meta_item: &P) -> Result { - match meta_item.node { - ast::MetaWord(ref name) if name == &"deny_unknown_fields" => { - Ok(self.deny_unknown_fields()) - } - _ => { - self.cx.span_err( - meta_item.span, - &format!("unknown serde container attribute `{}`", - meta_item_to_string(meta_item))); - Err(Error) - } - } - } - - pub fn deny_unknown_fields(mut self) -> Self { - self.deny_unknown_fields = true; - self - } - - pub fn build(self) -> ContainerAttrs { - ContainerAttrs { - deny_unknown_fields: self.deny_unknown_fields, - } - } -} - -/// Extract out the `#[serde(...)]` attributes from an item. -pub fn get_container_attrs(cx: &ExtCtxt, - container: &ast::Item, - ) -> Result { - let builder = ContainerAttrsBuilder::new(cx); - let builder = try!(builder.attrs(container.attrs())); - Ok(builder.build()) -} - /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn get_struct_field_attrs(cx: &ExtCtxt, - builder: &aster::AstBuilder, - fields: &[ast::StructField] - ) -> Result, Error> { - let mut attrs = vec![]; - for field in fields { - let builder = FieldAttrsBuilder::new(cx, builder); - let builder = try!(builder.field(field)); - let attr = builder.build(); - attrs.push(attr); + fields: &[ast::StructField]) -> Result, Error> { + fields.iter() + .map(|field| FieldAttrs::from_field(cx, field)) + .collect() +} + +fn get_renames(cx: &ExtCtxt, + items: &[P]) -> Result<(Option, Option), Error> { + let mut ser_name = None; + let mut de_name = None; + + for item in items { + match item.node { + ast::MetaNameValue(ref name, ref lit) if name == &"serialize" => { + ser_name = Some(lit.clone()); + } + + ast::MetaNameValue(ref name, ref lit) if name == &"deserialize" => { + de_name = Some(lit.clone()); + } + + _ => { + cx.span_err( + item.span, + &format!("unknown rename attribute `{}`", + meta_item_to_string(item))); + + return Err(Error); + } + } } - Ok(attrs) + Ok((ser_name, de_name)) +} + +fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P]> { + match attr.node.value.node { + ast::MetaList(ref name, ref items) if name == &"serde" => { + attr::mark_used(&attr); + Some(items) + } + _ => None + } } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 5a893e5f..ea5ed9b6 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -1,5 +1,3 @@ -use std::collections::HashSet; - use aster; use syntax::ast::{ @@ -14,8 +12,7 @@ use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::ptr::P; -use attr::{self, ContainerAttrs}; - +use attr; use error::Error; pub fn expand_derive_deserialize( @@ -88,7 +85,7 @@ fn deserialize_body( impl_generics: &ast::Generics, ty: P, ) -> Result, Error> { - let container_attrs = try!(attr::get_container_attrs(cx, item)); + let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item)); match item.node { ast::ItemStruct(ref variant_data, _) => { @@ -129,14 +126,14 @@ fn deserialize_item_struct( ty: P, span: Span, variant_data: &ast::VariantData, - container_attrs: &ContainerAttrs, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { match *variant_data { ast::VariantData::Unit(_) => { deserialize_unit_struct( cx, - &builder, item.ident, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { @@ -146,6 +143,7 @@ fn deserialize_item_struct( item.ident, impl_generics, ty, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) => { @@ -160,6 +158,7 @@ fn deserialize_item_struct( impl_generics, ty, fields.len(), + container_attrs, ) } ast::VariantData::Struct(ref fields, _) => { @@ -264,10 +263,10 @@ fn deserializer_ty_arg(builder: &aster::AstBuilder) -> P{ fn deserialize_unit_struct( cx: &ExtCtxt, - builder: &aster::AstBuilder, type_ident: Ident, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { struct __Visitor; @@ -301,6 +300,7 @@ fn deserialize_newtype_struct( type_ident: Ident, impl_generics: &ast::Generics, ty: P, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &impl_generics.where_clause; @@ -318,7 +318,7 @@ fn deserialize_newtype_struct( 1, ); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { $visitor_item @@ -353,6 +353,7 @@ fn deserialize_tuple_struct( impl_generics: &ast::Generics, ty: P, fields: usize, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &impl_generics.where_clause; @@ -370,7 +371,7 @@ fn deserialize_tuple_struct( fields, ); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { $visitor_item @@ -478,7 +479,7 @@ fn deserialize_struct( impl_generics: &ast::Generics, ty: P, fields: &[ast::StructField], - container_attrs: &ContainerAttrs, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &impl_generics.where_clause; @@ -506,7 +507,7 @@ fn deserialize_struct( container_attrs )); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); Ok(quote_expr!(cx, { $field_visitor @@ -544,23 +545,23 @@ fn deserialize_item_enum( impl_generics: &ast::Generics, ty: P, enum_def: &EnumDef, - container_attrs: &ContainerAttrs + container_attrs: &attr::ContainerAttrs ) -> Result, Error> { let where_clause = &impl_generics.where_clause; - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.deserialize_name_expr(); let variant_visitor = deserialize_field_visitor( cx, builder, - enum_def.variants.iter() - .map(|variant| { - let expr = builder.expr().str(variant.node.name); - attr::FieldAttrsBuilder::new(cx, builder) - .name(expr) - .build() - }) - .collect(), + try!( + enum_def.variants.iter() + .map(|variant| { + let attrs = try!(attr::VariantAttrs::from_variant(cx, variant)); + Ok(attrs.deserialize_name_expr()) + }) + .collect() + ), container_attrs, ); @@ -642,7 +643,7 @@ fn deserialize_variant( generics: &ast::Generics, ty: P, variant: &ast::Variant, - container_attrs: &ContainerAttrs, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let variant_ident = variant.node.name; @@ -735,7 +736,7 @@ fn deserialize_struct_variant( generics: &ast::Generics, ty: P, fields: &[ast::StructField], - container_attrs: &ContainerAttrs, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let where_clause = &generics.where_clause; @@ -798,11 +799,11 @@ fn deserialize_struct_variant( fn deserialize_field_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, - field_attrs: Vec, - container_attrs: &ContainerAttrs, + field_names: Vec>, + container_attrs: &attr::ContainerAttrs, ) -> Vec> { // Create the field names for the fields. - let field_idents: Vec = (0 .. field_attrs.len()) + let field_idents: Vec = (0 .. field_names.len()) .map(|i| builder.id(format!("__field{}", i))) .collect(); @@ -838,19 +839,11 @@ fn deserialize_field_visitor( } ); - // 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_attrs.iter()) - .map(|(field_ident, field_expr)| { - let expr = field_expr.default_key_expr(); - quote_arm!(cx, $expr => { Ok(__Field::$field_ident) }) + .zip(field_names.iter()) + .map(|(field_ident, field_name)| { + quote_arm!(cx, $field_name => { Ok(__Field::$field_ident) }) }) .collect(); @@ -860,49 +853,12 @@ fn deserialize_field_visitor( quote_expr!(cx, Err(::serde::de::Error::unknown_field(value))) }; - let str_body = if formats.is_empty() { - // No formats specific attributes, so no match on format required - quote_expr!(cx, - match value { - $default_field_arms - _ => { $fallthrough_arm_expr } - }) - } 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 - _ => { - $fallthrough_arm_expr - } - }}) - }) - .collect(); - - quote_expr!(cx, - match __D::format() { - $fmt_matches - _ => match value { - $default_field_arms - _ => $fallthrough_arm_expr - } - } - ) - }; + let str_body = quote_expr!(cx, + match value { + $default_field_arms + _ => $fallthrough_arm_expr + } + ); let impl_item = quote_item!(cx, impl ::serde::de::Deserialize for __Field { @@ -963,12 +919,19 @@ fn deserialize_struct_visitor( builder: &aster::AstBuilder, struct_path: ast::Path, fields: &[ast::StructField], - container_attrs: &ContainerAttrs, + container_attrs: &attr::ContainerAttrs, ) -> Result<(Vec>, P, P), Error> { let field_visitor = deserialize_field_visitor( cx, builder, - try!(attr::get_struct_field_attrs(cx, builder, fields)), + try!( + fields.iter() + .map(|field| { + let attrs = try!(attr::FieldAttrs::from_field(cx, field)); + Ok(attrs.deserialize_name_expr()) + }) + .collect() + ), container_attrs ); @@ -1006,7 +969,7 @@ fn deserialize_map( builder: &aster::AstBuilder, struct_path: ast::Path, fields: &[ast::StructField], - container_attrs: &ContainerAttrs, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { // Create the field names for the fields. let field_names: Vec = (0 .. fields.len()) @@ -1040,33 +1003,16 @@ fn deserialize_map( .chain(ignored_arm.into_iter()) .collect(); - let field_attrs = try!(attr::get_struct_field_attrs(cx, builder, fields)); + let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); - let extract_values: Vec> = field_names.iter() + 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 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 } - }))) - } + let name = field_attr.ident_expr(); + quote_expr!(cx, try!(visitor.missing_field($name))) }; quote_stmt!(cx, @@ -1076,7 +1022,7 @@ fn deserialize_map( }; ).unwrap() }) - .collect(); + .collect::>(); 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 7a61c6a2..1c1be6a2 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -84,20 +84,18 @@ fn serialize_body( impl_generics: &ast::Generics, ty: P, ) -> Result, Error> { - // Note: While we don't have any container attributes, we still want to try to - // parse them so we can report a proper error if we get passed an unknown attribute. - let _ = try!(attr::get_container_attrs(cx, item)); + let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item)); match item.node { ast::ItemStruct(ref variant_data, _) => { serialize_item_struct( cx, builder, - item, impl_generics, ty, item.span, variant_data, + &container_attrs, ) } ast::ItemEnum(ref enum_def, _) => { @@ -108,6 +106,7 @@ fn serialize_body( impl_generics, ty, enum_def, + &container_attrs, ) } _ => { @@ -120,25 +119,23 @@ fn serialize_body( fn serialize_item_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, - item: &Item, impl_generics: &ast::Generics, ty: P, span: Span, variant_data: &ast::VariantData, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { match *variant_data { ast::VariantData::Unit(_) => { serialize_unit_struct( cx, - &builder, - item.ident, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { serialize_newtype_struct( cx, - &builder, - item.ident, + container_attrs, ) } ast::VariantData::Tuple(ref fields, _) => { @@ -149,10 +146,10 @@ fn serialize_item_struct( serialize_tuple_struct( cx, &builder, - item.ident, impl_generics, ty, fields.len(), + container_attrs, ) } ast::VariantData::Struct(ref fields, _) => { @@ -163,10 +160,10 @@ fn serialize_item_struct( serialize_struct( cx, &builder, - item.ident, impl_generics, ty, fields, + container_attrs, ) } } @@ -174,10 +171,9 @@ fn serialize_item_struct( fn serialize_unit_struct( cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, serializer.serialize_unit_struct($type_name) @@ -186,10 +182,9 @@ fn serialize_unit_struct( fn serialize_newtype_struct( cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, serializer.serialize_newtype_struct($type_name, &self.0) @@ -199,10 +194,10 @@ fn serialize_newtype_struct( fn serialize_tuple_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, - type_ident: Ident, impl_generics: &ast::Generics, ty: P, fields: usize, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( cx, @@ -216,7 +211,7 @@ fn serialize_tuple_struct( impl_generics, ); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, { $visitor_struct @@ -232,10 +227,10 @@ fn serialize_tuple_struct( fn serialize_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, - type_ident: Ident, impl_generics: &ast::Generics, ty: P, 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"); @@ -255,7 +250,7 @@ fn serialize_struct( value_exprs, )); - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); Ok(quote_expr!(cx, { $visitor_struct @@ -275,6 +270,7 @@ fn serialize_item_enum( impl_generics: &ast::Generics, ty: P, enum_def: &ast::EnumDef, + container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { let mut arms = vec![]; @@ -287,6 +283,7 @@ fn serialize_item_enum( ty.clone(), variant, variant_index, + container_attrs, )); arms.push(arm); @@ -307,10 +304,13 @@ fn serialize_variant( ty: P, variant: &ast::Variant, variant_index: usize, + container_attrs: &attr::ContainerAttrs, ) -> Result { - let type_name = builder.expr().str(type_ident); + let type_name = container_attrs.serialize_name_expr(); + let variant_ident = variant.node.name; - let variant_name = builder.expr().str(variant_ident); + let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant)); + let variant_name = variant_attrs.serialize_name_expr(); match variant.node.data { ast::VariantData::Unit(_) => { @@ -605,14 +605,14 @@ fn serialize_struct_visitor( { let value_exprs = value_exprs.collect::>(); - let field_attrs = try!(attr::get_struct_field_attrs(cx, builder, fields)); + let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); let arms: Vec = field_attrs.iter() .zip(value_exprs.iter()) .filter(|&(ref field, _)| !field.skip_serializing_field()) .enumerate() .map(|(i, (ref field, value_expr))| { - let key_expr = field.serializer_key_expr(cx); + let key_expr = field.serialize_name_expr(); let stmt = if field.skip_serializing_field_if_empty() { quote_stmt!(cx, if ($value_expr).is_empty() { continue; }) diff --git a/serde_tests/tests/test_annotations.rs b/serde_tests/tests/test_annotations.rs index dc91b9b5..82f1265e 100644 --- a/serde_tests/tests/test_annotations.rs +++ b/serde_tests/tests/test_annotations.rs @@ -23,24 +23,44 @@ struct DisallowUnknown { } #[derive(Debug, PartialEq, Serialize, Deserialize)] -struct Rename { +#[serde(rename="Superhero")] +struct RenameStruct { a1: i32, #[serde(rename="a3")] a2: i32, } #[derive(Debug, PartialEq, Serialize, Deserialize)] -struct FormatRename { +#[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))] +struct RenameStructSerializeDeserialize { a1: i32, - #[serde(rename(xml= "a4", token="a5"))] + #[serde(rename(serialize="a4", deserialize="a5"))] a2: i32, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename="Superhero")] +enum RenameEnum { + #[serde(rename="bruce_wayne")] + Batman, + #[serde(rename="clark_kent")] + Superman(i8), + #[serde(rename="diana_prince")] + WonderWoman(i8, i8), + #[serde(rename="barry_allan")] + Flash { + #[serde(rename="b")] + a: i32, + }, +} + #[derive(Debug, PartialEq, Deserialize, Serialize)] -enum SerEnum { - Map { +#[serde(rename(serialize="SuperheroSer", deserialize="SuperheroDe"))] +enum RenameEnumSerializeDeserialize { + #[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))] + Robin { a: i8, - #[serde(rename(xml= "c", token="d"))] + #[serde(rename(serialize="c", deserialize="d"))] b: A, }, } @@ -153,11 +173,11 @@ fn test_ignore_unknown() { } #[test] -fn test_rename() { +fn test_rename_struct() { assert_tokens( - &Rename { a1: 1, a2: 2 }, + &RenameStruct { a1: 1, a2: 2 }, vec![ - Token::StructStart("Rename", Some(2)), + Token::StructStart("Superhero", Some(2)), Token::MapSep, Token::Str("a1"), @@ -173,11 +193,28 @@ fn test_rename() { } #[test] -fn test_format_rename() { - assert_tokens( - &FormatRename { a1: 1, a2: 2 }, +fn test_rename_struct_serialize_deserialize() { + assert_ser_tokens( + &RenameStructSerializeDeserialize { a1: 1, a2: 2 }, + &[ + Token::StructStart("SuperheroSer", Some(2)), + + Token::MapSep, + Token::Str("a1"), + Token::I32(1), + + Token::MapSep, + Token::Str("a4"), + Token::I32(2), + + Token::MapEnd, + ] + ); + + assert_de_tokens( + &RenameStructSerializeDeserialize { a1: 1, a2: 2 }, vec![ - Token::StructStart("FormatRename", Some(2)), + Token::StructStart("SuperheroDe", Some(2)), Token::MapSep, Token::Str("a1"), @@ -193,14 +230,80 @@ fn test_format_rename() { } #[test] -fn test_enum_format_rename() { +fn test_rename_enum() { assert_tokens( - &SerEnum::Map { + &RenameEnum::Batman, + vec![ + Token::EnumUnit("Superhero", "bruce_wayne"), + ] + ); + + assert_tokens( + &RenameEnum::Superman(0), + vec![ + Token::EnumNewtype("Superhero", "clark_kent"), + Token::I8(0), + ] + ); + + assert_tokens( + &RenameEnum::WonderWoman(0, 1), + vec![ + Token::EnumSeqStart("Superhero", "diana_prince", Some(2)), + + Token::SeqSep, + Token::I8(0), + + Token::SeqSep, + Token::I8(1), + + Token::SeqEnd, + ] + ); + + assert_tokens( + &RenameEnum::Flash { a: 1 }, + vec![ + Token::EnumMapStart("Superhero", "barry_allan", Some(1)), + + Token::MapSep, + Token::Str("b"), + Token::I32(1), + + Token::MapEnd, + ] + ); +} + +#[test] +fn test_enum_serialize_deserialize() { + assert_ser_tokens( + &RenameEnumSerializeDeserialize::Robin { + a: 0, + b: String::new(), + }, + &[ + Token::EnumMapStart("SuperheroSer", "dick_grayson", Some(2)), + + Token::MapSep, + Token::Str("a"), + Token::I8(0), + + Token::MapSep, + Token::Str("c"), + Token::Str(""), + + Token::MapEnd, + ] + ); + + assert_de_tokens( + &RenameEnumSerializeDeserialize::Robin { a: 0, b: String::new(), }, vec![ - Token::EnumMapStart("SerEnum", "Map", Some(2)), + Token::EnumMapStart("SuperheroDe", "jason_todd", Some(2)), Token::MapSep, Token::Str("a"), diff --git a/serde_tests/tests/token.rs b/serde_tests/tests/token.rs index b1170511..1b14a7d3 100644 --- a/serde_tests/tests/token.rs +++ b/serde_tests/tests/token.rs @@ -301,10 +301,6 @@ impl<'a, I> ser::Serializer for Serializer try!(key.serialize(self)); value.serialize(self) } - - fn format() -> &'static str { - "token" - } } ////////////////////////////////////////////////////////////////////////////// @@ -583,10 +579,6 @@ impl de::Deserializer for Deserializer None => Err(Error::EndOfStreamError), } } - - fn format() -> &'static str { - "token" - } } //////////////////////////////////////////////////////////////////////////