Merge pull request #1015 from spinda/with-variant

implement (de)serialize_with for variants
This commit is contained in:
David Tolnay
2017-09-09 10:49:24 -07:00
committed by GitHub
18 changed files with 919 additions and 67 deletions
+28
View File
@@ -510,6 +510,8 @@ pub struct Variant {
skip_deserializing: bool,
skip_serializing: bool,
other: bool,
serialize_with: Option<syn::Path>,
deserialize_with: Option<syn::Path>,
}
impl Variant {
@@ -520,6 +522,8 @@ impl Variant {
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
let mut rename_all = Attr::none(cx, "rename_all");
let mut other = BoolAttr::none(cx, "other");
let mut serialize_with = Attr::none(cx, "serialize_with");
let mut deserialize_with = Attr::none(cx, "deserialize_with");
for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
@@ -569,6 +573,20 @@ impl Variant {
other.set_true();
}
// Parse `#[serde(serialize_with = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "serialize_with" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
serialize_with.set(path);
}
}
// Parse `#[serde(deserialize_with = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "deserialize_with" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
deserialize_with.set(path);
}
}
MetaItem(ref meta_item) => {
cx.error(format!("unknown serde variant attribute `{}`", meta_item.name()));
}
@@ -595,6 +613,8 @@ impl Variant {
skip_deserializing: skip_deserializing.get(),
skip_serializing: skip_serializing.get(),
other: other.get(),
serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(),
}
}
@@ -626,6 +646,14 @@ impl Variant {
pub fn other(&self) -> bool {
self.other
}
pub fn serialize_with(&self) -> Option<&syn::Path> {
self.serialize_with.as_ref()
}
pub fn deserialize_with(&self) -> Option<&syn::Path> {
self.deserialize_with.as_ref()
}
}
/// Represents field attribute information
+56
View File
@@ -15,6 +15,7 @@ use Ctxt;
pub fn check(cx: &Ctxt, cont: &Container) {
check_getter(cx, cont);
check_identifier(cx, cont);
check_variant_skip_attrs(cx, cont);
}
/// Getters are only allowed inside structs (not enums) with the `remote`
@@ -94,3 +95,58 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
}
}
}
/// Skip-(de)serializing attributes are not allowed on variants marked
/// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
let variants = match cont.body {
Body::Enum(ref variants) => variants,
Body::Struct(_, _) => {
return;
}
};
for variant in variants.iter() {
if variant.attrs.serialize_with().is_some() {
if variant.attrs.skip_serializing() {
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
#[serde(skip_serializing)]", variant.ident));
}
for (i, field) in variant.fields.iter().enumerate() {
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|ident| format!("`{}`", ident));
if field.attrs.skip_serializing() {
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
a field {} marked with #[serde(skip_serializing)]",
variant.ident, ident));
}
if field.attrs.skip_serializing_if().is_some() {
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
a field {} marked with #[serde(skip_serializing_if)]",
variant.ident, ident));
}
}
}
if variant.attrs.deserialize_with().is_some() {
if variant.attrs.skip_deserializing() {
cx.error(format!("variant `{}` cannot have both #[serde(deserialize_with)] and \
#[serde(skip_deserializing)]", variant.ident));
}
for (i, field) in variant.fields.iter().enumerate() {
if field.attrs.skip_deserializing() {
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|ident| format!("`{}`", ident));
cx.error(format!("variant `{}` cannot have both #[serde(deserialize_with)] \
and a field {} marked with #[serde(skip_deserializing)]",
variant.ident, ident));
}
}
}
}
}