650 lines
22 KiB
Rust
Raw Normal View History

use std::rc::Rc;
use syntax::ast::{self, TokenTree};
use syntax::attr;
use syntax::codemap::Span;
2015-05-01 15:45:58 -04:00
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};
2015-05-01 15:45:58 -04:00
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<ast::Lit>,
deserialize_name: Option<ast::Lit>,
deny_unknown_fields: bool,
}
impl ContainerAttrs {
/// 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 {
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<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(),
}
}
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,
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,
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")]`
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;
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.
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
#[derive(Debug)]
2015-05-01 15:45:58 -04:00
pub struct FieldAttrs {
ident: ast::Ident,
serialize_name: Option<ast::Lit>,
deserialize_name: Option<ast::Lit>,
skip_serializing_field: bool,
skip_serializing_field_if: Option<P<ast::Expr>>,
default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<P<ast::Expr>>,
deserialize_with: Option<P<ast::Expr>>,
2015-05-01 15:45:58 -04:00
}
impl FieldAttrs {
/// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn from_field(cx: &ExtCtxt,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
field: &ast::StructField) -> Result<Self, Error> {
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)
}
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 08:03:46 -08:00
/// 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 deserializing.
pub fn deserialize_name_expr(&self) -> P<ast::Expr> {
match self.deserialize_name {
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 expr_is_missing(&self) -> P<ast::Expr> {
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()
}
}
2015-05-01 15:45:58 -04:00
}
/// 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<ast::Expr>> {
self.skip_serializing_field_if.as_ref()
}
pub fn serialize_with(&self) -> Option<&P<ast::Expr>> {
self.serialize_with.as_ref()
}
pub fn deserialize_with(&self) -> Option<&P<ast::Expr>> {
self.deserialize_with.as_ref()
}
2015-05-01 15:45:58 -04:00
}
/// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn get_struct_field_attrs(cx: &ExtCtxt,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
fields: &[ast::StructField]) -> Result<Vec<FieldAttrs>, Error> {
fields.iter()
.map(|field| FieldAttrs::from_field(cx, container_ty, generics, field))
.collect()
}
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 {
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<ast::MetaItem>]> {
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<P<ast::Expr>, 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!("<serde {} expansion>", 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<ast::Ty>,
generics: &ast::Generics,
expr: P<ast::Expr>) -> P<ast::Expr> {
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<ast::Ty>,
generics: &ast::Generics,
expr: P<ast::Expr>) -> P<ast::Expr> {
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<ast::Ty>,
generics: &ast::Generics,
expr: P<ast::Expr>) -> P<ast::Expr> {
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,
}
})
}
/// 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,
expr: P<ast::Expr>) -> P<ast::Expr> {
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<D>(deserializer: &mut D) -> Result<Self, D::Error>
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)
})
}