diff --git a/crates/xml/derive/src/lib.rs b/crates/xml/derive/src/lib.rs index 4b6759c..6be7312 100644 --- a/crates/xml/derive/src/lib.rs +++ b/crates/xml/derive/src/lib.rs @@ -61,3 +61,15 @@ pub fn derive_xml_document(input: proc_macro::TokenStream) -> proc_macro::TokenS } .into() } + +#[proc_macro_derive(EnumVariants, attributes(xml))] +pub fn derive_enum_variants(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + match &input.data { + syn::Data::Struct(_) => panic!("Struct not supported, use XmlRootTag instead"), + syn::Data::Enum(e) => Enum::parse(&input, e).impl_enum_variants(), + syn::Data::Union(_) => panic!("Union not supported as root"), + } + .into() +} diff --git a/crates/xml/derive/src/xml_enum.rs b/crates/xml/derive/src/xml_enum.rs index 9232eca..05ea7da 100644 --- a/crates/xml/derive/src/xml_enum.rs +++ b/crates/xml/derive/src/xml_enum.rs @@ -193,4 +193,27 @@ impl Enum { } } } + + pub fn impl_enum_variants(&self) -> proc_macro2::TokenStream { + let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); + let ident = &self.ident; + + let tagged_variants = self.variants.iter().map(|variant| { + let ns = match &variant.attrs.common.ns { + Some(ns) => quote! { Some(#ns) }, + None => quote! { None }, + }; + let b_xml_name = variant.xml_name().value(); + let xml_name = String::from_utf8_lossy(&b_xml_name); + quote! {(#ns, #xml_name)} + }); + + quote! { + impl #impl_generics ::rustical_xml::EnumVariants for #ident #type_generics #where_clause { + const TAGGED_VARIANTS: &'static [(Option<::quick_xml::name::Namespace<'static>>, &'static str)] = &[ + #(#tagged_variants),* + ]; + } + } + } } diff --git a/crates/xml/src/lib.rs b/crates/xml/src/lib.rs index c65cbe9..06d387b 100644 --- a/crates/xml/src/lib.rs +++ b/crates/xml/src/lib.rs @@ -14,6 +14,7 @@ pub use se::XmlSerialize; pub use se::XmlSerializeRoot; pub use unparsed::Unparsed; pub use value::{ParseValueError, ValueDeserialize, ValueSerialize}; +pub use xml_derive::EnumVariants; pub use xml_derive::XmlRootTag; pub trait XmlRootTag { @@ -21,3 +22,7 @@ pub trait XmlRootTag { fn root_ns() -> Option>; fn root_ns_prefixes() -> HashMap, &'static [u8]>; } + +pub trait EnumVariants { + const TAGGED_VARIANTS: &'static [(Option>, &'static str)]; +} diff --git a/crates/xml/tests/enum_variants.rs b/crates/xml/tests/enum_variants.rs new file mode 100644 index 0000000..8678825 --- /dev/null +++ b/crates/xml/tests/enum_variants.rs @@ -0,0 +1,36 @@ +use quick_xml::name::Namespace; +use rustical_xml::EnumVariants; + +pub const NS_DAV: Namespace = Namespace(b"DAV:"); +pub const NS_DAVPUSH: Namespace = Namespace(b"https://bitfire.at/webdav-push"); +pub const NS_CALDAV: Namespace = Namespace(b"urn:ietf:params:xml:ns:caldav"); +pub const NS_CARDDAV: Namespace = Namespace(b"urn:ietf:params:xml:ns:carddav"); +pub const NS_ICAL: Namespace = Namespace(b"http://apple.com/ns/ical/"); +pub const NS_CALENDARSERVER: Namespace = Namespace(b"http://calendarserver.org/ns/"); +pub const NS_NEXTCLOUD: Namespace = Namespace(b"http://nextcloud.com/ns"); + +#[derive(EnumVariants)] +pub enum CalendarProp { + // WebDAV (RFC 2518) + #[xml(ns = "NS_DAV")] + Displayname(Option), + #[xml(ns = "NS_DAV")] + Getcontenttype(&'static str), + + #[xml(ns = "NS_DAV", rename = b"principal-URL")] + PrincipalUrl, + Topic, +} + +#[test] +fn test_enum_variants() { + assert_eq!( + CalendarProp::TAGGED_VARIANTS, + &[ + (Some(NS_DAV), "displayname"), + (Some(NS_DAV), "getcontenttype"), + (Some(NS_DAV), "principal-URL"), + (None, "topic"), + ] + ); +}