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; #[derive(XmlSerialize)] 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, } fn xml_serialize_status( status: &StatusCode, ns: Option, tag: Option<&[u8]>, namespaces: &HashMap, writer: &mut quick_xml::Writer, ) -> std::io::Result<()> { XmlSerialize::serialize(&format!("HTTP/1.1 {}", status), ns, tag, namespaces, writer) } #[derive(XmlSerialize)] #[xml(untagged)] pub enum PropstatWrapper { Normal(PropstatElement>), TagList(PropstatElement), } // RFC 2518 // #[derive(XmlSerialize)] #[xml(ns = "crate::namespace::NS_DAV")] pub struct ResponseElement { pub href: String, #[xml(serialize_with = "xml_serialize_optional_status")] pub status: Option, #[xml(flatten)] pub propstat: Vec>, } fn xml_serialize_optional_status( val: &Option, ns: Option, tag: Option<&[u8]>, namespaces: &HashMap, writer: &mut quick_xml::Writer, ) -> 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 = b"multistatus", ns = "crate::namespace::NS_DAV")] #[xml(ns_prefix( crate::namespace::NS_DAV = b"", crate::namespace::NS_CARDDAV = b"CARD", crate::namespace::NS_CALDAV = b"CAL", crate::namespace::NS_CALENDARSERVER = b"CS", crate::namespace::NS_DAVPUSH = b"PUSH" ))] pub struct MultistatusElement { #[xml(rename = b"response", flatten)] pub responses: Vec>, #[xml(rename = b"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 mut output: Vec<_> = b"\n".into(); let mut writer = quick_xml::Writer::new_with_indent(&mut output, b' ', 4); if let Err(err) = self.serialize_root(&mut writer) { 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() } }