2016-02-21 09:54:03 -08:00
|
|
|
use std::rc::Rc;
|
|
|
|
use syntax::ast::{self, TokenTree};
|
2015-09-07 16:44:56 -07:00
|
|
|
use syntax::attr;
|
2016-02-21 09:54:03 -08:00
|
|
|
use syntax::codemap::Span;
|
2015-05-01 15:45:58 -04:00
|
|
|
use syntax::ext::base::ExtCtxt;
|
2016-02-21 09:54:03 -08:00
|
|
|
use syntax::fold::Folder;
|
2016-02-21 15:28:25 -08:00
|
|
|
use syntax::parse::parser::PathParsingMode;
|
2016-02-23 19:51:53 -08:00
|
|
|
use syntax::parse::token::{self, InternedString};
|
2016-02-12 21:53:35 -08:00
|
|
|
use syntax::parse;
|
2016-02-21 09:54:03 -08:00
|
|
|
use syntax::print::pprust::{lit_to_string, 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-23 19:51:53 -08:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Name {
|
|
|
|
ident: ast::Ident,
|
|
|
|
serialize_name: Option<InternedString>,
|
|
|
|
deserialize_name: Option<InternedString>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Name {
|
|
|
|
fn new(ident: ast::Ident) -> Self {
|
|
|
|
Name {
|
|
|
|
ident: ident,
|
|
|
|
serialize_name: None,
|
|
|
|
deserialize_name: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the container name for the container when serializing.
|
|
|
|
pub fn serialize_name(&self) -> InternedString {
|
|
|
|
match self.serialize_name {
|
|
|
|
Some(ref name) => name.clone(),
|
|
|
|
None => self.ident.name.as_str(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the container name expression for the container when deserializing.
|
|
|
|
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
|
|
|
|
AstBuilder::new().expr().str(self.serialize_name())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the container name for the container when deserializing.
|
|
|
|
pub fn deserialize_name(&self) -> InternedString {
|
|
|
|
match self.deserialize_name {
|
|
|
|
Some(ref name) => name.clone(),
|
|
|
|
None => self.ident.name.as_str(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the container name expression for the container when deserializing.
|
|
|
|
pub fn deserialize_name_expr(&self) -> P<ast::Expr> {
|
|
|
|
AstBuilder::new().expr().str(self.deserialize_name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-08 08:30:29 -08:00
|
|
|
/// Represents container (e.g. struct) attribute information
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ContainerAttrs {
|
2016-02-23 19:51:53 -08:00
|
|
|
name: Name,
|
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.
|
2016-02-23 19:51:53 -08:00
|
|
|
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<Self, Error> {
|
2016-02-08 10:09:18 -08:00
|
|
|
let mut container_attrs = ContainerAttrs {
|
2016-02-23 19:51:53 -08:00
|
|
|
name: Name::new(item.ident),
|
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-23 19:51:53 -08:00
|
|
|
let s = try!(get_str_from_lit(cx, name, lit));
|
|
|
|
|
|
|
|
container_attrs.name.serialize_name = Some(s.clone());
|
|
|
|
container_attrs.name.deserialize_name = Some(s);
|
2016-02-08 08:00:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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));
|
2016-02-23 19:51:53 -08:00
|
|
|
|
|
|
|
container_attrs.name.serialize_name = ser_name;
|
|
|
|
container_attrs.name.deserialize_name = de_name;
|
2016-02-08 08:00:38 -08:00
|
|
|
}
|
|
|
|
|
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-23 19:51:53 -08:00
|
|
|
pub fn name(&self) -> &Name {
|
|
|
|
&self.name
|
2016-02-08 08:00:38 -08:00
|
|
|
}
|
|
|
|
|
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 {
|
2016-02-23 19:51:53 -08:00
|
|
|
name: Name,
|
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 {
|
2016-02-23 19:51:53 -08:00
|
|
|
name: Name::new(variant.node.name),
|
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-23 19:51:53 -08:00
|
|
|
let s = try!(get_str_from_lit(cx, name, lit));
|
|
|
|
|
|
|
|
variant_attrs.name.serialize_name = Some(s.clone());
|
|
|
|
variant_attrs.name.deserialize_name = Some(s);
|
2016-02-08 10:35:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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));
|
2016-02-23 19:51:53 -08:00
|
|
|
|
|
|
|
variant_attrs.name.serialize_name = ser_name;
|
|
|
|
variant_attrs.name.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)
|
|
|
|
}
|
|
|
|
|
2016-02-23 19:51:53 -08:00
|
|
|
pub fn name(&self) -> &Name {
|
|
|
|
&self.name
|
2016-02-08 08:03:46 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-23 19:51:53 -08:00
|
|
|
name: Name,
|
2015-07-23 08:07:49 -07:00
|
|
|
skip_serializing_field: bool,
|
2016-03-06 23:27:12 -08:00
|
|
|
skip_deserializing_field: bool,
|
2016-02-12 22:05:02 -08:00
|
|
|
skip_serializing_field_if: Option<P<ast::Expr>>,
|
2016-02-12 21:53:35 -08:00
|
|
|
default_expr_if_missing: Option<P<ast::Expr>>,
|
2016-02-15 17:39:46 -08:00
|
|
|
serialize_with: Option<P<ast::Expr>>,
|
2016-02-15 20:43:11 -08:00
|
|
|
deserialize_with: Option<P<ast::Expr>>,
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FieldAttrs {
|
2016-02-08 09:51:07 -08:00
|
|
|
/// Extract out the `#[serde(...)]` attributes from a struct field.
|
2016-02-12 21:53:35 -08:00
|
|
|
pub fn from_field(cx: &ExtCtxt,
|
2016-02-12 22:05:02 -08:00
|
|
|
container_ty: &P<ast::Ty>,
|
2016-02-12 21:53:35 -08:00
|
|
|
generics: &ast::Generics,
|
2016-02-21 15:28:25 -08:00
|
|
|
field: &ast::StructField,
|
|
|
|
is_enum: bool) -> Result<Self, Error> {
|
2016-02-12 21:53:35 -08:00
|
|
|
let builder = AstBuilder::new();
|
|
|
|
|
2016-04-10 19:54:54 -07:00
|
|
|
let field_ident = match field.ident {
|
2016-02-08 09:51:07 -08:00
|
|
|
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 {
|
2016-02-23 19:51:53 -08:00
|
|
|
name: Name::new(field_ident),
|
2016-02-08 10:35:42 -08:00
|
|
|
skip_serializing_field: false,
|
2016-03-06 23:27:12 -08:00
|
|
|
skip_deserializing_field: false,
|
2016-02-12 22:05:02 -08:00
|
|
|
skip_serializing_field_if: None,
|
2016-02-12 21:53:35 -08:00
|
|
|
default_expr_if_missing: None,
|
2016-02-15 17:39:46 -08:00
|
|
|
serialize_with: None,
|
2016-02-15 20:43:11 -08:00
|
|
|
deserialize_with: None,
|
2016-02-08 10:35:42 -08:00
|
|
|
};
|
2016-02-08 09:51:07 -08:00
|
|
|
|
2016-04-10 19:54:54 -07:00
|
|
|
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
|
2016-02-08 09:51:07 -08:00
|
|
|
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-23 19:51:53 -08:00
|
|
|
let s = try!(get_str_from_lit(cx, name, lit));
|
|
|
|
|
|
|
|
field_attrs.name.serialize_name = Some(s.clone());
|
|
|
|
field_attrs.name.deserialize_name = Some(s);
|
2016-02-08 10:35:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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));
|
2016-02-23 19:51:53 -08:00
|
|
|
|
|
|
|
field_attrs.name.serialize_name = ser_name;
|
|
|
|
field_attrs.name.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-12 21:53:35 -08:00
|
|
|
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(
|
2016-02-21 15:28:25 -08:00
|
|
|
try!(parse_lit_into_path(cx, name, lit)),
|
2016-02-12 21:53:35 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
field_attrs.default_expr_if_missing = Some(wrapped_expr);
|
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
|
|
|
}
|
|
|
|
|
2016-03-06 23:27:12 -08:00
|
|
|
// Parse `#[serde(skip_deserializing)]`
|
|
|
|
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
|
|
|
|
field_attrs.skip_deserializing_field = true;
|
|
|
|
}
|
|
|
|
|
2016-02-12 22:05:02 -08:00
|
|
|
// Parse `#[serde(skip_serializing_if="...")]`
|
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => {
|
|
|
|
let expr = wrap_skip_serializing(
|
2016-02-21 15:28:25 -08:00
|
|
|
field_ident,
|
|
|
|
try!(parse_lit_into_path(cx, name, lit)),
|
|
|
|
is_enum,
|
2016-02-12 22:05:02 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
field_attrs.skip_serializing_field_if = Some(expr);
|
|
|
|
}
|
|
|
|
|
2016-02-15 17:39:46 -08:00
|
|
|
// Parse `#[serde(serialize_with="...")]`
|
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
|
|
|
|
let expr = wrap_serialize_with(
|
|
|
|
cx,
|
|
|
|
container_ty,
|
|
|
|
generics,
|
2016-02-21 15:28:25 -08:00
|
|
|
field_ident,
|
|
|
|
try!(parse_lit_into_path(cx, name, lit)),
|
|
|
|
is_enum,
|
2016-02-15 17:39:46 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
field_attrs.serialize_with = Some(expr);
|
|
|
|
}
|
|
|
|
|
2016-02-15 20:43:11 -08:00
|
|
|
// Parse `#[serde(deserialize_with="...")]`
|
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
|
|
|
|
let expr = wrap_deserialize_with(
|
|
|
|
cx,
|
2016-04-10 19:54:54 -07:00
|
|
|
&field.ty,
|
2016-02-15 20:43:11 -08:00
|
|
|
generics,
|
2016-02-21 15:28:25 -08:00
|
|
|
try!(parse_lit_into_path(cx, name, lit)),
|
2016-02-15 20:43:11 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
field_attrs.deserialize_with = Some(expr);
|
|
|
|
}
|
|
|
|
|
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-23 19:51:53 -08:00
|
|
|
pub fn name(&self) -> &Name {
|
|
|
|
&self.name
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Predicate for using a field's default value
|
2016-02-12 21:53:35 -08:00
|
|
|
pub fn expr_is_missing(&self) -> P<ast::Expr> {
|
|
|
|
match self.default_expr_if_missing {
|
|
|
|
Some(ref expr) => expr.clone(),
|
|
|
|
None => {
|
2016-03-30 17:29:27 +02:00
|
|
|
let name = self.name.deserialize_name_expr();
|
2016-02-12 21:53:35 -08:00
|
|
|
AstBuilder::new().expr()
|
|
|
|
.try()
|
|
|
|
.method_call("missing_field").id("visitor")
|
|
|
|
.with_arg(name)
|
|
|
|
.build()
|
|
|
|
}
|
|
|
|
}
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
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
|
|
|
|
2016-03-06 23:27:12 -08:00
|
|
|
pub fn skip_deserializing_field(&self) -> bool {
|
|
|
|
self.skip_deserializing_field
|
|
|
|
}
|
|
|
|
|
2016-02-12 22:05:02 -08:00
|
|
|
pub fn skip_serializing_field_if(&self) -> Option<&P<ast::Expr>> {
|
|
|
|
self.skip_serializing_field_if.as_ref()
|
|
|
|
}
|
|
|
|
|
2016-02-15 17:39:46 -08:00
|
|
|
pub fn serialize_with(&self) -> Option<&P<ast::Expr>> {
|
|
|
|
self.serialize_with.as_ref()
|
|
|
|
}
|
2016-02-15 20:43:11 -08:00
|
|
|
|
|
|
|
pub fn deserialize_with(&self) -> Option<&P<ast::Expr>> {
|
|
|
|
self.deserialize_with.as_ref()
|
|
|
|
}
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
2015-09-07 13:13:32 -07:00
|
|
|
|
2016-02-12 22:05:02 -08: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-12 22:05:02 -08:00
|
|
|
container_ty: &P<ast::Ty>,
|
2016-02-12 21:53:35 -08:00
|
|
|
generics: &ast::Generics,
|
2016-02-21 15:28:25 -08:00
|
|
|
fields: &[ast::StructField],
|
|
|
|
is_enum: bool) -> Result<Vec<FieldAttrs>, Error> {
|
2016-02-08 09:51:07 -08:00
|
|
|
fields.iter()
|
2016-02-21 15:28:25 -08:00
|
|
|
.map(|field| FieldAttrs::from_field(cx, container_ty, generics, field, is_enum))
|
2016-02-08 09:51:07 -08:00
|
|
|
.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,
|
2016-02-23 19:51:53 -08:00
|
|
|
items: &[P<ast::MetaItem>],
|
|
|
|
)-> Result<(Option<InternedString>, Option<InternedString>), Error> {
|
2016-02-08 10:35:42 -08:00
|
|
|
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-23 19:51:53 -08:00
|
|
|
let s = try!(get_str_from_lit(cx, name, lit));
|
|
|
|
ser_name = Some(s);
|
2016-02-08 10:35:42 -08:00
|
|
|
}
|
|
|
|
|
2016-02-12 21:43:23 -08:00
|
|
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
|
2016-02-23 19:51:53 -08:00
|
|
|
let s = try!(get_str_from_lit(cx, name, lit));
|
|
|
|
de_name = Some(s);
|
2016-02-08 10:35:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
cx.span_err(
|
|
|
|
item.span,
|
|
|
|
&format!("unknown rename attribute `{}`",
|
|
|
|
meta_item_to_string(item)));
|
|
|
|
|
|
|
|
return Err(Error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok((ser_name, de_name))
|
|
|
|
}
|
|
|
|
|
feat(codegen): Inhibit generic bounds if skip_serializing
The generated code for a struct like:
struct Test<A, B, C> {
a: X<A>
#[serde(skip_serializing)]
b: B
#[serde(serialize_with="...")]
c: C
}
Used to be:
impl<A, B, C> Serialize for Test<A, B, C>
where A: Serialize,
B: Serialize,
C: Serialize,
{ ... }
Now it is:
impl<A, B, C> Serialize for Test<A, B, C>
where X<A>: Serialize,
{ ... }
Both `skip_serializing` and `serialize_with` mean the type does not need to
implement `Serialize`.
2016-02-28 12:17:29 -08:00
|
|
|
pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
2016-02-08 10:09:18 -08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 21:53:35 -08:00
|
|
|
|
2016-02-21 09:54:03 -08:00
|
|
|
/// 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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-23 19:51:53 -08:00
|
|
|
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<InternedString, Error> {
|
|
|
|
match lit.node {
|
|
|
|
ast::LitKind::Str(ref s, _) => Ok(s.clone()),
|
2016-02-12 21:53:35 -08:00
|
|
|
_ => {
|
|
|
|
cx.span_err(
|
|
|
|
lit.span,
|
2016-02-21 09:54:03 -08:00
|
|
|
&format!("serde annotation `{}` must be a string, not `{}`",
|
2016-02-12 21:53:35 -08:00
|
|
|
name,
|
|
|
|
lit_to_string(lit)));
|
|
|
|
|
|
|
|
return Err(Error);
|
|
|
|
}
|
2016-02-23 19:51:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, Error> {
|
|
|
|
let source = try!(get_str_from_lit(cx, name, lit));
|
2016-02-12 21:53:35 -08:00
|
|
|
|
2016-02-21 09:54:03 -08:00
|
|
|
// 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.
|
2016-03-16 23:33:23 -07:00
|
|
|
let tts = panictry!(parse::parse_tts_from_source_str(
|
2016-02-21 09:54:03 -08:00
|
|
|
format!("<serde {} expansion>", name),
|
2016-02-23 19:51:53 -08:00
|
|
|
(*source).to_owned(),
|
2016-02-21 09:54:03 -08:00
|
|
|
cx.cfg(),
|
2016-03-16 23:33:23 -07:00
|
|
|
cx.parse_sess()));
|
2016-02-21 09:54:03 -08:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2016-02-21 15:28:25 -08:00
|
|
|
let path = match parser.parse_path(PathParsingMode::LifetimeAndTypesWithoutColons) {
|
|
|
|
Ok(path) => path,
|
2016-02-21 09:54:03 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 21:53:35 -08:00
|
|
|
|
2016-02-21 15:28:25 -08:00
|
|
|
Ok(path)
|
2016-02-12 21:53:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it
|
|
|
|
/// from accessing the internal `Deserialize` state.
|
2016-02-21 15:28:25 -08:00
|
|
|
fn wrap_default(path: ast::Path) -> P<ast::Expr> {
|
|
|
|
AstBuilder::new().expr().call()
|
|
|
|
.build_path(path)
|
|
|
|
.build()
|
2016-02-12 21:53:35 -08:00
|
|
|
}
|
2016-02-12 22:05:02 -08:00
|
|
|
|
|
|
|
/// This function wraps the expression in `#[serde(skip_serializing_if="...")]` in a trait to
|
|
|
|
/// prevent it from accessing the internal `Serialize` state.
|
2016-02-21 15:28:25 -08:00
|
|
|
fn wrap_skip_serializing(field_ident: ast::Ident,
|
|
|
|
path: ast::Path,
|
|
|
|
is_enum: bool) -> P<ast::Expr> {
|
|
|
|
let builder = AstBuilder::new();
|
2016-02-12 22:05:02 -08:00
|
|
|
|
2016-02-21 15:28:25 -08:00
|
|
|
let expr = builder.expr()
|
|
|
|
.field(field_ident)
|
|
|
|
.field("value")
|
|
|
|
.self_();
|
2016-02-12 22:05:02 -08:00
|
|
|
|
2016-02-21 15:28:25 -08:00
|
|
|
let expr = if is_enum {
|
|
|
|
expr
|
|
|
|
} else {
|
|
|
|
builder.expr().ref_().build(expr)
|
|
|
|
};
|
2016-02-12 22:05:02 -08:00
|
|
|
|
2016-02-21 15:28:25 -08:00
|
|
|
builder.expr().call()
|
|
|
|
.build_path(path)
|
|
|
|
.arg().build(expr)
|
|
|
|
.build()
|
2016-02-12 22:05:02 -08:00
|
|
|
}
|
2016-02-15 17:39:46 -08:00
|
|
|
|
|
|
|
/// 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<ast::Ty>,
|
|
|
|
generics: &ast::Generics,
|
2016-02-21 15:28:25 -08:00
|
|
|
field_ident: ast::Ident,
|
|
|
|
path: ast::Path,
|
|
|
|
is_enum: bool) -> P<ast::Expr> {
|
|
|
|
let builder = AstBuilder::new();
|
|
|
|
|
|
|
|
let expr = builder.expr()
|
|
|
|
.field(field_ident)
|
|
|
|
.self_();
|
|
|
|
|
|
|
|
let expr = if is_enum {
|
|
|
|
expr
|
|
|
|
} else {
|
|
|
|
builder.expr().ref_().build(expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
let expr = builder.expr().call()
|
|
|
|
.build_path(path)
|
|
|
|
.arg().build(expr)
|
|
|
|
.arg()
|
|
|
|
.id("serializer")
|
|
|
|
.build();
|
|
|
|
|
2016-02-15 17:39:46 -08:00
|
|
|
let where_clause = &generics.where_clause;
|
|
|
|
|
|
|
|
quote_expr!(cx, {
|
|
|
|
trait __SerdeSerializeWith {
|
|
|
|
fn __serde_serialize_with<S>(&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<S>(&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<S>(&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<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
|
|
|
where S: ::serde::ser::Serializer
|
|
|
|
{
|
|
|
|
self.value.__serde_serialize_with(serializer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__SerdeSerializeWithStruct {
|
|
|
|
value: &self.value,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2016-02-15 20:43:11 -08:00
|
|
|
|
|
|
|
/// 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<ast::Ty>,
|
|
|
|
generics: &ast::Generics,
|
2016-02-21 15:28:25 -08:00
|
|
|
path: ast::Path) -> P<ast::Expr> {
|
2016-02-15 20:43:11 -08:00
|
|
|
// 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 where_clause = &generics.where_clause;
|
|
|
|
|
|
|
|
quote_expr!(cx, {
|
|
|
|
struct __SerdeDeserializeWithStruct $generics $where_clause {
|
|
|
|
value: $field_ty,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl $generics ::serde::de::Deserialize for $ty_path $where_clause {
|
2016-04-05 15:53:51 -07:00
|
|
|
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<Self, D::Error>
|
2016-02-15 20:43:11 -08:00
|
|
|
where D: ::serde::de::Deserializer
|
|
|
|
{
|
2016-02-21 15:28:25 -08:00
|
|
|
let value = try!($path(deserializer));
|
2016-02-15 20:43:11 -08:00
|
|
|
Ok(__SerdeDeserializeWithStruct { value: value })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let value: $ty_path = try!(visitor.visit_value());
|
|
|
|
Ok(value.value)
|
|
|
|
})
|
|
|
|
}
|