diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index 468097c..544530f 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -13,7 +13,7 @@ use async_trait::async_trait; use derive_more::derive::{From, Into}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{Resource, ResourceService}; -use rustical_dav::xml::HrefElement; +use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_store::auth::User; use rustical_store::{Calendar, CalendarStore}; use rustical_xml::{XmlDeserialize, XmlSerialize}; @@ -93,11 +93,33 @@ impl Resource for CalendarResource { type Error = Error; type PrincipalResource = PrincipalResource; - fn get_resourcetype(&self) -> &'static [&'static str] { + fn get_resourcetype(&self) -> Resourcetype { if self.0.subscription_url.is_none() { - &["collection", "C:calendar"] + Resourcetype { + inner: &[ + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "collection", + }, + ResourcetypeInner { + ns: rustical_dav::namespace::NS_CALDAV, + name: "calendar", + }, + ], + } } else { - &["collection", "CS:subscribed"] + Resourcetype { + inner: &[ + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "collection", + }, + ResourcetypeInner { + ns: rustical_dav::namespace::NS_CALENDARSERVER, + name: "subscribed", + }, + ], + } } } diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index e70b2be..5ef685e 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -6,6 +6,7 @@ use derive_more::derive::{From, Into}; use rustical_dav::{ privileges::UserPrivilegeSet, resource::{Resource, ResourceService}, + xml::Resourcetype, }; use rustical_store::{auth::User, CalendarObject, CalendarStore}; use rustical_xml::{XmlDeserialize, XmlSerialize}; @@ -53,8 +54,8 @@ impl Resource for CalendarObjectResource { type Error = Error; type PrincipalResource = PrincipalResource; - fn get_resourcetype(&self) -> &'static [&'static str] { - &[] + fn get_resourcetype(&self) -> Resourcetype { + Resourcetype { inner: &[] } } fn get_prop( diff --git a/crates/caldav/src/principal/mod.rs b/crates/caldav/src/principal/mod.rs index 03e8088..dfb217b 100644 --- a/crates/caldav/src/principal/mod.rs +++ b/crates/caldav/src/principal/mod.rs @@ -4,7 +4,7 @@ use actix_web::dev::ResourceMap; use async_trait::async_trait; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{Resource, ResourceService}; -use rustical_dav::xml::HrefElement; +use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_store::auth::User; use rustical_store::CalendarStore; use rustical_xml::{XmlDeserialize, XmlSerialize}; @@ -57,8 +57,19 @@ impl Resource for PrincipalResource { type Error = Error; type PrincipalResource = PrincipalResource; - fn get_resourcetype(&self) -> &'static [&'static str] { - &["collection", "principal"] + fn get_resourcetype(&self) -> Resourcetype { + Resourcetype { + inner: &[ + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "collection", + }, + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "principal", + }, + ], + } } fn get_prop( diff --git a/crates/carddav/src/address_object/resource.rs b/crates/carddav/src/address_object/resource.rs index 40f8191..29b03d5 100644 --- a/crates/carddav/src/address_object/resource.rs +++ b/crates/carddav/src/address_object/resource.rs @@ -5,6 +5,7 @@ use derive_more::derive::{From, Into}; use rustical_dav::{ privileges::UserPrivilegeSet, resource::{Resource, ResourceService}, + xml::Resourcetype, }; use rustical_store::{auth::User, AddressObject, AddressbookStore}; use rustical_xml::{XmlDeserialize, XmlSerialize}; @@ -54,8 +55,8 @@ impl Resource for AddressObjectResource { type Error = Error; type PrincipalResource = PrincipalResource; - fn get_resourcetype(&self) -> &'static [&'static str] { - &[] + fn get_resourcetype(&self) -> Resourcetype { + Resourcetype { inner: &[] } } fn get_prop( diff --git a/crates/carddav/src/addressbook/resource.rs b/crates/carddav/src/addressbook/resource.rs index fb67aed..5e9df00 100644 --- a/crates/carddav/src/addressbook/resource.rs +++ b/crates/carddav/src/addressbook/resource.rs @@ -11,6 +11,7 @@ use async_trait::async_trait; use derive_more::derive::{From, Into}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{Resource, ResourceService}; +use rustical_dav::xml::{Resourcetype, ResourcetypeInner}; use rustical_store::auth::User; use rustical_store::{Addressbook, AddressbookStore}; use rustical_xml::{XmlDeserialize, XmlSerialize}; @@ -68,9 +69,19 @@ impl Resource for AddressbookResource { type Error = Error; type PrincipalResource = PrincipalResource; - fn get_resourcetype(&self) -> &'static [&'static str] { - // TODO: namespace - &["collection", "CARD:addressbook"] + fn get_resourcetype(&self) -> Resourcetype { + Resourcetype { + inner: &[ + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "collection", + }, + ResourcetypeInner { + ns: rustical_dav::namespace::NS_CARDDAV, + name: "addressbook", + }, + ], + } } fn get_prop( diff --git a/crates/carddav/src/principal/mod.rs b/crates/carddav/src/principal/mod.rs index 1275188..4f7904b 100644 --- a/crates/carddav/src/principal/mod.rs +++ b/crates/carddav/src/principal/mod.rs @@ -4,7 +4,7 @@ use actix_web::dev::ResourceMap; use async_trait::async_trait; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{Resource, ResourceService}; -use rustical_dav::xml::HrefElement; +use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_store::auth::User; use rustical_store::AddressbookStore; use rustical_xml::{XmlDeserialize, XmlSerialize}; @@ -58,8 +58,19 @@ impl Resource for PrincipalResource { type Error = Error; type PrincipalResource = PrincipalResource; - fn get_resourcetype(&self) -> &'static [&'static str] { - &["collection", "principal"] + fn get_resourcetype(&self) -> Resourcetype { + Resourcetype { + inner: &[ + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "collection", + }, + ResourcetypeInner { + ns: rustical_dav::namespace::NS_DAV, + name: "principal", + }, + ], + } } fn get_prop( diff --git a/crates/dav/src/resource/mod.rs b/crates/dav/src/resource/mod.rs index 0d9acb2..75e7886 100644 --- a/crates/dav/src/resource/mod.rs +++ b/crates/dav/src/resource/mod.rs @@ -64,7 +64,7 @@ pub trait Resource: Clone + 'static { type Error: ResponseError + From; type PrincipalResource: Resource; - fn get_resourcetype(&self) -> &'static [&'static str]; + fn get_resourcetype(&self) -> Resourcetype; fn list_props() -> Vec<&'static str> { [Self::PropName::VARIANTS, CommonPropertiesPropName::VARIANTS].concat() @@ -78,7 +78,7 @@ pub trait Resource: Clone + 'static { ) -> Result { Ok(match prop { CommonPropertiesPropName::Resourcetype => { - CommonPropertiesProp::Resourcetype(Resourcetype(self.get_resourcetype())) + CommonPropertiesProp::Resourcetype(self.get_resourcetype()) } CommonPropertiesPropName::CurrentUserPrincipal => { CommonPropertiesProp::CurrentUserPrincipal( diff --git a/crates/dav/src/resources/root.rs b/crates/dav/src/resources/root.rs index b6a3147..ea3a69f 100644 --- a/crates/dav/src/resources/root.rs +++ b/crates/dav/src/resources/root.rs @@ -1,5 +1,6 @@ use crate::privileges::UserPrivilegeSet; use crate::resource::{Resource, ResourceService}; +use crate::xml::{Resourcetype, ResourcetypeInner}; use actix_web::dev::ResourceMap; use async_trait::async_trait; use rustical_store::auth::User; @@ -37,8 +38,13 @@ impl Resource for RootResource { type Error = PR::Error; type PrincipalResource = PR; - fn get_resourcetype(&self) -> &'static [&'static str] { - &["collection"] + fn get_resourcetype(&self) -> Resourcetype { + Resourcetype { + inner: &[ResourcetypeInner { + ns: crate::namespace::NS_DAV, + name: "collection", + }], + } } fn get_prop( diff --git a/crates/dav/src/xml/mod.rs b/crates/dav/src/xml/mod.rs index b8f61c7..374542b 100644 --- a/crates/dav/src/xml/mod.rs +++ b/crates/dav/src/xml/mod.rs @@ -5,7 +5,7 @@ pub mod tag_list; use derive_more::derive::From; pub use multistatus::MultistatusElement; pub use propfind::{PropElement, PropfindElement, PropfindType, Propname}; -pub use resourcetype::Resourcetype; +pub use resourcetype::{Resourcetype, ResourcetypeInner}; use rustical_xml::{XmlDeserialize, XmlSerialize}; pub use tag_list::TagList; diff --git a/crates/dav/src/xml/resourcetype.rs b/crates/dav/src/xml/resourcetype.rs index 79c7e52..08e16bd 100644 --- a/crates/dav/src/xml/resourcetype.rs +++ b/crates/dav/src/xml/resourcetype.rs @@ -1,47 +1,25 @@ -use std::collections::HashMap; - -use quick_xml::events::attributes::Attribute; -use quick_xml::events::{BytesEnd, BytesStart, Event}; -use quick_xml::name::Namespace; use rustical_xml::XmlSerialize; -#[derive(Debug, Clone, PartialEq)] -pub struct Resourcetype(pub &'static [&'static str]); - -impl XmlSerialize for Resourcetype { - fn serialize( - &self, - ns: Option, - tag: Option<&[u8]>, - namespaces: &HashMap, - 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::Start(BytesStart::new(tag.clone())))?; - } - - for &ty in self.0 { - writer.write_event(Event::Empty(BytesStart::new(ty)))?; - } - - if let Some(tag) = &tag_str { - writer.write_event(Event::End(BytesEnd::new(tag.clone())))?; - } - Ok(()) - } +#[derive(Debug, Clone, PartialEq, XmlSerialize)] +pub struct Resourcetype { + #[xml(flatten, ty = "untagged")] + pub inner: &'static [ResourcetypeInner], +} - #[allow(refining_impl_trait)] - fn attributes<'a>(&self) -> Option>> { - None - } +#[derive(Debug, Clone, PartialEq, XmlSerialize)] +pub struct ResourcetypeInner { + #[xml(ty = "namespace")] + pub ns: quick_xml::name::Namespace<'static>, + #[xml(ty = "tag_name")] + pub name: &'static str, } #[cfg(test)] mod tests { - use super::Resourcetype; use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; + use super::{Resourcetype, ResourcetypeInner}; + #[derive(XmlSerialize, XmlRootTag)] #[xml(root = b"document")] struct Document { @@ -53,14 +31,25 @@ mod tests { let mut buf = Vec::new(); let mut writer = quick_xml::Writer::new(&mut buf); Document { - resourcetype: Resourcetype(&["collection", "hello"]), + resourcetype: Resourcetype { + inner: &[ + ResourcetypeInner { + ns: crate::namespace::NS_DAV, + name: "displayname", + }, + ResourcetypeInner { + ns: crate::namespace::NS_CALENDARSERVER, + name: "calendar-color", + }, + ], + }, } .serialize_root(&mut writer) .unwrap(); let out = String::from_utf8(buf).unwrap(); assert_eq!( out, - "" + "" ) } } diff --git a/crates/xml/derive/src/xml_struct.rs b/crates/xml/derive/src/xml_struct.rs index dda345b..cf5040a 100644 --- a/crates/xml/derive/src/xml_struct.rs +++ b/crates/xml/derive/src/xml_struct.rs @@ -1,5 +1,5 @@ use crate::attrs::{FieldType, StructAttrs}; -use crate::{field, Field}; +use crate::Field; use core::panic; use darling::FromDeriveInput; use quote::quote; @@ -197,7 +197,7 @@ impl NamedStruct { 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 tag_writers = self.fields.iter().map(Field::tag_writer); + let tag_writers: Vec<_> = self.fields.iter().filter_map(Field::tag_writer).collect(); let untagged_attributes = self .fields @@ -240,7 +240,18 @@ impl NamedStruct { } }); - let is_empty = tag_writers.len() == 0; + let namespace_field = self + .fields + .iter() + .find(|field| field.attrs.xml_ty == FieldType::Namespace) + .map(|field| { + let field_ident = field.field_ident(); + quote! { + let ns = Some(self.#field_ident); + } + }); + + let is_empty = tag_writers.is_empty(); // If we are the root element write the xmlns attributes let prefix_attributes = if self.attrs.root.is_some() { @@ -276,6 +287,7 @@ impl NamedStruct { use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; #tag_name_field; + #namespace_field; let prefix = ns .map(|ns| namespaces.get(&ns))