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, fmt::Debug};
7
8#[derive(XmlSerialize, Debug)]
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, Debug)]
34#[xml(untagged)]
35pub enum PropstatWrapper<T: XmlSerialize> {
36 Normal(PropstatElement<PropTagWrapper<T>>),
37 TagList(PropstatElement<TagList>),
38}
39
40#[derive(XmlSerialize, XmlRootTag, Debug)]
44#[xml(ns = "crate::namespace::NS_DAV", root = "response")]
45pub struct ResponseElement<PropstatType: XmlSerialize> {
46 pub href: String,
47 #[xml(serialize_with = "xml_serialize_optional_status")]
48 pub status: Option<StatusCode>,
49 #[xml(flatten)]
50 pub propstat: Vec<PropstatWrapper<PropstatType>>,
51}
52
53#[allow(clippy::trivially_copy_pass_by_ref, clippy::ref_option)]
54fn xml_serialize_optional_status(
55 val: &Option<StatusCode>,
56 ns: Option<Namespace>,
57 tag: Option<&str>,
58 namespaces: &HashMap<Namespace, &str>,
59 writer: &mut quick_xml::Writer<&mut Vec<u8>>,
60) -> std::io::Result<()> {
61 XmlSerialize::serialize(
62 &val.map(|status| format!("HTTP/1.1 {status}")),
63 ns,
64 tag,
65 namespaces,
66 writer,
67 )
68}
69
70impl<PT: XmlSerialize> Default for ResponseElement<PT> {
71 fn default() -> Self {
72 Self {
73 href: String::new(),
74 status: None,
75 propstat: vec![],
76 }
77 }
78}
79
80#[derive(XmlSerialize, XmlRootTag)]
84#[xml(root = "multistatus", ns = "crate::namespace::NS_DAV")]
85#[xml(ns_prefix(
86 crate::namespace::NS_DAV = "",
87 crate::namespace::NS_CARDDAV = "CARD",
88 crate::namespace::NS_CALDAV = "CAL",
89 crate::namespace::NS_CALENDARSERVER = "CS",
90 crate::namespace::NS_DAVPUSH = "PUSH"
91))]
92pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSerialize> {
93 #[xml(rename = "response", flatten)]
94 pub responses: Vec<ResponseElement<PropType>>,
95 #[xml(rename = "response", flatten)]
96 pub member_responses: Vec<ResponseElement<MemberPropType>>,
97 pub sync_token: Option<String>,
98}
99
100impl<T1: XmlSerialize, T2: XmlSerialize> Default for MultistatusElement<T1, T2> {
101 fn default() -> Self {
102 Self {
103 responses: vec![],
104 member_responses: vec![],
105 sync_token: None,
106 }
107 }
108}
109
110impl<T1: XmlSerialize, T2: XmlSerialize> axum::response::IntoResponse
111 for MultistatusElement<T1, T2>
112{
113 fn into_response(self) -> axum::response::Response {
114 use axum::body::Body;
115
116 let output = match self.serialize_to_string() {
117 Ok(out) => out,
118 Err(err) => return crate::Error::from(err).into_response(),
119 };
120
121 let mut resp = axum::response::Response::builder().status(StatusCode::MULTI_STATUS);
122 let hdrs = resp.headers_mut().unwrap();
123 hdrs.typed_insert(ContentType::xml());
124 hdrs.typed_insert(CacheControl::new().with_no_cache());
125 resp.body(Body::from(output)).unwrap()
126 }
127}