diff --git a/crates/xml/derive/src/variant.rs b/crates/xml/derive/src/variant.rs index d024ad0..a3d8c06 100644 --- a/crates/xml/derive/src/variant.rs +++ b/crates/xml/derive/src/variant.rs @@ -172,4 +172,38 @@ impl Variant { } }) } + + pub fn se_branch(&self) -> proc_macro2::TokenStream { + let ident = self.ident(); + let variant_name = self.xml_name(); + + match &self.variant.fields { + Fields::Named(_) => { + panic!( + "struct variants are not supported, please use a tuple variant with a struct" + ) + } + Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { + if unnamed.len() != 1 { + panic!("tuple variants should contain exactly one element"); + } + quote! { + if let Self::#ident(val) = &self { + if !enum_untagged { + ::rustical_xml::XmlSerialize::serialize(val, None, Some(#variant_name), writer)?; + } else { + ::rustical_xml::XmlSerialize::serialize(val, None, None, writer)?; + }; + } + } + } + Fields::Unit => { + quote! { + if let Self::#ident = self { + ::rustical_xml::XmlSerialize::serialize(&(), ns, tag, writer)?; + } + } + } + } + } } diff --git a/crates/xml/derive/src/xml_enum.rs b/crates/xml/derive/src/xml_enum.rs index f440085..8d77083 100644 --- a/crates/xml/derive/src/xml_enum.rs +++ b/crates/xml/derive/src/xml_enum.rs @@ -92,27 +92,38 @@ impl Enum { pub fn impl_se(&self) -> proc_macro2::TokenStream { let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); let ident = &self.ident; + let enum_untagged = self.attrs.untagged.is_present(); + let variant_serializers = self.variants.iter().map(Variant::se_branch); - // TODO: Implement attributes quote! { impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause { fn serialize( &self, + ns: Option<&[u8]>, tag: Option<&[u8]>, writer: &mut ::quick_xml::Writer ) -> ::std::io::Result<()> { use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; let tag_str = tag.map(String::from_utf8_lossy); + const enum_untagged: bool = #enum_untagged; if let Some(tag) = &tag_str { - writer.write_event(Event::Start(BytesStart::new(tag.to_owned())))?; + let bytes_start = BytesStart::new(tag.to_owned()); + writer.write_event(Event::Start(bytes_start))?; } + + #(#variant_serializers);* + if let Some(tag) = &tag_str { writer.write_event(Event::End(BytesEnd::new(tag.to_owned())))?; } Ok(()) } + + fn attributes<'a>(&self) -> Vec<::quick_xml::events::attributes::Attribute<'a>> { + vec![] + } } } } diff --git a/crates/xml/src/lib.rs b/crates/xml/src/lib.rs index 2ea4e8f..a5d6811 100644 --- a/crates/xml/src/lib.rs +++ b/crates/xml/src/lib.rs @@ -1,3 +1,4 @@ +use quick_xml::events::BytesEnd; use quick_xml::events::{BytesStart, Event}; use std::io::BufRead; @@ -34,6 +35,27 @@ impl XmlDeserialize for () { } } +impl XmlSerialize for () { + fn serialize( + &self, + ns: Option<&[u8]>, + tag: Option<&[u8]>, + writer: &mut quick_xml::Writer, + ) -> std::io::Result<()> { + let tag_str = tag.map(String::from_utf8_lossy); + + if let Some(tag) = &tag_str { + writer.write_event(Event::Empty(BytesStart::new(tag.clone())))?; + } + Ok(()) + } + + #[allow(refining_impl_trait)] + fn attributes<'a>(&self) -> Vec> { + vec![] + } +} + // TODO: actually implement #[derive(Debug, Clone, PartialEq)] pub struct Unparsed(BytesStart<'static>); diff --git a/crates/xml/tests/se_enum.rs b/crates/xml/tests/se_enum.rs new file mode 100644 index 0000000..dcbb9e5 --- /dev/null +++ b/crates/xml/tests/se_enum.rs @@ -0,0 +1,27 @@ +use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; + +#[test] +fn test_struct_value_tagged() { + #[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)] + #[xml(root = b"propfind")] + struct Document { + prop: Prop, + } + + #[derive(Debug, XmlSerialize, PartialEq)] + enum Prop { + Test(String), + Hello(usize), + Unit, + } + + let mut buf = Vec::new(); + let mut writer = quick_xml::Writer::new(&mut buf); + Document { + prop: Prop::Test("asd".to_owned()), + } + .serialize_root(&mut writer) + .unwrap(); + let out = String::from_utf8(buf).unwrap(); + assert_eq!(out, "asd"); +}