use super::{attrs::EnumAttrs, Variant}; use crate::attrs::VariantAttrs; use core::panic; use darling::{FromDeriveInput, FromVariant}; use quote::quote; use syn::{DataEnum, DeriveInput}; pub struct Enum { attrs: EnumAttrs, variants: Vec, ident: syn::Ident, generics: syn::Generics, } impl Enum { fn impl_de_untagged(&self) -> proc_macro2::TokenStream { let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); let name = &self.ident; let variant_branches = self .variants .iter() .filter_map(|variant| variant.untagged_branch()); quote! { impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause { fn deserialize( reader: &mut quick_xml::NsReader, start: &quick_xml::events::BytesStart, empty: bool ) -> Result { #(#variant_branches);* Err(rustical_xml::XmlError::InvalidVariant("could not match".to_owned())) } } } } fn impl_de_tagged(&self) -> proc_macro2::TokenStream { let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); let name = &self.ident; let variant_branches = self.variants.iter().filter_map(Variant::tagged_branch); quote! { impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause { fn deserialize( reader: &mut quick_xml::NsReader, start: &quick_xml::events::BytesStart, empty: bool ) -> Result { let (_ns, name) = reader.resolve_element(start.name()); match name.as_ref() { #(#variant_branches),* name => { // Handle invalid variant name Err(rustical_xml::XmlError::InvalidVariant(String::from_utf8_lossy(name).to_string())) } } } } } } pub fn impl_de(&self) -> proc_macro2::TokenStream { match self.attrs.untagged.is_present() { true => self.impl_de_untagged(), false => self.impl_de_tagged(), } } pub fn parse(input: &DeriveInput, data: &DataEnum) -> Self { let attrs = EnumAttrs::from_derive_input(input).unwrap(); Self { variants: data .variants .iter() .map(|variant| Variant { attrs: VariantAttrs::from_variant(variant).unwrap(), variant: variant.to_owned(), }) .collect(), attrs, ident: input.ident.to_owned(), generics: input.generics.to_owned(), } } 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); quote! { impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause { fn serialize( &self, ns: Option<::quick_xml::name::Namespace>, tag: Option<&[u8]>, namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>, writer: &mut ::quick_xml::Writer ) -> ::std::io::Result<()> { use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; let prefix = ns .map(|ns| namespaces.get(&ns)) .unwrap_or(None) .map(|prefix| [*prefix, b":"].concat()); let has_prefix = prefix.is_some(); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname)); const enum_untagged: bool = #enum_untagged; if let Some(qname) = &qname { let mut bytes_start = BytesStart::from(qname.to_owned()); if !has_prefix { if let Some(ns) = &ns { bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref())); } } writer.write_event(Event::Start(bytes_start))?; } #(#variant_serializers);* if let Some(qname) = &qname { writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?; } Ok(()) } fn attributes<'a>(&self) -> Option>> { None } } } } pub fn impl_xml_document(&self) -> proc_macro2::TokenStream { if self.attrs.untagged.is_present() { panic!("XmlDocument only supported for untagged enums"); } let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); let ident = &self.ident; quote! { impl #impl_generics ::rustical_xml::XmlDocument for #ident #type_generics #where_clause { fn parse(mut reader: ::quick_xml::NsReader) -> Result where Self: ::rustical_xml::XmlDeserialize { use ::quick_xml::events::Event; let mut buf = Vec::new(); loop { let event = reader.read_event_into(&mut buf)?; let empty = matches!(event, Event::Empty(_)); match event { Event::Start(start) | Event::Empty(start) => { return ::deserialize(&mut reader, &start, empty); } Event::Eof => return Err(::rustical_xml::XmlError::Eof), Event::Text(bytes_text) => { return Err(::rustical_xml::XmlError::UnsupportedEvent("Text")); } Event::CData(cdata) => { return Err(::rustical_xml::XmlError::UnsupportedEvent("CDATA")); } Event::Comment(_) => { /* ignore */ } Event::Decl(_) => { /* ignore */ // return Err(::rustical_xml::XmlError::UnsupportedEvent("Declaration")); } Event::PI(_) => { return Err(::rustical_xml::XmlError::UnsupportedEvent("Processing instruction")); } Event::DocType(doctype) => { return Err(::rustical_xml::XmlError::UnsupportedEvent("Doctype in the middle of the document")); } Event::End(end) => { return Err(::rustical_xml::XmlError::UnsupportedEvent("Premature end")); } }; } } } } } }