use std::rc::Rc; use syntax::ast::{self, TokenTree}; use syntax::attr; use syntax::codemap::Span; use syntax::ext::base::ExtCtxt; use syntax::fold::Folder; use syntax::parse::token; use syntax::parse; use syntax::print::pprust::{lit_to_string, meta_item_to_string}; use syntax::ptr::P; use aster::AstBuilder; use error::Error; /// Represents container (e.g. struct) attribute information #[derive(Debug)] 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::MetaItemKind::NameValue(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::MetaItemKind::List(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::MetaItemKind::Word(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::MetaItemKind::NameValue(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::MetaItemKind::List(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: Option>, default_expr_if_missing: Option>, serialize_with: Option>, deserialize_with: Option>, } impl FieldAttrs { /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn from_field(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, 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 field_attrs = FieldAttrs { ident: field_ident, serialize_name: None, deserialize_name: None, skip_serializing_field: false, skip_serializing_field_if: None, default_expr_if_missing: None, serialize_with: None, deserialize_with: None, }; 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::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { field_attrs.serialize_name = Some(lit.clone()); field_attrs.deserialize_name = Some(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` ast::MetaItemKind::List(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; } // Parse `#[serde(default)]` ast::MetaItemKind::Word(ref name) if name == &"default" => { let default_expr = builder.expr().default(); field_attrs.default_expr_if_missing = Some(default_expr); } // Parse `#[serde(default="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { let wrapped_expr = wrap_default( cx, &field.node.ty, generics, try!(parse_lit_into_expr(cx, name, lit)), ); field_attrs.default_expr_if_missing = Some(wrapped_expr); } // Parse `#[serde(skip_serializing)]` ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => { field_attrs.skip_serializing_field = true; } // Parse `#[serde(skip_serializing_if="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => { let expr = wrap_skip_serializing( cx, container_ty, generics, try!(parse_lit_into_expr(cx, name, lit)), ); field_attrs.skip_serializing_field_if = Some(expr); } // Parse `#[serde(serialize_with="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => { let expr = wrap_serialize_with( cx, container_ty, generics, try!(parse_lit_into_expr(cx, name, lit)), ); field_attrs.serialize_with = Some(expr); } // Parse `#[serde(deserialize_with="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => { let expr = wrap_deserialize_with( cx, &field.node.ty, generics, try!(parse_lit_into_expr(cx, name, lit)), ); field_attrs.deserialize_with = Some(expr); } _ => { 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 pub fn expr_is_missing(&self) -> P { match self.default_expr_if_missing { Some(ref expr) => expr.clone(), None => { let name = self.ident_expr(); AstBuilder::new().expr() .try() .method_call("missing_field").id("visitor") .with_arg(name) .build() } } } /// Predicate for ignoring a field when serializing a value pub fn skip_serializing_field(&self) -> bool { self.skip_serializing_field } pub fn skip_serializing_field_if(&self) -> Option<&P> { self.skip_serializing_field_if.as_ref() } pub fn serialize_with(&self) -> Option<&P> { self.serialize_with.as_ref() } pub fn deserialize_with(&self) -> Option<&P> { self.deserialize_with.as_ref() } } /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn get_struct_field_attrs(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, fields: &[ast::StructField]) -> Result, Error> { fields.iter() .map(|field| FieldAttrs::from_field(cx, container_ty, generics, 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::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => { ser_name = Some(lit.clone()); } ast::MetaItemKind::NameValue(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((ser_name, de_name)) } fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P]> { match attr.node.value.node { ast::MetaItemKind::List(ref name, ref items) if name == &"serde" => { attr::mark_used(&attr); Some(items) } _ => None } } /// This syntax folder rewrites tokens to say their spans are coming from a macro context. struct Respanner<'a, 'b: 'a> { cx: &'a ExtCtxt<'b>, } impl<'a, 'b> Folder for Respanner<'a, 'b> { fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree { match *tt { TokenTree::Token(span, ref tok) => { TokenTree::Token( self.new_span(span), self.fold_token(tok.clone()) ) } TokenTree::Delimited(span, ref delimed) => { TokenTree::Delimited( self.new_span(span), Rc::new(ast::Delimited { delim: delimed.delim, open_span: delimed.open_span, tts: self.fold_tts(&delimed.tts), close_span: delimed.close_span, }) ) } TokenTree::Sequence(span, ref seq) => { TokenTree::Sequence( self.new_span(span), Rc::new(ast::SequenceRepetition { tts: self.fold_tts(&seq.tts), separator: seq.separator.clone().map(|tok| self.fold_token(tok)), ..**seq }) ) } } } fn new_span(&mut self, span: Span) -> Span { Span { lo: span.lo, hi: span.hi, expn_id: self.cx.backtrace(), } } } fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result, Error> { let source: &str = match lit.node { ast::LitKind::Str(ref source, _) => &source, _ => { cx.span_err( lit.span, &format!("serde annotation `{}` must be a string, not `{}`", name, lit_to_string(lit))); return Err(Error); } }; // If we just parse the string into an expression, any syntax errors in the source will only // have spans that point inside the string, and not back to the attribute. So to have better // error reporting, we'll first parse the string into a token tree. Then we'll update those // spans to say they're coming from a macro context that originally came from the attribute, // and then finally parse them into an expression. let tts = parse::parse_tts_from_source_str( format!("", name), source.to_owned(), cx.cfg(), cx.parse_sess()); // Respan the spans to say they are all coming from this macro. let tts = Respanner { cx: cx }.fold_tts(&tts); let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); let expr = parser.parse_expr(); let expr = match expr { Ok(expr) => expr, Err(mut e) => { e.emit(); return Err(Error); } }; // Make sure to error out if there are trailing characters in the stream. match parser.expect(&token::Eof) { Ok(()) => { } Err(mut e) => { e.emit(); return Err(Error); } } Ok(expr) } /// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it /// from accessing the internal `Deserialize` state. fn wrap_default(cx: &ExtCtxt, field_ty: &P, generics: &ast::Generics, expr: P) -> P { let builder = AstBuilder::new(); // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. let fn_path = builder.path() .segment("__serde_default") .with_generics(generics.clone()) .build() .build(); let where_clause = &generics.where_clause; quote_expr!(cx, { fn __serde_default $generics() -> $field_ty $where_clause { $expr } $fn_path() }) } /// This function wraps the expression in `#[serde(skip_serializing_if="...")]` in a trait to /// prevent it from accessing the internal `Serialize` state. fn wrap_skip_serializing(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, expr: P) -> P { let where_clause = &generics.where_clause; quote_expr!(cx, { trait __SerdeShouldSkipSerializing { fn __serde_should_skip_serializing(&self) -> bool; } impl $generics __SerdeShouldSkipSerializing for $container_ty $where_clause { fn __serde_should_skip_serializing(&self) -> bool { $expr } } self.value.__serde_should_skip_serializing() }) } /// This function wraps the expression in `#[serde(serialize_with="...")]` in a trait to /// prevent it from accessing the internal `Serialize` state. fn wrap_serialize_with(cx: &ExtCtxt, container_ty: &P, generics: &ast::Generics, expr: P) -> P { let where_clause = &generics.where_clause; quote_expr!(cx, { trait __SerdeSerializeWith { fn __serde_serialize_with(&self, serializer: &mut S) -> Result<(), S::Error> where S: ::serde::ser::Serializer; } impl<'a, T> __SerdeSerializeWith for &'a T where T: 'a + __SerdeSerializeWith, { fn __serde_serialize_with(&self, serializer: &mut S) -> Result<(), S::Error> where S: ::serde::ser::Serializer { (**self).__serde_serialize_with(serializer) } } impl $generics __SerdeSerializeWith for $container_ty $where_clause { fn __serde_serialize_with(&self, serializer: &mut S) -> Result<(), S::Error> where S: ::serde::ser::Serializer { $expr } } struct __SerdeSerializeWithStruct<'a, T: 'a> { value: &'a T, } impl<'a, T> ::serde::ser::Serialize for __SerdeSerializeWithStruct<'a, T> where T: 'a + __SerdeSerializeWith { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: ::serde::ser::Serializer { self.value.__serde_serialize_with(serializer) } } __SerdeSerializeWithStruct { value: &self.value, } }) } /// This function wraps the expression in `#[serde(deserialize_with="...")]` in a trait to prevent /// it from accessing the internal `Deserialize` state. fn wrap_deserialize_with(cx: &ExtCtxt, field_ty: &P, generics: &ast::Generics, expr: P) -> P { let builder = AstBuilder::new(); let fn_generics = builder.from_generics(generics.clone()) .ty_param("__D") .bound() .trait_( builder.path() .global() .ids(&["serde", "de", "Deserializer"]) .build() ) .build() .build() .build(); // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. let ty_path = AstBuilder::new().path() .segment("__SerdeDeserializeWithStruct") .with_generics(generics.clone()) .build() .build(); let fn_where_clause = &fn_generics.where_clause; let where_clause = &generics.where_clause; quote_expr!(cx, { fn __serde_deserialize_with $fn_generics(deserializer: &mut __D) -> Result<$field_ty, __D::Error> $fn_where_clause { $expr } struct __SerdeDeserializeWithStruct $generics $where_clause { value: $field_ty, } impl $generics ::serde::de::Deserialize for $ty_path $where_clause { fn deserialize(deserializer: &mut D) -> Result where D: ::serde::de::Deserializer { let value = try!(__serde_deserialize_with(deserializer)); Ok(__SerdeDeserializeWithStruct { value: value }) } } let value: $ty_path = try!(visitor.visit_value()); Ok(value.value) }) }