use std::collections::HashMap; use crate::xml::TagList; use actix_web::{ body::BoxBody, http::{header::ContentType, StatusCode}, HttpRequest, HttpResponse, Responder, ResponseError, }; use quick_xml::name::Namespace; use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; // Intermediate struct because of a serde limitation, see following article: // https://stackoverflow.com/questions/78444158/unsupportedcannot-serialize-enum-newtype-variant-exampledata #[derive(XmlSerialize)] pub struct PropTagWrapper { #[xml(flatten, ty = "untagged")] pub prop: Vec, } // RFC 2518 // #[derive(XmlSerialize)] pub struct PropstatElement { pub prop: PropType, #[xml(serialize_with = "xml_serialize_status")] 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)] 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")] pub struct MultistatusElement { #[xml(rename = b"response", flatten)] pub responses: Vec>, #[xml(rename = b"response", flatten)] pub member_responses: Vec>, // TODO: napespaces // pub ns_dav: &'static str, // pub ns_davpush: &'static str, // pub ns_caldav: &'static str, // pub ns_ical: &'static str, // pub ns_calendarserver: &'static str, // pub ns_carddav: &'static str, pub sync_token: Option, } impl Default for MultistatusElement { fn default() -> Self { Self { responses: vec![], member_responses: vec![], // ns_dav: Namespace::Dav.as_str(), // ns_davpush: Namespace::DavPush.as_str(), // ns_caldav: Namespace::CalDAV.as_str(), // ns_ical: Namespace::ICal.as_str(), // ns_calendarserver: Namespace::CServer.as_str(), // ns_carddav: Namespace::CardDAV.as_str(), sync_token: None, } } } impl Responder for MultistatusElement { type Body = BoxBody; fn respond_to(self, _req: &HttpRequest) -> HttpResponse { 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).error_response(); } HttpResponse::MultiStatus() .content_type(ContentType::xml()) .body(String::from_utf8(output).unwrap()) } }