diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index e4015546..51a83f73 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -5,7 +5,10 @@ use syntax::ast; use syntax::ext::base::ExtCtxt; use syntax::ptr::P; +use aster; + /// Represents field name information +#[derive(Debug)] pub enum FieldNames { Global(P), Format{ @@ -15,6 +18,7 @@ pub enum FieldNames { } /// Represents field attribute information +#[derive(Debug)] pub struct FieldAttrs { skip_serializing_field: bool, names: FieldNames, @@ -22,36 +26,6 @@ pub struct FieldAttrs { } impl FieldAttrs { - /// Create a FieldAttr with a single default field name - pub fn new( - skip_serializing_field: bool, - default_value: bool, - name: P, - ) -> FieldAttrs { - FieldAttrs { - skip_serializing_field: skip_serializing_field, - names: FieldNames::Global(name), - use_default: default_value, - } - } - - /// Create a FieldAttr with format specific field names - pub fn new_with_formats( - skip_serializing_field: bool, - default_value: bool, - default_name: P, - formats: HashMap, P>, - ) -> FieldAttrs { - FieldAttrs { - skip_serializing_field: skip_serializing_field, - 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 { @@ -72,20 +46,19 @@ impl FieldAttrs { /// that implements `Serializer`. pub fn serializer_key_expr(self, cx: &ExtCtxt) -> P { match self.names { - FieldNames::Global(x) => x, - FieldNames::Format{formats, default} => { + FieldNames::Global(name) => name, + 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 } - } - }) + match S::format() { + $arms + _ => { $default } + } + ) }, } } @@ -94,17 +67,17 @@ impl FieldAttrs { pub fn default_key_expr(&self) -> &P { match self.names { FieldNames::Global(ref expr) => expr, - FieldNames::Format{formats: _, ref default} => default + 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} => + FieldNames::Global(ref expr) => expr, + FieldNames::Format { ref formats, ref default } => { formats.get(format).unwrap_or(default) + } } } @@ -118,3 +91,121 @@ impl FieldAttrs { self.skip_serializing_field } } + +pub struct FieldAttrsBuilder<'a> { + builder: &'a aster::AstBuilder, + skip_serializing_field: bool, + name: Option>, + format_rename: HashMap, P>, + use_default: bool, +} + +impl<'a> FieldAttrsBuilder<'a> { + pub fn new(builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> { + FieldAttrsBuilder { + builder: builder, + skip_serializing_field: false, + name: None, + format_rename: HashMap::new(), + use_default: false, + } + } + + pub fn field(mut self, field: &ast::StructField) -> FieldAttrsBuilder<'a> { + 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(self, attrs: &[ast::Attribute]) -> FieldAttrsBuilder<'a> { + attrs.iter().fold(self, FieldAttrsBuilder::attr) + } + + pub fn attr(self, attr: &ast::Attribute) -> FieldAttrsBuilder<'a> { + match attr.node.value.node { + ast::MetaList(ref name, ref items) if name == &"serde" => { + items.iter().fold(self, FieldAttrsBuilder::meta_item) + } + _ => { + self + } + } + } + + pub fn meta_item(mut self, meta_item: &P) -> FieldAttrsBuilder<'a> { + match meta_item.node { + ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { + let expr = self.builder.expr().build_lit(P(lit.clone())); + + 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); + } + _ => { } + } + } + self + } + ast::MetaWord(ref name) if name == &"default" => { + self.default() + } + ast::MetaWord(ref name) if name == &"skip_serializing" => { + self.skip_serializing_field() + } + _ => { + // Ignore unknown meta variables for now. + self + } + } + } + + pub fn skip_serializing_field(mut self) -> FieldAttrsBuilder<'a> { + self.skip_serializing_field = 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, + names: names, + use_default: self.use_default, + } + } +} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index c903e706..3f343ed3 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -543,11 +543,13 @@ fn deserialize_item_enum( cx, builder, enum_def.variants.iter() - .map(|variant| - attr::FieldAttrs::new( - false, - true, - builder.expr().str(variant.node.name))) + .map(|variant| { + let expr = builder.expr().str(variant.node.name); + attr::FieldAttrsBuilder::new(builder) + .name(expr) + .default() + .build() + }) .collect() ); diff --git a/serde_codegen/src/field.rs b/serde_codegen/src/field.rs index 6f10eacf..6447b9b2 100644 --- a/serde_codegen/src/field.rs +++ b/serde_codegen/src/field.rs @@ -1,147 +1,17 @@ -use std::collections::HashMap; +use syntax::ast; +use syntax::ext::base::ExtCtxt; use aster; - -use syntax::ast; -use syntax::attr; -use syntax::ext::base::ExtCtxt; -use syntax::ptr::P; - -use attr::FieldAttrs; - -enum Rename<'a> { - None, - Global(&'a ast::Lit), - Format(HashMap, &'a ast::Lit>) -} - -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 default_value(mi: &ast::MetaItem) -> bool { - if let ast::MetaItem_::MetaWord(ref n) = mi.node { - n == &"default" - } else { - false - } -} - -fn skip_serializing_field(mi: &ast::MetaItem) -> bool { - if let ast::MetaItem_::MetaWord(ref n) = mi.node { - n == &"skip_serializing" - } else { - false - } -} - -fn field_attrs<'a>( - builder: &aster::AstBuilder, - field: &'a ast::StructField, -) -> (Rename<'a>, bool, bool) { - field.node.attrs.iter() - .find(|sa| { - if let ast::MetaList(ref n, _) = sa.node.value.node { - n == &"serde" - } else { - false - } - }) - .and_then(|sa| { - if let ast::MetaList(_, ref vals) = sa.node.value.node { - attr::mark_used(&sa); - Some(( - vals.iter() - .fold(None, |v, mi| v.or(rename(builder, mi))) - .unwrap_or(Rename::None), - vals.iter().any(|mi| default_value(mi)), - vals.iter().any(|mi| skip_serializing_field(mi)), - )) - } else { - Some((Rename::None, false, false)) - } - }) - .unwrap_or((Rename::None, false, false)) -} +use attr::{FieldAttrs, FieldAttrsBuilder}; pub fn struct_field_attrs( - cx: &ExtCtxt, + _cx: &ExtCtxt, builder: &aster::AstBuilder, struct_def: &ast::StructDef, ) -> Vec { struct_def.fields.iter() .map(|field| { - match field_attrs(builder, field) { - (Rename::Global(rename), default_value, skip_serializing_field) => - FieldAttrs::new( - skip_serializing_field, - default_value, - builder.expr().build_lit(P(rename.clone()))), - (Rename::Format(renames), default_value, skip_serializing_field) => { - 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( - skip_serializing_field, - default_value, - default_field_name(cx, builder, field.node.kind), - res) - }, - (Rename::None, default_value, skip_serializing_field) => { - FieldAttrs::new( - skip_serializing_field, - default_value, - default_field_name(cx, builder, field.node.kind)) - } - } + FieldAttrsBuilder::new(builder).field(field).build() }) .collect() } - -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") - } - } -}