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