From 5d4f9ce72b4956fba1fc00bc23e77e09bd7a3232 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 8 Feb 2016 09:51:07 -0800 Subject: [PATCH] refactor(codegen): Add FieldsAttr::from_{field,variant} --- serde_codegen/src/attr.rs | 277 +++++++++++++++----------------------- serde_codegen/src/de.rs | 11 +- serde_codegen/src/ser.rs | 2 +- 3 files changed, 110 insertions(+), 180 deletions(-) diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 2c7b750a..99851ccc 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -7,7 +7,7 @@ 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; @@ -15,7 +15,7 @@ use error::Error; #[derive(Debug)] pub enum FieldNames { Global(P), - Format{ + Format { formats: HashMap, P>, default: P, } @@ -73,6 +73,106 @@ pub struct FieldAttrs { } impl FieldAttrs { + /// Extract out the `#[serde(...)]` attributes from a struct field. + pub fn from_field(cx: &ExtCtxt, field: &ast::StructField) -> Result { + let builder = AstBuilder::new(); + + let field_ident = match field.node.ident() { + Some(ident) => ident, + None => { cx.span_bug(field.span, "struct field has no name?") } + }; + + let mut skip_serializing_field = false; + let mut skip_serializing_field_if_empty = false; + let mut skip_serializing_field_if_none = false; + let mut field_name = builder.expr().str(field_ident); + let mut format_rename = HashMap::new(); + let mut 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_name = builder.expr().build_lit(P(lit.clone())); + } + + // Parse `#[serde(rename(xml="foo", token="bar"))]` + ast::MetaList(ref name, ref meta_items) if name == &"rename" => { + for meta_item in meta_items { + match meta_item.node { + ast::MetaNameValue(ref name, ref lit) => { + let name = builder.expr().str(name); + let expr = builder.expr().build_lit(P(lit.clone())); + format_rename.insert(name, expr); + } + _ => { } + } + } + } + + // Parse `#[serde(default)]` + ast::MetaWord(ref name) if name == &"default" => { + use_default = true; + } + + // Parse `#[serde(skip_serializing)]` + ast::MetaWord(ref name) if name == &"skip_serializing" => { + skip_serializing_field = true; + } + + // Parse `#[serde(skip_serializing_if_none)]` + ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => { + skip_serializing_field_if_none = true; + } + + // Parse `#[serde(skip_serializing_if_empty)]` + ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => { + 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); + } + } + } + } + + let names = if format_rename.is_empty() { + FieldNames::Global(field_name) + } else { + FieldNames::Format { + formats: format_rename, + default: field_name, + } + }; + + Ok(FieldAttrs { + skip_serializing_field: skip_serializing_field, + skip_serializing_field_if_empty: skip_serializing_field_if_empty, + skip_serializing_field_if_none: skip_serializing_field_if_none, + names: names, + use_default: use_default, + }) + } + + pub fn from_variant(variant: &ast::Variant) -> Self { + let name = AstBuilder::new().expr().str(variant.node.name); + + FieldAttrs { + skip_serializing_field: false, + skip_serializing_field_if_empty: false, + skip_serializing_field_if_none: false, + names: FieldNames::Global(name), + use_default: false, + } + } + /// Return a set of formats that the field has attributes for. pub fn formats(&self) -> HashSet> { match self.names { @@ -147,177 +247,12 @@ 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, - } - } -} - /// 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); - } - - Ok(attrs) + fields: &[ast::StructField]) -> Result, Error> { + fields.iter() + .map(|field| FieldAttrs::from_field(cx, field)) + .collect() } fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P]> { diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index f83892e6..6f16fa4b 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -553,12 +553,7 @@ fn deserialize_item_enum( cx, builder, enum_def.variants.iter() - .map(|variant| { - let expr = builder.expr().str(variant.node.name); - attr::FieldAttrsBuilder::new(cx, builder) - .name(expr) - .build() - }) + .map(|variant| attr::FieldAttrs::from_variant(variant)) .collect(), container_attrs, ); @@ -967,7 +962,7 @@ fn deserialize_struct_visitor( let field_visitor = deserialize_field_visitor( cx, builder, - try!(attr::get_struct_field_attrs(cx, builder, fields)), + try!(attr::get_struct_field_attrs(cx, fields)), container_attrs ); @@ -1039,7 +1034,7 @@ 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() .zip(field_attrs.iter()) diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 0c8ee210..edd54b0a 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -605,7 +605,7 @@ fn serialize_struct_visitor( { let value_exprs = value_exprs.collect::>(); - let field_attrs = try!(attr::get_struct_field_attrs(cx, builder, fields)); + let field_attrs = try!(attr::get_struct_field_attrs(cx, fields)); let arms: Vec = field_attrs.iter() .zip(value_exprs.iter())