use crate::xml::TagList; use headers::{CacheControl, ContentType, HeaderMapExt}; use http::StatusCode; use quick_xml::name::Namespace; use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; use std::{collections::HashMap, fmt::Debug}; #[derive(XmlSerialize, Debug)] pub struct PropTagWrapper(#[xml(flatten, ty = "untagged")] pub Vec); // RFC 2518 // #[derive(XmlSerialize, Debug)] pub struct PropstatElement { #[xml(ns = "crate::namespace::NS_DAV")] pub prop: PropType, #[xml(serialize_with = "xml_serialize_status")] #[xml(ns = "crate::namespace::NS_DAV")] pub status: StatusCode, } #[allow(clippy::trivially_copy_pass_by_ref)] fn xml_serialize_status( status: &StatusCode, ns: Option, tag: Option<&str>, namespaces: &HashMap, writer: &mut quick_xml::Writer<&mut Vec>, ) -> std::io::Result<()> { XmlSerialize::serialize(&format!("HTTP/1.1 {status}"), ns, tag, namespaces, writer) } #[derive(XmlSerialize, Debug)] #[xml(untagged)] pub enum PropstatWrapper { Normal(PropstatElement>), TagList(PropstatElement), } // RFC 2518 // #[derive(XmlSerialize, XmlRootTag, Debug)] #[xml(ns = "crate::namespace::NS_DAV", root = "response")] #[xml(ns_prefix( crate::namespace::NS_DAV = "", crate::namespace::NS_CARDDAV = "CARD", crate::namespace::NS_CALDAV = "CAL", crate::namespace::NS_CALENDARSERVER = "CS", crate::namespace::NS_DAVPUSH = "PUSH" ))] pub struct ResponseElement { pub href: String, #[xml(serialize_with = "xml_serialize_optional_status")] pub status: Option, #[xml(flatten)] pub propstat: Vec>, } #[allow(clippy::trivially_copy_pass_by_ref, clippy::ref_option)] fn xml_serialize_optional_status( val: &Option, ns: Option, tag: Option<&str>, namespaces: &HashMap, writer: &mut quick_xml::Writer<&mut Vec>, ) -> std::io::Result<()> { XmlSerialize::serialize( &val.map(|status| format!("HTTP/1.1 {status}")), ns, tag, namespaces, writer, ) } impl Default for ResponseElement { fn default() -> Self { Self { href: String::new(), status: None, propstat: vec![], } } } // RFC 2518 // // Extended by sync-token as specified in RFC 6578 #[derive(XmlSerialize, XmlRootTag)] #[xml(root = "multistatus", ns = "crate::namespace::NS_DAV")] #[xml(ns_prefix( crate::namespace::NS_DAV = "", crate::namespace::NS_CARDDAV = "CARD", crate::namespace::NS_CALDAV = "CAL", crate::namespace::NS_CALENDARSERVER = "CS", crate::namespace::NS_DAVPUSH = "PUSH" ))] pub struct MultistatusElement { #[xml(rename = "response", flatten)] pub responses: Vec>, #[xml(rename = "response", flatten)] pub member_responses: Vec>, pub sync_token: Option, } impl Default for MultistatusElement { fn default() -> Self { Self { responses: vec![], member_responses: vec![], sync_token: None, } } } impl axum::response::IntoResponse for MultistatusElement { fn into_response(self) -> axum::response::Response { use axum::body::Body; let output = match self.serialize_to_string() { Ok(out) => out, Err(err) => return crate::Error::from(err).into_response(), }; let mut resp = axum::response::Response::builder().status(StatusCode::MULTI_STATUS); let hdrs = resp.headers_mut().unwrap(); hdrs.typed_insert(ContentType::xml()); hdrs.typed_insert(CacheControl::new().with_no_cache()); resp.body(Body::from(output)).unwrap() } }