2015-05-01 15:45:58 -04:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
|
|
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;
|
|
|
|
use syntax::ptr::P;
|
|
|
|
|
2015-09-07 13:13:32 -07:00
|
|
|
use aster;
|
|
|
|
|
2015-05-01 15:45:58 -04:00
|
|
|
/// Represents field name information
|
2015-09-07 13:13:32 -07:00
|
|
|
#[derive(Debug)]
|
2015-05-01 15:45:58 -04:00
|
|
|
pub enum FieldNames {
|
|
|
|
Global(P<ast::Expr>),
|
|
|
|
Format{
|
|
|
|
formats: HashMap<P<ast::Expr>, P<ast::Expr>>,
|
|
|
|
default: P<ast::Expr>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 {
|
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
|
|
|
names: FieldNames,
|
|
|
|
use_default: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FieldAttrs {
|
|
|
|
/// Return a set of formats that the field has attributes for.
|
|
|
|
pub fn formats(&self) -> HashSet<P<ast::Expr>> {
|
|
|
|
match self.names {
|
2016-01-18 12:24:03 -08:00
|
|
|
FieldNames::Format { ref formats, .. } => {
|
2015-05-01 15:45:58 -04:00
|
|
|
let mut set = HashSet::new();
|
|
|
|
for (fmt, _) in formats.iter() {
|
|
|
|
set.insert(fmt.clone());
|
|
|
|
};
|
|
|
|
set
|
|
|
|
},
|
|
|
|
_ => HashSet::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return an expression for the field key name for serialisation.
|
|
|
|
///
|
|
|
|
/// The resulting expression assumes that `S` refers to a type
|
|
|
|
/// that implements `Serializer`.
|
2015-09-07 16:44:56 -07:00
|
|
|
pub fn serializer_key_expr(&self, cx: &ExtCtxt) -> P<ast::Expr> {
|
2015-05-01 15:45:58 -04:00
|
|
|
match self.names {
|
2015-09-07 16:44:56 -07:00
|
|
|
FieldNames::Global(ref name) => name.clone(),
|
|
|
|
FieldNames::Format { ref formats, ref default } => {
|
2015-05-01 15:45:58 -04:00
|
|
|
let arms = formats.iter()
|
|
|
|
.map(|(fmt, lit)| {
|
|
|
|
quote_arm!(cx, $fmt => { $lit })
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
quote_expr!(cx,
|
2015-09-07 13:13:32 -07:00
|
|
|
match S::format() {
|
|
|
|
$arms
|
|
|
|
_ => { $default }
|
|
|
|
}
|
|
|
|
)
|
2015-05-01 15:45:58 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the default field name for the field.
|
|
|
|
pub fn default_key_expr(&self) -> &P<ast::Expr> {
|
|
|
|
match self.names {
|
|
|
|
FieldNames::Global(ref expr) => expr,
|
2016-01-18 12:24:03 -08:00
|
|
|
FieldNames::Format { ref default, .. } => default,
|
2015-05-01 15:45:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the field name for the field in the specified format.
|
|
|
|
pub fn key_expr(&self, format: &P<ast::Expr>) -> &P<ast::Expr> {
|
|
|
|
match self.names {
|
2015-09-07 13:13:32 -07:00
|
|
|
FieldNames::Global(ref expr) => expr,
|
|
|
|
FieldNames::Format { ref formats, ref default } => {
|
2015-05-01 15:45:58 -04:00
|
|
|
formats.get(format).unwrap_or(default)
|
2015-09-07 13:13:32 -07:00
|
|
|
}
|
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
|
|
|
|
|
|
|
pub struct FieldAttrsBuilder<'a> {
|
|
|
|
builder: &'a aster::AstBuilder,
|
|
|
|
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-09-07 13:13:32 -07:00
|
|
|
name: Option<P<ast::Expr>>,
|
|
|
|
format_rename: HashMap<P<ast::Expr>, P<ast::Expr>>,
|
|
|
|
use_default: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FieldAttrsBuilder<'a> {
|
|
|
|
pub fn new(builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> {
|
|
|
|
FieldAttrsBuilder {
|
|
|
|
builder: builder,
|
|
|
|
skip_serializing_field: false,
|
2015-09-07 16:44:56 -07:00
|
|
|
skip_serializing_field_if_empty: false,
|
|
|
|
skip_serializing_field_if_none: false,
|
2015-09-07 13:13:32 -07:00
|
|
|
name: None,
|
|
|
|
format_rename: HashMap::new(),
|
|
|
|
use_default: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn field(mut self, field: &ast::StructField) -> FieldAttrsBuilder<'a> {
|
|
|
|
match field.node.kind {
|
|
|
|
ast::NamedField(name, _) => {
|
|
|
|
self.name = Some(self.builder.expr().str(name));
|
|
|
|
}
|
|
|
|
ast::UnnamedField(_) => { }
|
|
|
|
};
|
|
|
|
|
|
|
|
self.attrs(&field.node.attrs)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn attrs(self, attrs: &[ast::Attribute]) -> FieldAttrsBuilder<'a> {
|
|
|
|
attrs.iter().fold(self, FieldAttrsBuilder::attr)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn attr(self, attr: &ast::Attribute) -> FieldAttrsBuilder<'a> {
|
|
|
|
match attr.node.value.node {
|
|
|
|
ast::MetaList(ref name, ref items) if name == &"serde" => {
|
2015-09-07 16:44:56 -07:00
|
|
|
attr::mark_used(&attr);
|
2015-09-07 13:13:32 -07:00
|
|
|
items.iter().fold(self, FieldAttrsBuilder::meta_item)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn meta_item(mut self, meta_item: &P<ast::MetaItem>) -> FieldAttrsBuilder<'a> {
|
|
|
|
match meta_item.node {
|
|
|
|
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
|
|
|
|
let expr = self.builder.expr().build_lit(P(lit.clone()));
|
|
|
|
|
|
|
|
self.name(expr)
|
|
|
|
}
|
|
|
|
ast::MetaList(ref name, ref items) if name == &"rename" => {
|
|
|
|
for item in items {
|
|
|
|
match item.node {
|
|
|
|
ast::MetaNameValue(ref name, ref lit) => {
|
|
|
|
let name = self.builder.expr().str(name);
|
|
|
|
let expr = self.builder.expr().build_lit(P(lit.clone()));
|
|
|
|
|
|
|
|
self = self.format_rename(name, expr);
|
|
|
|
}
|
|
|
|
_ => { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
ast::MetaWord(ref name) if name == &"default" => {
|
|
|
|
self.default()
|
|
|
|
}
|
|
|
|
ast::MetaWord(ref name) if name == &"skip_serializing" => {
|
|
|
|
self.skip_serializing_field()
|
|
|
|
}
|
2015-09-07 16:44:56 -07:00
|
|
|
ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => {
|
|
|
|
self.skip_serializing_field_if_empty()
|
|
|
|
}
|
|
|
|
ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => {
|
|
|
|
self.skip_serializing_field_if_none()
|
|
|
|
}
|
2015-09-07 13:13:32 -07:00
|
|
|
_ => {
|
|
|
|
// Ignore unknown meta variables for now.
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn skip_serializing_field(mut self) -> FieldAttrsBuilder<'a> {
|
|
|
|
self.skip_serializing_field = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2015-09-07 16:44:56 -07:00
|
|
|
pub fn skip_serializing_field_if_empty(mut self) -> FieldAttrsBuilder<'a> {
|
|
|
|
self.skip_serializing_field_if_empty = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn skip_serializing_field_if_none(mut self) -> FieldAttrsBuilder<'a> {
|
|
|
|
self.skip_serializing_field_if_none = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2015-09-07 13:13:32 -07:00
|
|
|
pub fn name(mut self, name: P<ast::Expr>) -> FieldAttrsBuilder<'a> {
|
|
|
|
self.name = Some(name);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn format_rename(mut self, format: P<ast::Expr>, name: P<ast::Expr>) -> FieldAttrsBuilder<'a> {
|
|
|
|
self.format_rename.insert(format, name);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default(mut self) -> FieldAttrsBuilder<'a> {
|
|
|
|
self.use_default = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build(self) -> FieldAttrs {
|
|
|
|
let name = self.name.expect("here");
|
|
|
|
let names = if self.format_rename.is_empty() {
|
|
|
|
FieldNames::Global(name)
|
|
|
|
} else {
|
|
|
|
FieldNames::Format {
|
|
|
|
formats: self.format_rename,
|
|
|
|
default: name,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
FieldAttrs {
|
|
|
|
skip_serializing_field: self.skip_serializing_field,
|
2015-09-07 16:44:56 -07:00
|
|
|
skip_serializing_field_if_empty: self.skip_serializing_field_if_empty,
|
|
|
|
skip_serializing_field_if_none: self.skip_serializing_field_if_none,
|
2015-09-07 13:13:32 -07:00
|
|
|
names: names,
|
|
|
|
use_default: self.use_default,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|