Add support for deserializing internally tagged enums

This commit is contained in:
etwyniel
2019-11-18 16:23:21 +01:00
parent 48f995f2ac
commit 479533ad17
2 changed files with 134 additions and 0 deletions

115
src/de.rs
View File

@@ -13,6 +13,7 @@ pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream
match &tag_type {
TagType::External => deserialize_external(input, enumeration),
TagType::Adjacent { tag, content } => deserialize_adjacent(input, enumeration, tag, content),
TagType::Internal(tag) => deserialize_internal(input, enumeration, tag),
_ => Err(Error::new(
Span::call_site(),
"Only externally tagged enums are supported",
@@ -20,6 +21,120 @@ pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream
}
}
pub fn deserialize_internal(input: &DeriveInput, enumeration: &DataEnum, tag: &str) -> Result<TokenStream> {
let ident = &input.ident;
let (unit_variants, struct_variants): (Vec<_>, Vec<_>) =
enumeration.variants.iter().partition(|v| {
if let Fields::Unit = &v.fields {
true
} else {
false
}
});
let struct_variant_names = struct_variants
.iter()
.cloned()
.map(attr::name_of_variant)
.collect::<Result<Vec<_>>>()?;
let struct_names = struct_variants
.iter()
.map(|variant| {
Ident::new(
&format!("__{}_{}_Struct", ident, variant.ident),
Span::call_site(),
)
})
.collect::<Vec<_>>();
let structs = struct_variants
.iter()
.zip(struct_names.iter())
.map(|(variant, ident)| variant_as_struct(variant, ident, &input.ident))
.collect::<Result<Vec<_>>>()?;
let unit_variant_idents = unit_variants.iter().map(|v| &v.ident).collect::<Vec<_>>();
let unit_variant_names = unit_variants
.iter()
.cloned()
.map(attr::name_of_variant)
.collect::<Result<Vec<_>>>()?;
Ok(quote! {
const _: () = {
struct __Visitor {
__out: miniserde::export::Option<#ident>,
}
impl miniserde::Deserialize for #ident {
fn begin(__out: &mut miniserde::export::Option<Self>) -> &mut dyn miniserde::de::Visitor {
unsafe {
&mut *{
__out
as *mut miniserde::export::Option<Self>
as *mut __Visitor
}
}
}
}
impl miniserde::de::Visitor for __Visitor {
fn map(&mut self) -> miniserde::Result<miniserde::export::Box<dyn miniserde::de::Map + '_>> {
Ok(miniserde::export::Box::new(__State {
#(#struct_names: None,)*
__tag: None,
__map: None,
__out: &mut self.__out,
}))
}
}
struct __State<'a> {
#(#[allow(non_snake_case)] #struct_names: miniserde::export::Option<#struct_names>,)*
__tag: miniserde::export::Option<String>,
__map: miniserde::export::Option<miniserde::export::Box<dyn miniserde::de::Map + 'a>>,
__out: &'a mut miniserde::export::Option<#ident>,
}
#(#structs)*
impl<'a> miniserde::de::Map for __State<'a> {
fn key(&mut self, k: &miniserde::export::str) -> miniserde::Result<&mut dyn miniserde::de::Visitor> {
if k == #tag {
return Ok(<String as miniserde::Deserialize>::begin(&mut self.__tag));
}
if self.__map.is_none() {
let tag = self.__tag.as_ref().ok_or(miniserde::Error)?;
self.__map.replace(match tag.as_ref() {
#(#struct_variant_names => <#struct_names as miniserde::Deserialize>::begin(
unsafe {&mut *(&mut self.#struct_names as *mut miniserde::export::Option<#struct_names>)}
).map()?,)*
_ => return miniserde::export::Err(miniserde::Error),
});
}
self.__map.as_mut().ok_or(miniserde::Error)?.key(k)
}
fn finish(&mut self) -> miniserde::Result<()> {
let tag = self.__tag.take().ok_or(miniserde::Error)?;
match tag.as_str() {
#(#unit_variant_names => {
self.__out.replace(#ident::#unit_variant_idents);
return miniserde::export::Ok(());
})*
_ => (),
}
self.__map.take().ok_or(miniserde::Error)?.finish()?;
match tag.as_str() {
#(#struct_variant_names => {
self.__out.replace(self.#struct_names.take().ok_or(miniserde::Error)?.as_enum());
miniserde::export::Ok(())
})*
_ => miniserde::export::Err(miniserde::Error)
}
}
}
};
})
}
pub fn deserialize_adjacent(input: &DeriveInput, enumeration: &DataEnum, tag: &str, content: &str) -> Result<TokenStream> {
let ident = &input.ident;
let (unit_variants, struct_variants): (Vec<_>, Vec<_>) =

View File

@@ -39,3 +39,22 @@ fn test_adjacent() {
let expected = [A(21), B(42, "everything".to_string()), C { x: 2 }, D];
assert_eq!(actual, expected);
}
#[test]
fn test_internal() {
#[derive(Deserialize_enum, Debug, PartialEq)]
#[serde(tag = "type")]
enum Internal {
#[serde(rename = "renamedB")]
B,
C {
x: i32,
},
D,
}
use Internal::*;
let example = r#"[{"type":"renamedB"},{"type":"C","x":2},{"type":"D"}]"#;
let actual: Vec<Internal> = json::from_str(example).unwrap();
let expected = [B, C { x: 2 }, D];
assert_eq!(actual, expected);
}