Derive for remote types

This commit is contained in:
David Tolnay
2017-04-08 22:42:42 -07:00
parent cc933b9cdb
commit a6d172111b
7 changed files with 620 additions and 242 deletions
+9 -2
View File
@@ -1,5 +1,6 @@
use syn;
use attr;
use check;
use Ctxt;
pub struct Item<'a> {
@@ -62,12 +63,14 @@ impl<'a> Item<'a> {
}
}
Item {
let item = Item {
ident: item.ident.clone(),
attrs: attrs,
body: body,
generics: &item.generics,
}
};
check::check(cx, &item);
item
}
}
@@ -81,6 +84,10 @@ impl<'a> Body<'a> {
Body::Struct(_, ref fields) => Box::new(fields.iter()),
}
}
pub fn has_getter(&self) -> bool {
self.all_fields().any(|f| f.attrs.getter().is_some())
}
}
fn enum_from_ast<'a>(cx: &Ctxt, variants: &'a [syn::Variant]) -> Vec<Variant<'a>> {
+28
View File
@@ -102,6 +102,7 @@ pub struct Item {
tag: EnumTag,
from_type: Option<syn::Ty>,
into_type: Option<syn::Ty>,
remote: Option<syn::Path>,
}
/// Styles of representing an enum.
@@ -151,6 +152,7 @@ impl Item {
let mut content = Attr::none(cx, "content");
let mut from_type = Attr::none(cx, "from");
let mut into_type = Attr::none(cx, "into");
let mut remote = Attr::none(cx, "remote");
for meta_items in item.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
@@ -290,6 +292,13 @@ impl Item {
}
}
// Parse `#[serde(remote = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "remote" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
remote.set(path);
}
}
MetaItem(ref meta_item) => {
cx.error(format!("unknown serde container attribute `{}`",
meta_item.name()));
@@ -361,6 +370,7 @@ impl Item {
tag: tag,
from_type: from_type.get(),
into_type: into_type.get(),
remote: remote.get(),
}
}
@@ -399,6 +409,10 @@ impl Item {
pub fn into_type(&self) -> Option<&syn::Ty> {
self.into_type.as_ref()
}
pub fn remote(&self) -> Option<&syn::Path> {
self.remote.as_ref()
}
}
/// Represents variant attribute information
@@ -531,6 +545,7 @@ pub struct Field {
ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>,
borrowed_lifetimes: BTreeSet<syn::Lifetime>,
getter: Option<syn::Path>,
}
/// Represents the default to use for a field when deserializing.
@@ -558,6 +573,7 @@ impl Field {
let mut ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound");
let mut borrowed_lifetimes = Attr::none(cx, "borrow");
let mut getter = Attr::none(cx, "getter");
let ident = match field.ident {
Some(ref ident) => ident.to_string(),
@@ -676,6 +692,13 @@ impl Field {
}
}
// Parse `#[serde(getter = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "getter" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
getter.set(path);
}
}
MetaItem(ref meta_item) => {
cx.error(format!("unknown serde field attribute `{}`", meta_item.name()));
}
@@ -737,6 +760,7 @@ impl Field {
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
borrowed_lifetimes: borrowed_lifetimes,
getter: getter.get(),
}
}
@@ -788,6 +812,10 @@ impl Field {
pub fn borrowed_lifetimes(&self) -> &BTreeSet<syn::Lifetime> {
&self.borrowed_lifetimes
}
pub fn getter(&self) -> Option<&syn::Path> {
self.getter.as_ref()
}
}
type SerAndDe<T> = (Option<T>, Option<T>);
+20
View File
@@ -0,0 +1,20 @@
use ast::{Body, Item};
use Ctxt;
/// Cross-cutting checks that require looking at more than a single attrs
/// object. Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, item: &Item) {
match item.body {
Body::Enum(_) => {
if item.body.has_getter() {
cx.error("#[serde(getter = \"...\")] is not allowed in an enum");
}
}
Body::Struct(_, _) => {
if item.body.has_getter() && item.attrs.remote().is_none() {
cx.error("#[serde(getter = \"...\")] can only be used in structs \
that have #[serde(remote = \"...\")]");
}
}
}
}
+1
View File
@@ -9,3 +9,4 @@ mod ctxt;
pub use ctxt::Ctxt;
mod case;
mod check;