rustical_dav/xml/
multistatus.rs1use crate::xml::TagList;
2use headers::{CacheControl, ContentType, HeaderMapExt};
3use http::StatusCode;
4use quick_xml::name::Namespace;
5use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
6use std::collections::HashMap;
7
8#[derive(XmlSerialize)]
9pub struct PropTagWrapper<T: XmlSerialize>(#[xml(flatten, ty = "untagged")] pub Vec<T>);
10
11#[derive(XmlSerialize, Debug)]
14pub struct PropstatElement<PropType: XmlSerialize> {
15 #[xml(ns = "crate::namespace::NS_DAV")]
16 pub prop: PropType,
17 #[xml(serialize_with = "xml_serialize_status")]
18 #[xml(ns = "crate::namespace::NS_DAV")]
19 pub status: StatusCode,
20}
21
22#[allow(clippy::trivially_copy_pass_by_ref)]
23fn xml_serialize_status(
24 status: &StatusCode,
25 ns: Option<Namespace>,
26 tag: Option<&str>,
27 namespaces: &HashMap<Namespace, &str>,
28 writer: &mut quick_xml::Writer<&mut Vec<u8>>,
29) -> std::io::Result<()> {
30 XmlSerialize::serialize(&format!("HTTP/1.1 {status}"), ns, tag, namespaces, writer)
31}
32
33#[derive(XmlSerialize)]
34#[xml(untagged)]
35pub enum PropstatWrapper<T: XmlSerialize> {
36 Normal(PropstatElement<PropTagWrapper<T>>),
37 TagList(PropstatElement<TagList>),
38}
39
40#[derive(XmlSerialize, XmlRootTag)]
44#[xml(ns = "crate::namespace::NS_DAV", root = "response")]
45#[xml(ns_prefix(
46 crate::namespace::NS_DAV = "",
47 crate::namespace::NS_CARDDAV = "CARD",
48 crate::namespace::NS_CALDAV = "CAL",
49 crate::namespace::NS_CALENDARSERVER = "CS",
50 crate::namespace::NS_DAVPUSH = "PUSH"
51))]
52pub struct ResponseElement<PropstatType: XmlSerialize> {
53 pub href: String,
54 #[xml(serialize_with = "xml_serialize_optional_status")]
55 pub status: Option<StatusCode>,
56 #[xml(flatten)]
57 pub propstat: Vec<PropstatWrapper<PropstatType>>,
58}
59
60#[allow(clippy::trivially_copy_pass_by_ref, clippy::ref_option)]
61fn xml_serialize_optional_status(
62 val: &Option<StatusCode>,
63 ns: Option<Namespace>,
64 tag: Option<&str>,
65 namespaces: &HashMap<Namespace, &str>,
66 writer: &mut quick_xml::Writer<&mut Vec<u8>>,
67) -> std::io::Result<()> {
68 XmlSerialize::serialize(
69 &val.map(|status| format!("HTTP/1.1 {status}")),
70 ns,
71 tag,
72 namespaces,
73 writer,
74 )
75}
76
77impl<PT: XmlSerialize> Default for ResponseElement<PT> {
78 fn default() -> Self {
79 Self {
80 href: String::new(),
81 status: None,
82 propstat: vec![],
83 }
84 }
85}
86
87#[derive(XmlSerialize, XmlRootTag)]
91#[xml(root = "multistatus", ns = "crate::namespace::NS_DAV")]
92#[xml(ns_prefix(
93 crate::namespace::NS_DAV = "",
94 crate::namespace::NS_CARDDAV = "CARD",
95 crate::namespace::NS_CALDAV = "CAL",
96 crate::namespace::NS_CALENDARSERVER = "CS",
97 crate::namespace::NS_DAVPUSH = "PUSH"
98))]
99pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSerialize> {
100 #[xml(rename = "response", flatten)]
101 pub responses: Vec<ResponseElement<PropType>>,
102 #[xml(rename = "response", flatten)]
103 pub member_responses: Vec<ResponseElement<MemberPropType>>,
104 pub sync_token: Option<String>,
105}
106
107impl<T1: XmlSerialize, T2: XmlSerialize> Default for MultistatusElement<T1, T2> {
108 fn default() -> Self {
109 Self {
110 responses: vec![],
111 member_responses: vec![],
112 sync_token: None,
113 }
114 }
115}
116
117impl<T1: XmlSerialize, T2: XmlSerialize> axum::response::IntoResponse
118 for MultistatusElement<T1, T2>
119{
120 fn into_response(self) -> axum::response::Response {
121 use axum::body::Body;
122
123 let output = match self.serialize_to_string() {
124 Ok(out) => out,
125 Err(err) => return crate::Error::from(err).into_response(),
126 };
127
128 let mut resp = axum::response::Response::builder().status(StatusCode::MULTI_STATUS);
129 let hdrs = resp.headers_mut().unwrap();
130 hdrs.typed_insert(ContentType::xml());
131 hdrs.typed_insert(CacheControl::new().with_no_cache());
132 resp.body(Body::from(output)).unwrap()
133 }
134}