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