use Ctxt; use syn::{self, Ident}; // This module handles parsing of `#[serde(...)]` attributes. The entrypoints // are `attr::Item::from_ast`, `attr::Variant::from_ast`, and // `attr::Field::from_ast`. Each returns an instance of the corresponding // struct. Note that none of them return a Result. Unrecognized, malformed, or // duplicated attributes result in a span_err but otherwise are ignored. The // user will see errors simultaneously for all bad attributes in the crate // rather than just the first. struct Attr<'c, T> { cx: &'c Ctxt, name: &'static str, value: Option, } impl<'c, T> Attr<'c, T> { fn none(cx: &'c Ctxt, name: &'static str) -> Self { Attr { cx: cx, name: name, value: None, } } fn set(&mut self, value: T) { if self.value.is_some() { self.cx.error(format!("duplicate serde attribute `{}`", self.name)); } else { self.value = Some(value); } } fn set_opt(&mut self, value: Option) { if let Some(value) = value { self.set(value); } } fn set_if_none(&mut self, value: T) { if self.value.is_none() { self.value = Some(value); } } fn get(self) -> Option { self.value } } struct BoolAttr<'c>(Attr<'c, ()>); impl<'c> BoolAttr<'c> { fn none(cx: &'c Ctxt, name: &'static str) -> Self { BoolAttr(Attr::none(cx, name)) } fn set_true(&mut self) { self.0.set(()); } fn get(&self) -> bool { self.0.value.is_some() } } #[derive(Debug)] pub struct Name { serialize: String, deserialize: String, } impl Name { /// Return the container name for the container when serializing. pub fn serialize_name(&self) -> String { self.serialize.clone() } /// Return the container name for the container when deserializing. pub fn deserialize_name(&self) -> String { self.deserialize.clone() } } /// Represents container (e.g. struct) attribute information #[derive(Debug)] pub struct Item { name: Name, deny_unknown_fields: bool, ser_bound: Option>, de_bound: Option>, } impl Item { /// Extract out the `#[serde(...)]` attributes from an item. pub fn from_ast(cx: &Ctxt, item: &syn::Item) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields"); let mut ser_bound = Attr::none(cx, "bound"); let mut de_bound = Attr::none(cx, "bound"); for meta_items in item.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { ser_name.set(lit.clone()); de_name.set(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` syn::MetaItem::List(ref name, ref meta_items) if name == "rename" => { if let Ok((ser, de)) = get_renames(cx, meta_items) { ser_name.set_opt(ser); de_name.set_opt(de); } } // Parse `#[serde(deny_unknown_fields)]` syn::MetaItem::Word(ref name) if name == "deny_unknown_fields" => { deny_unknown_fields.set_true(); } // Parse `#[serde(bound="D: Serialize")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "bound" => { if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) { ser_bound.set(where_predicates.clone()); de_bound.set(where_predicates); } } // Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]` syn::MetaItem::List(ref name, ref meta_items) if name == "bound" => { if let Ok((ser, de)) = get_where_predicates(cx, meta_items) { ser_bound.set_opt(ser); de_bound.set_opt(de); } } _ => { // TODO include name of attr cx.error("unknown serde container attribute"); } } } } Item { name: Name { serialize: ser_name.get().unwrap_or_else(|| item.ident.to_string()), deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()), }, deny_unknown_fields: deny_unknown_fields.get(), ser_bound: ser_bound.get(), de_bound: de_bound.get(), } } pub fn name(&self) -> &Name { &self.name } pub fn deny_unknown_fields(&self) -> bool { self.deny_unknown_fields } pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { self.ser_bound.as_ref().map(|vec| &vec[..]) } pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> { self.de_bound.as_ref().map(|vec| &vec[..]) } } /// Represents variant attribute information #[derive(Debug)] pub struct Variant { name: Name, } impl Variant { pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { ser_name.set(lit.clone()); de_name.set(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` syn::MetaItem::List(ref name, ref meta_items) if name == "rename" => { if let Ok((ser, de)) = get_renames(cx, meta_items) { ser_name.set_opt(ser); de_name.set_opt(de); } } _ => { // TODO include attribute cx.error("unknown serde variant attribute"); } } } } Variant { name: Name { serialize: ser_name.get().unwrap_or_else(|| variant.ident.to_string()), deserialize: de_name.get().unwrap_or_else(|| variant.ident.to_string()), }, } } pub fn name(&self) -> &Name { &self.name } } /// Represents field attribute information #[derive(Debug)] pub struct Field { name: Name, skip_serializing: bool, skip_deserializing: bool, skip_serializing_if: Option, default: FieldDefault, serialize_with: Option, deserialize_with: Option, ser_bound: Option>, de_bound: Option>, } /// Represents the default to use for a field when deserializing. #[derive(Debug, PartialEq)] pub enum FieldDefault { /// Field must always be specified because it does not have a default. None, /// The default is given by `std::default::Default::default()`. Default, /// The default is given by this function. Path(syn::Path), } impl Field { /// Extract out the `#[serde(...)]` attributes from a struct field. pub fn from_ast(cx: &Ctxt, index: usize, field: &syn::Field) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); let mut skip_serializing = BoolAttr::none(cx, "skip_serializing"); let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing"); let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if"); let mut default = Attr::none(cx, "default"); let mut serialize_with = Attr::none(cx, "serialize_with"); let mut deserialize_with = Attr::none(cx, "deserialize_with"); let mut ser_bound = Attr::none(cx, "bound"); let mut de_bound = Attr::none(cx, "bound"); let ident = match field.ident { Some(ref ident) => ident.to_string(), None => index.to_string(), }; for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { ser_name.set(lit.clone()); de_name.set(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` syn::MetaItem::List(ref name, ref meta_items) if name == "rename" => { if let Ok((ser, de)) = get_renames(cx, meta_items) { ser_name.set_opt(ser); de_name.set_opt(de); } } // Parse `#[serde(default)]` syn::MetaItem::Word(ref name) if name == "default" => { default.set(FieldDefault::Default); } // Parse `#[serde(default="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "default" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { default.set(FieldDefault::Path(path)); } } // Parse `#[serde(skip_serializing)]` syn::MetaItem::Word(ref name) if name == "skip_serializing" => { skip_serializing.set_true(); } // Parse `#[serde(skip_deserializing)]` syn::MetaItem::Word(ref name) if name == "skip_deserializing" => { skip_deserializing.set_true(); } // Parse `#[serde(skip_serializing_if="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "skip_serializing_if" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { skip_serializing_if.set(path); } } // Parse `#[serde(serialize_with="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "serialize_with" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { serialize_with.set(path); } } // Parse `#[serde(deserialize_with="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "deserialize_with" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { deserialize_with.set(path); } } // Parse `#[serde(bound="D: Serialize")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "bound" => { if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) { ser_bound.set(where_predicates.clone()); de_bound.set(where_predicates); } } // Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]` syn::MetaItem::List(ref name, ref meta_items) if name == "bound" => { if let Ok((ser, de)) = get_where_predicates(cx, meta_items) { ser_bound.set_opt(ser); de_bound.set_opt(de); } } _ => { // TODO include attribute cx.error("unknown serde field attribute"); } } } } // Is skip_deserializing, initialize the field to Default::default() // unless a different default is specified by `#[serde(default="...")]` if skip_deserializing.0.value.is_some() { default.set_if_none(FieldDefault::Default); } Field { name: Name { serialize: ser_name.get().unwrap_or(ident.clone()), deserialize: de_name.get().unwrap_or(ident), }, skip_serializing: skip_serializing.get(), skip_deserializing: skip_deserializing.get(), skip_serializing_if: skip_serializing_if.get(), default: default.get().unwrap_or(FieldDefault::None), serialize_with: serialize_with.get(), deserialize_with: deserialize_with.get(), ser_bound: ser_bound.get(), de_bound: de_bound.get(), } } pub fn name(&self) -> &Name { &self.name } pub fn skip_serializing(&self) -> bool { self.skip_serializing } pub fn skip_deserializing(&self) -> bool { self.skip_deserializing } pub fn skip_serializing_if(&self) -> Option<&syn::Path> { self.skip_serializing_if.as_ref() } pub fn default(&self) -> &FieldDefault { &self.default } pub fn serialize_with(&self) -> Option<&syn::Path> { self.serialize_with.as_ref() } pub fn deserialize_with(&self) -> Option<&syn::Path> { self.deserialize_with.as_ref() } pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { self.ser_bound.as_ref().map(|vec| &vec[..]) } pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> { self.de_bound.as_ref().map(|vec| &vec[..]) } } type SerAndDe = (Option, Option); fn get_ser_and_de( cx: &Ctxt, attribute: &'static str, items: &[syn::MetaItem], f: F ) -> Result, ()> where F: Fn(&Ctxt, &Ident, &str) -> Result, { let mut ser_item = Attr::none(cx, attribute); let mut de_item = Attr::none(cx, attribute); for item in items { match *item { syn::MetaItem::NameValue(ref name, ref lit) if name == "serialize" => { if let Ok(v) = f(cx, name, lit) { ser_item.set(v); } } syn::MetaItem::NameValue(ref name, ref lit) if name == "deserialize" => { if let Ok(v) = f(cx, name, lit) { de_item.set(v); } } _ => { cx.error(format!("bad {} attribute", attribute)); return Err(()); } } } Ok((ser_item.get(), de_item.get())) } fn get_renames( cx: &Ctxt, items: &[syn::MetaItem], ) -> Result, ()> { get_ser_and_de(cx, "rename", items, |_, _, s| Ok(s.to_owned())) } fn get_where_predicates( cx: &Ctxt, items: &[syn::MetaItem], ) -> Result>, ()> { get_ser_and_de(cx, "bound", items, parse_lit_into_where) } pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option> { match attr.value { syn::MetaItem::List(ref name, ref items) if name == "serde" => { Some(items.iter().cloned().collect()) } _ => None } } fn parse_lit_into_path(cx: &Ctxt, name: &Ident, lit: &str) -> Result { // TODO handle error Ok(syn::parse_path(lit).unwrap()) } fn parse_lit_into_where(cx: &Ctxt, name: &Ident, lit: &str) -> Result, ()> { if lit.is_empty() { return Ok(Vec::new()); } let where_string = format!("where {}", lit); // TODO handle error Ok(syn::parse_where_clause(&where_string).unwrap().predicates) }