Add enum integration test and fix ser/de logic

This commit is contained in:
rsdy 2022-09-27 12:21:05 +01:00 committed by Dirkjan Ochtman
parent 6455801950
commit 4a61982792
5 changed files with 59 additions and 18 deletions

View File

@ -1,4 +1,4 @@
//! Originally from https://raw.githubusercontent.com/serde-rs/serde/master/serde_derive/src/internals/case.rs
//! Originally from <https://raw.githubusercontent.com/serde-rs/serde/master/serde_derive/src/internals/case.rs>
//! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the
//! case of the source (e.g. `my-field`, `MY_FIELD`).

View File

@ -1,6 +1,6 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
use syn::{spanned::Spanned, ImplGenerics};
use super::{discard_lifetimes, ContainerMeta, FieldMeta, Namespace, VariantMeta};
@ -11,22 +11,37 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
Err(e) => return e.to_compile_error(),
};
let mut xml_generics = input.generics.clone();
let mut xml = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
xml.bounds
.extend(xml_generics.lifetimes().map(|lt| lt.lifetime.clone()));
xml_generics.params.push(xml.into());
let (xml_impl_generics, _, _) = xml_generics.split_for_impl();
match &input.data {
syn::Data::Struct(_) if meta.scalar => {
syn::Error::new(input.span(), "scalar structs are unsupported!").to_compile_error()
}
syn::Data::Struct(ref data) => deserialize_struct(input, data, meta, ident),
syn::Data::Struct(ref data) => {
deserialize_struct(input, data, meta, ident, xml_impl_generics)
}
syn::Data::Enum(_) if !meta.scalar => {
syn::Error::new(input.span(), "non-scalar enums are currently unsupported!")
.to_compile_error()
}
syn::Data::Enum(ref data) => deserialize_enum(input, data, meta),
syn::Data::Enum(ref data) => deserialize_enum(input, data, meta, xml_impl_generics),
_ => todo!(),
}
}
#[rustfmt::skip]
fn deserialize_enum(input: &syn::DeriveInput, data: &syn::DataEnum, meta: ContainerMeta) -> TokenStream {
fn deserialize_enum(
input: &syn::DeriveInput,
data: &syn::DataEnum,
meta: ContainerMeta,
xml_impl_generics: ImplGenerics
) -> TokenStream {
let ident = &input.ident;
let mut variants = TokenStream::new();
@ -38,19 +53,21 @@ fn deserialize_enum(input: &syn::DeriveInput, data: &syn::DataEnum, meta: Contai
};
let serialize_as = meta.serialize_as;
variants.extend(quote!(Ok(#serialize_as) => #ident::#v_ident,));
variants.extend(quote!(Ok(#serialize_as) => Ok(#ident::#v_ident),));
}
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
quote!(
impl #impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> {
match deserializer.take_str() {
#variants
_ => Err(::instant_xml::Error::UnexpectedValue)
}
}
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Scalar;
}
)
}
@ -60,20 +77,14 @@ fn deserialize_struct(
data: &syn::DataStruct,
container_meta: ContainerMeta,
ident: &Ident,
xml_impl_generics: ImplGenerics,
) -> TokenStream {
let default_namespace = match &container_meta.ns.uri {
Some(ns) => quote!(#ns),
None => quote!(""),
};
let mut xml_generics = input.generics.clone();
let mut xml = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
xml.bounds
.extend(xml_generics.lifetimes().map(|lt| lt.lifetime.clone()));
xml_generics.params.push(xml.into());
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
let (xml_impl_generics, _, _) = xml_generics.split_for_impl();
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
for (k, v) in container_meta.ns.prefixes.iter() {

View File

@ -684,7 +684,7 @@ mod tests {
};
assert_eq!(super::ser::to_xml(&input).to_string(),
"impl ToXml for TestEnum { fn serialize < W : :: core :: fmt :: Write + ? :: core :: marker :: Sized > (& self , serializer : & mut instant_xml :: Serializer < W > ,) -> Result < () , instant_xml :: Error > { serializer . write_str (match self { TestEnum :: Foo => \"Foo\" , TestEnum :: Bar => \"Bar\" , TestEnum :: Baz => \"1\" , }) } }"
"impl ToXml for TestEnum { fn serialize < W : :: core :: fmt :: Write + ? :: core :: marker :: Sized > (& self , serializer : & mut instant_xml :: Serializer < W > ,) -> Result < () , instant_xml :: Error > { serializer . write_str (match self { TestEnum :: Foo => \"Foo\" , TestEnum :: Bar => \"Bar\" , TestEnum :: Baz => \"1\" , }) } const KIND : :: instant_xml :: Kind = :: instant_xml :: Kind :: Scalar ; }"
)
}
@ -701,7 +701,7 @@ mod tests {
};
assert_eq!(super::de::from_xml(&input).to_string(),
"impl FromXml < 'xml > for TestEnum { fn deserialize < 'cx > (deserializer : & 'cx mut :: instant_xml :: Deserializer < 'cx , 'xml >) -> Result < Self , :: instant_xml :: Error > { match deserializer . take_str () { Ok (\"Foo\") => TestEnum :: Foo , Ok (\"Bar\") => TestEnum :: Bar , Ok (\"1\") => TestEnum :: Baz , _ => Err (:: instant_xml :: Error :: UnexpectedValue) } } }"
"impl < 'xml > FromXml < 'xml > for TestEnum { fn deserialize < 'cx > (deserializer : & 'cx mut :: instant_xml :: Deserializer < 'cx , 'xml >) -> Result < Self , :: instant_xml :: Error > { match deserializer . take_str () { Ok (\"Foo\") => Ok (TestEnum :: Foo) , Ok (\"Bar\") => Ok (TestEnum :: Bar) , Ok (\"1\") => Ok (TestEnum :: Baz) , _ => Err (:: instant_xml :: Error :: UnexpectedValue) } } const KIND : :: instant_xml :: Kind = :: instant_xml :: Kind :: Scalar ; }"
)
}
@ -789,7 +789,7 @@ mod tests {
Bar,
Baz
}
}).to_string(), "impl ToXml for TestEnum { fn serialize < W : :: core :: fmt :: Write + ? :: core :: marker :: Sized > (& self , serializer : & mut instant_xml :: Serializer < W > ,) -> Result < () , instant_xml :: Error > { serializer . write_str (match self { TestEnum :: Foo => \"1\" , TestEnum :: Bar => \"BAR\" , TestEnum :: Baz => \"BAZ\" , }) } }");
}).to_string(), "impl ToXml for TestEnum { fn serialize < W : :: core :: fmt :: Write + ? :: core :: marker :: Sized > (& self , serializer : & mut instant_xml :: Serializer < W > ,) -> Result < () , instant_xml :: Error > { serializer . write_str (match self { TestEnum :: Foo => \"1\" , TestEnum :: Bar => \"BAR\" , TestEnum :: Baz => \"BAZ\" , }) } const KIND : :: instant_xml :: Kind = :: instant_xml :: Kind :: Scalar ; }");
}
#[test]

View File

@ -56,6 +56,8 @@ fn serialize_enum(
) -> Result<(), instant_xml::Error> {
serializer.write_str(match self { #variants })
}
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Scalar;
}
)
}

View File

@ -41,3 +41,31 @@ fn rename_all_struct() {
assert_eq!(to_string(&instance).unwrap(), serialized);
assert_eq!(from_str::<TestStruct>(serialized), Ok(instance));
}
#[test]
fn rename_all_enum_variant() {
#[derive(Debug, PartialEq, Eq, ToXml, FromXml)]
#[xml(scalar, rename_all = "snake_case")]
pub enum TestEnum {
SnakeCased,
ThisToo,
}
#[derive(Debug, PartialEq, Eq, ToXml, FromXml)]
#[xml(rename_all = "UPPERCASE")]
pub struct TestStruct {
field_1: TestEnum,
#[xml(attribute)]
field_2: TestEnum,
}
let serialized =
r#"<TestStruct FIELD_2="this_too"><FIELD_1>snake_cased</FIELD_1></TestStruct>"#;
let instance = TestStruct {
field_1: TestEnum::SnakeCased,
field_2: TestEnum::ThisToo,
};
assert_eq!(to_string(&instance).unwrap(), serialized);
assert_eq!(from_str::<TestStruct>(serialized), Ok(instance));
}