rustical_dav/xml/
multistatus.rs1use crate::xml::TagList;
2use headers::{CacheControl, ContentType, HeaderMapExt};
3use http::{StatusCode, Uri};
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: Uri,
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
70#[derive(XmlSerialize, XmlRootTag)]
74#[xml(root = "multistatus", ns = "crate::namespace::NS_DAV")]
75#[xml(ns_prefix(
76 crate::namespace::NS_DAV = "",
77 crate::namespace::NS_CARDDAV = "CARD",
78 crate::namespace::NS_CALDAV = "CAL",
79 crate::namespace::NS_CALENDARSERVER = "CS",
80 crate::namespace::NS_DAVPUSH = "PUSH"
81))]
82pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSerialize> {
83 #[xml(rename = "response", flatten)]
84 pub responses: Vec<ResponseElement<PropType>>,
85 #[xml(rename = "response", flatten)]
86 pub member_responses: Vec<ResponseElement<MemberPropType>>,
87 pub sync_token: Option<String>,
88}
89
90impl<T1: XmlSerialize, T2: XmlSerialize> Default for MultistatusElement<T1, T2> {
91 fn default() -> Self {
92 Self {
93 responses: vec![],
94 member_responses: vec![],
95 sync_token: None,
96 }
97 }
98}
99
100impl<T1: XmlSerialize, T2: XmlSerialize> axum::response::IntoResponse
101 for MultistatusElement<T1, T2>
102{
103 fn into_response(self) -> axum::response::Response {
104 use axum::body::Body;
105
106 let output = match self.serialize_to_string() {
107 Ok(out) => out,
108 Err(err) => return crate::Error::from(err).into_response(),
109 };
110
111 let mut resp = axum::response::Response::builder().status(StatusCode::MULTI_STATUS);
112 let hdrs = resp.headers_mut().unwrap();
113 hdrs.typed_insert(ContentType::xml());
114 hdrs.typed_insert(CacheControl::new().with_no_cache());
115 resp.body(Body::from(output)).unwrap()
116 }
117}