refactor(codegen): Simplify parsing attributes
This commit is contained in:
parent
76cca814f0
commit
c68ab508c0
@ -5,7 +5,10 @@ use syntax::ast;
|
|||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
|
use aster;
|
||||||
|
|
||||||
/// Represents field name information
|
/// Represents field name information
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum FieldNames {
|
pub enum FieldNames {
|
||||||
Global(P<ast::Expr>),
|
Global(P<ast::Expr>),
|
||||||
Format{
|
Format{
|
||||||
@ -15,6 +18,7 @@ pub enum FieldNames {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents field attribute information
|
/// Represents field attribute information
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FieldAttrs {
|
pub struct FieldAttrs {
|
||||||
skip_serializing_field: bool,
|
skip_serializing_field: bool,
|
||||||
names: FieldNames,
|
names: FieldNames,
|
||||||
@ -22,36 +26,6 @@ pub struct FieldAttrs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FieldAttrs {
|
impl FieldAttrs {
|
||||||
/// Create a FieldAttr with a single default field name
|
|
||||||
pub fn new(
|
|
||||||
skip_serializing_field: bool,
|
|
||||||
default_value: bool,
|
|
||||||
name: P<ast::Expr>,
|
|
||||||
) -> FieldAttrs {
|
|
||||||
FieldAttrs {
|
|
||||||
skip_serializing_field: skip_serializing_field,
|
|
||||||
names: FieldNames::Global(name),
|
|
||||||
use_default: default_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a FieldAttr with format specific field names
|
|
||||||
pub fn new_with_formats(
|
|
||||||
skip_serializing_field: bool,
|
|
||||||
default_value: bool,
|
|
||||||
default_name: P<ast::Expr>,
|
|
||||||
formats: HashMap<P<ast::Expr>, P<ast::Expr>>,
|
|
||||||
) -> FieldAttrs {
|
|
||||||
FieldAttrs {
|
|
||||||
skip_serializing_field: skip_serializing_field,
|
|
||||||
names: FieldNames::Format {
|
|
||||||
formats: formats,
|
|
||||||
default: default_name,
|
|
||||||
},
|
|
||||||
use_default: default_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a set of formats that the field has attributes for.
|
/// Return a set of formats that the field has attributes for.
|
||||||
pub fn formats(&self) -> HashSet<P<ast::Expr>> {
|
pub fn formats(&self) -> HashSet<P<ast::Expr>> {
|
||||||
match self.names {
|
match self.names {
|
||||||
@ -72,20 +46,19 @@ impl FieldAttrs {
|
|||||||
/// that implements `Serializer`.
|
/// that implements `Serializer`.
|
||||||
pub fn serializer_key_expr(self, cx: &ExtCtxt) -> P<ast::Expr> {
|
pub fn serializer_key_expr(self, cx: &ExtCtxt) -> P<ast::Expr> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Global(x) => x,
|
FieldNames::Global(name) => name,
|
||||||
FieldNames::Format{formats, default} => {
|
FieldNames::Format { formats, default } => {
|
||||||
let arms = formats.iter()
|
let arms = formats.iter()
|
||||||
.map(|(fmt, lit)| {
|
.map(|(fmt, lit)| {
|
||||||
quote_arm!(cx, $fmt => { $lit })
|
quote_arm!(cx, $fmt => { $lit })
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
quote_expr!(cx,
|
quote_expr!(cx,
|
||||||
{
|
match S::format() {
|
||||||
match S::format() {
|
$arms
|
||||||
$arms
|
_ => { $default }
|
||||||
_ => { $default }
|
}
|
||||||
}
|
)
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,17 +67,17 @@ impl FieldAttrs {
|
|||||||
pub fn default_key_expr(&self) -> &P<ast::Expr> {
|
pub fn default_key_expr(&self) -> &P<ast::Expr> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Global(ref expr) => expr,
|
FieldNames::Global(ref expr) => expr,
|
||||||
FieldNames::Format{formats: _, ref default} => default
|
FieldNames::Format{formats: _, ref default} => default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the field name for the field in the specified format.
|
/// Return the field name for the field in the specified format.
|
||||||
pub fn key_expr(&self, format: &P<ast::Expr>) -> &P<ast::Expr> {
|
pub fn key_expr(&self, format: &P<ast::Expr>) -> &P<ast::Expr> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Global(ref expr) =>
|
FieldNames::Global(ref expr) => expr,
|
||||||
expr,
|
FieldNames::Format { ref formats, ref default } => {
|
||||||
FieldNames::Format{ref formats, ref default} =>
|
|
||||||
formats.get(format).unwrap_or(default)
|
formats.get(format).unwrap_or(default)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,3 +91,121 @@ impl FieldAttrs {
|
|||||||
self.skip_serializing_field
|
self.skip_serializing_field
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FieldAttrsBuilder<'a> {
|
||||||
|
builder: &'a aster::AstBuilder,
|
||||||
|
skip_serializing_field: bool,
|
||||||
|
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,
|
||||||
|
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" => {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Ignore unknown meta variables for now.
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_serializing_field(mut self) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.skip_serializing_field = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
names: names,
|
||||||
|
use_default: self.use_default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -543,11 +543,13 @@ fn deserialize_item_enum(
|
|||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
enum_def.variants.iter()
|
enum_def.variants.iter()
|
||||||
.map(|variant|
|
.map(|variant| {
|
||||||
attr::FieldAttrs::new(
|
let expr = builder.expr().str(variant.node.name);
|
||||||
false,
|
attr::FieldAttrsBuilder::new(builder)
|
||||||
true,
|
.name(expr)
|
||||||
builder.expr().str(variant.node.name)))
|
.default()
|
||||||
|
.build()
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,147 +1,17 @@
|
|||||||
use std::collections::HashMap;
|
use syntax::ast;
|
||||||
|
use syntax::ext::base::ExtCtxt;
|
||||||
|
|
||||||
use aster;
|
use aster;
|
||||||
|
use attr::{FieldAttrs, FieldAttrsBuilder};
|
||||||
use syntax::ast;
|
|
||||||
use syntax::attr;
|
|
||||||
use syntax::ext::base::ExtCtxt;
|
|
||||||
use syntax::ptr::P;
|
|
||||||
|
|
||||||
use attr::FieldAttrs;
|
|
||||||
|
|
||||||
enum Rename<'a> {
|
|
||||||
None,
|
|
||||||
Global(&'a ast::Lit),
|
|
||||||
Format(HashMap<P<ast::Expr>, &'a ast::Lit>)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rename<'a>(
|
|
||||||
builder: &aster::AstBuilder,
|
|
||||||
mi: &'a ast::MetaItem,
|
|
||||||
) -> Option<Rename<'a>>
|
|
||||||
{
|
|
||||||
match mi.node {
|
|
||||||
ast::MetaNameValue(ref n, ref lit) => {
|
|
||||||
if n == &"rename" {
|
|
||||||
Some(Rename::Global(lit))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ast::MetaList(ref n, ref items) => {
|
|
||||||
if n == &"rename" {
|
|
||||||
let mut m = HashMap::new();
|
|
||||||
m.extend(
|
|
||||||
items.iter()
|
|
||||||
.filter_map(
|
|
||||||
|item|
|
|
||||||
match item.node {
|
|
||||||
ast::MetaNameValue(ref n, ref lit) =>
|
|
||||||
Some((builder.expr().str(n),
|
|
||||||
lit)),
|
|
||||||
_ => None
|
|
||||||
}));
|
|
||||||
Some(Rename::Format(m))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_value(mi: &ast::MetaItem) -> bool {
|
|
||||||
if let ast::MetaItem_::MetaWord(ref n) = mi.node {
|
|
||||||
n == &"default"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_serializing_field(mi: &ast::MetaItem) -> bool {
|
|
||||||
if let ast::MetaItem_::MetaWord(ref n) = mi.node {
|
|
||||||
n == &"skip_serializing"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field_attrs<'a>(
|
|
||||||
builder: &aster::AstBuilder,
|
|
||||||
field: &'a ast::StructField,
|
|
||||||
) -> (Rename<'a>, bool, bool) {
|
|
||||||
field.node.attrs.iter()
|
|
||||||
.find(|sa| {
|
|
||||||
if let ast::MetaList(ref n, _) = sa.node.value.node {
|
|
||||||
n == &"serde"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.and_then(|sa| {
|
|
||||||
if let ast::MetaList(_, ref vals) = sa.node.value.node {
|
|
||||||
attr::mark_used(&sa);
|
|
||||||
Some((
|
|
||||||
vals.iter()
|
|
||||||
.fold(None, |v, mi| v.or(rename(builder, mi)))
|
|
||||||
.unwrap_or(Rename::None),
|
|
||||||
vals.iter().any(|mi| default_value(mi)),
|
|
||||||
vals.iter().any(|mi| skip_serializing_field(mi)),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Some((Rename::None, false, false))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or((Rename::None, false, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn struct_field_attrs(
|
pub fn struct_field_attrs(
|
||||||
cx: &ExtCtxt,
|
_cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
struct_def: &ast::StructDef,
|
struct_def: &ast::StructDef,
|
||||||
) -> Vec<FieldAttrs> {
|
) -> Vec<FieldAttrs> {
|
||||||
struct_def.fields.iter()
|
struct_def.fields.iter()
|
||||||
.map(|field| {
|
.map(|field| {
|
||||||
match field_attrs(builder, field) {
|
FieldAttrsBuilder::new(builder).field(field).build()
|
||||||
(Rename::Global(rename), default_value, skip_serializing_field) =>
|
|
||||||
FieldAttrs::new(
|
|
||||||
skip_serializing_field,
|
|
||||||
default_value,
|
|
||||||
builder.expr().build_lit(P(rename.clone()))),
|
|
||||||
(Rename::Format(renames), default_value, skip_serializing_field) => {
|
|
||||||
let mut res = HashMap::new();
|
|
||||||
res.extend(
|
|
||||||
renames.into_iter()
|
|
||||||
.map(|(k,v)|
|
|
||||||
(k, builder.expr().build_lit(P(v.clone())))));
|
|
||||||
FieldAttrs::new_with_formats(
|
|
||||||
skip_serializing_field,
|
|
||||||
default_value,
|
|
||||||
default_field_name(cx, builder, field.node.kind),
|
|
||||||
res)
|
|
||||||
},
|
|
||||||
(Rename::None, default_value, skip_serializing_field) => {
|
|
||||||
FieldAttrs::new(
|
|
||||||
skip_serializing_field,
|
|
||||||
default_value,
|
|
||||||
default_field_name(cx, builder, field.node.kind))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_field_name(
|
|
||||||
cx: &ExtCtxt,
|
|
||||||
builder: &aster::AstBuilder,
|
|
||||||
kind: ast::StructFieldKind,
|
|
||||||
) -> P<ast::Expr> {
|
|
||||||
match kind {
|
|
||||||
ast::NamedField(name, _) => {
|
|
||||||
builder.expr().str(name)
|
|
||||||
}
|
|
||||||
ast::UnnamedField(_) => {
|
|
||||||
cx.bug("struct has named and unnamed fields")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user