2015-05-01 15:45:58 -04:00
|
|
|
use syntax::ast;
|
2015-09-07 16:44:56 -07:00
|
|
|
use syntax::attr;
|
2015-05-01 15:45:58 -04:00
|
|
|
use syntax::ext::base::ExtCtxt;
|
2016-01-18 12:39:46 -08:00
|
|
|
use syntax::print::pprust::meta_item_to_string;
|
2015-05-01 15:45:58 -04:00
|
|
|
use syntax::ptr::P;
|
|
|
|
|
2016-02-08 09:51:07 -08:00
|
|
|
use aster::AstBuilder;
|
2015-09-07 13:13:32 -07:00
|
|
|
|
2016-02-05 18:11:58 -08:00
|
|
|
use error::Error;
|
|
|
|
|
2016-02-08 08:30:29 -08:00
|
|
|
/// Represents container (e.g. struct) attribute information
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ContainerAttrs {
|
2016-02-08 08:00:38 -08:00
|
|
|
ident: ast::Ident,
|
|
|
|
serialize_name: Option<ast::Lit>,
|
|
|
|
deserialize_name: Option<ast::Lit>,
|
2016-02-08 08:30:29 -08:00
|
|
|
deny_unknown_fields: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainerAttrs {
|
2016-02-08 10:09:18 -08:00
|
|
|
/// Extract out the `#[serde(...)]` attributes from an item.
|
|
|
|
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<ContainerAttrs, Error> {
|
|
|
|
let mut container_attrs = ContainerAttrs {
|
2016-02-08 08:00:38 -08:00
|
|
|
ident: item.ident,
|
|
|
|
serialize_name: None,
|
|
|
|
deserialize_name: None,
|
2016-02-08 10:09:18 -08:00
|
|
|
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 {
|
2016-02-08 08:00:38 -08:00
|
|
|
// Parse `#[serde(rename="foo")]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
2016-02-08 08:00:38 -08:00
|
|
|
container_attrs.serialize_name = Some(lit.clone());
|
|
|
|
container_attrs.deserialize_name = Some(lit.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
2016-02-08 08:00:38 -08:00
|
|
|
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
|
|
|
|
container_attrs.serialize_name = ser_name;
|
|
|
|
container_attrs.deserialize_name = de_name;
|
|
|
|
}
|
|
|
|
|
2016-02-08 10:09:18 -08:00
|
|
|
// Parse `#[serde(deny_unknown_fields)]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::Word(ref name) if name == &"deny_unknown_fields" => {
|
2016-02-08 10:09:18 -08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2016-02-08 08:00:38 -08:00
|
|
|
/// Return the string expression of the field ident.
|
|
|
|
pub fn ident_expr(&self) -> P<ast::Expr> {
|
|
|
|
AstBuilder::new().expr().str(self.ident)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the field name for the field when serializing.
|
|
|
|
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
|
|
|
|
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<ast::Expr> {
|
|
|
|
match self.deserialize_name {
|
|
|
|
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
|
|
|
|
None => self.ident_expr(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-08 08:30:29 -08:00
|
|
|
pub fn deny_unknown_fields(&self) -> bool {
|
|
|
|
self.deny_unknown_fields
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-08 08:03:46 -08:00
|
|
|
/// Represents variant attribute information
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct VariantAttrs {
|
|
|
|
ident: ast::Ident,
|
2016-02-08 10:35:42 -08:00
|
|
|
serialize_name: Option<ast::Lit>,
|
|
|
|
deserialize_name: Option<ast::Lit>,
|
2016-02-08 08:03:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl VariantAttrs {
|
|
|
|
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
|
|
|
|
let mut variant_attrs = VariantAttrs {
|
|
|
|
ident: variant.node.name,
|
2016-02-08 10:35:42 -08:00
|
|
|
serialize_name: None,
|
|
|
|
deserialize_name: None,
|
2016-02-08 08:03:46 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
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")]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
variant_attrs.serialize_name = Some(lit.clone());
|
|
|
|
variant_attrs.deserialize_name = Some(lit.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
|
|
|
|
variant_attrs.serialize_name = ser_name;
|
|
|
|
variant_attrs.deserialize_name = de_name;
|
2016-02-08 08:03:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
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<ast::Expr> {
|
|
|
|
AstBuilder::new().expr().str(self.ident)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the field name for the field when serializing.
|
2016-02-08 10:35:42 -08:00
|
|
|
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
|
|
|
|
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<ast::Expr> {
|
|
|
|
match self.deserialize_name {
|
2016-02-08 08:03:46 -08:00
|
|
|
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
|
|
|
|
None => self.ident_expr(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-01 15:45:58 -04:00
|
|
|
/// Represents field attribute information
|
2015-09-07 13:13:32 -07:00
|
|
|
#[derive(Debug)]
|
2015-05-01 15:45:58 -04:00
|
|
|
pub struct FieldAttrs {
|
2016-02-07 22:03:01 -08:00
|
|
|
ident: ast::Ident,
|
2016-02-08 10:35:42 -08:00
|
|
|
serialize_name: Option<ast::Lit>,
|
|
|
|
deserialize_name: Option<ast::Lit>,
|
2015-07-23 08:07:49 -07:00
|
|
|
skip_serializing_field: bool,
|
2015-09-07 16:44:56 -07:00
|
|
|
skip_serializing_field_if_empty: bool,
|
|
|
|
skip_serializing_field_if_none: bool,
|
2015-05-01 15:45:58 -04:00
|
|
|
use_default: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FieldAttrs {
|
2016-02-08 09:51:07 -08:00
|
|
|
/// Extract out the `#[serde(...)]` attributes from a struct field.
|
|
|
|
pub fn from_field(cx: &ExtCtxt, field: &ast::StructField) -> Result<Self, Error> {
|
|
|
|
let field_ident = match field.node.ident() {
|
|
|
|
Some(ident) => ident,
|
|
|
|
None => { cx.span_bug(field.span, "struct field has no name?") }
|
|
|
|
};
|
|
|
|
|
2016-02-08 10:35:42 -08:00
|
|
|
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,
|
|
|
|
};
|
2016-02-08 09:51:07 -08:00
|
|
|
|
|
|
|
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")]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
field_attrs.serialize_name = Some(lit.clone());
|
|
|
|
field_attrs.deserialize_name = Some(lit.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
|
|
|
|
field_attrs.serialize_name = ser_name;
|
|
|
|
field_attrs.deserialize_name = de_name;
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(default)]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::Word(ref name) if name == &"default" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
field_attrs.use_default = true;
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(skip_serializing)]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
field_attrs.skip_serializing_field = true;
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(skip_serializing_if_none)]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing_if_none" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
field_attrs.skip_serializing_field_if_none = true;
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse `#[serde(skip_serializing_if_empty)]`
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing_if_empty" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
field_attrs.skip_serializing_field_if_empty = true;
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
cx.span_err(
|
|
|
|
meta_item.span,
|
|
|
|
&format!("unknown serde field attribute `{}`",
|
|
|
|
meta_item_to_string(meta_item)));
|
|
|
|
|
|
|
|
return Err(Error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-08 10:35:42 -08:00
|
|
|
Ok(field_attrs)
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
2016-02-08 08:03:46 -08:00
|
|
|
/// Return the string expression of the field ident.
|
|
|
|
pub fn ident_expr(&self) -> P<ast::Expr> {
|
|
|
|
AstBuilder::new().expr().str(self.ident)
|
2016-02-08 09:51:07 -08:00
|
|
|
}
|
|
|
|
|
2016-02-08 08:03:46 -08:00
|
|
|
/// Return the field name for the field when serializing.
|
2016-02-08 10:35:42 -08:00
|
|
|
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
|
|
|
|
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<ast::Expr> {
|
|
|
|
match self.deserialize_name {
|
2016-02-07 22:03:01 -08:00
|
|
|
Some(ref name) => AstBuilder::new().expr().build_lit(P(name.clone())),
|
2016-02-08 08:03:46 -08:00
|
|
|
None => self.ident_expr(),
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Predicate for using a field's default value
|
|
|
|
pub fn use_default(&self) -> bool {
|
|
|
|
self.use_default
|
|
|
|
}
|
2015-07-23 08:07:49 -07:00
|
|
|
|
|
|
|
/// Predicate for ignoring a field when serializing a value
|
|
|
|
pub fn skip_serializing_field(&self) -> bool {
|
|
|
|
self.skip_serializing_field
|
|
|
|
}
|
2015-09-07 16:44:56 -07:00
|
|
|
|
|
|
|
pub fn skip_serializing_field_if_empty(&self) -> bool {
|
|
|
|
self.skip_serializing_field_if_empty
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn skip_serializing_field_if_none(&self) -> bool {
|
|
|
|
self.skip_serializing_field_if_none
|
|
|
|
}
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
2015-09-07 13:13:32 -07:00
|
|
|
|
2016-02-05 18:11:58 -08:00
|
|
|
/// Extract out the `#[serde(...)]` attributes from a struct field.
|
|
|
|
pub fn get_struct_field_attrs(cx: &ExtCtxt,
|
2016-02-08 09:51:07 -08:00
|
|
|
fields: &[ast::StructField]) -> Result<Vec<FieldAttrs>, Error> {
|
|
|
|
fields.iter()
|
|
|
|
.map(|field| FieldAttrs::from_field(cx, field))
|
|
|
|
.collect()
|
2016-02-05 18:11:58 -08:00
|
|
|
}
|
2016-02-08 10:09:18 -08:00
|
|
|
|
2016-02-08 10:35:42 -08:00
|
|
|
fn get_renames(cx: &ExtCtxt,
|
|
|
|
items: &[P<ast::MetaItem>]) -> Result<(Option<ast::Lit>, Option<ast::Lit>), Error> {
|
|
|
|
let mut ser_name = None;
|
|
|
|
let mut de_name = None;
|
|
|
|
|
|
|
|
for item in items {
|
|
|
|
match item.node {
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
ser_name = Some(lit.clone());
|
|
|
|
}
|
|
|
|
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
|
2016-02-08 10:35:42 -08:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2016-02-08 10:09:18 -08:00
|
|
|
fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
|
|
|
match attr.node.value.node {
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::List(ref name, ref items) if name == &"serde" => {
|
2016-02-08 10:09:18 -08:00
|
|
|
attr::mark_used(&attr);
|
|
|
|
Some(items)
|
|
|
|
}
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|