diff --git a/src/de.rs b/src/de.rs index f2caf9a..34e86ad 100644 --- a/src/de.rs +++ b/src/de.rs @@ -13,6 +13,7 @@ pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result 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 Result { + 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::>>()?; + let struct_names = struct_variants + .iter() + .map(|variant| { + Ident::new( + &format!("__{}_{}_Struct", ident, variant.ident), + Span::call_site(), + ) + }) + .collect::>(); + let structs = struct_variants + .iter() + .zip(struct_names.iter()) + .map(|(variant, ident)| variant_as_struct(variant, ident, &input.ident)) + .collect::>>()?; + let unit_variant_idents = unit_variants.iter().map(|v| &v.ident).collect::>(); + let unit_variant_names = unit_variants + .iter() + .cloned() + .map(attr::name_of_variant) + .collect::>>()?; + + Ok(quote! { + const _: () = { + struct __Visitor { + __out: miniserde::export::Option<#ident>, + } + + impl miniserde::Deserialize for #ident { + fn begin(__out: &mut miniserde::export::Option) -> &mut dyn miniserde::de::Visitor { + unsafe { + &mut *{ + __out + as *mut miniserde::export::Option + as *mut __Visitor + } + } + } + } + + impl miniserde::de::Visitor for __Visitor { + fn map(&mut self) -> miniserde::Result> { + 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, + __map: miniserde::export::Option>, + __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(::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 { let ident = &input.ident; let (unit_variants, struct_variants): (Vec<_>, Vec<_>) = diff --git a/tests/deserialize.rs b/tests/deserialize.rs index 460254d..5d29d62 100644 --- a/tests/deserialize.rs +++ b/tests/deserialize.rs @@ -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 = json::from_str(example).unwrap(); + let expected = [B, C { x: 2 }, D]; + assert_eq!(actual, expected); +}