rustical_dav/xml/
multistatus.rs1use std::collections::HashMap;
2
3use crate::xml::TagList;
4use actix_web::{
5 body::BoxBody,
6 http::{header::ContentType, StatusCode},
7 HttpRequest, HttpResponse, Responder, ResponseError,
8};
9use quick_xml::name::Namespace;
10use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
11
12#[derive(XmlSerialize)]
13pub struct PropTagWrapper<T: XmlSerialize>(#[xml(flatten, ty = "untagged")] pub Vec<T>);
14
15#[derive(XmlSerialize, Debug)]
18pub struct PropstatElement<PropType: XmlSerialize> {
19 #[xml(ns = "crate::namespace::NS_DAV")]
20 pub prop: PropType,
21 #[xml(serialize_with = "xml_serialize_status")]
22 #[xml(ns = "crate::namespace::NS_DAV")]
23 pub status: StatusCode,
24}
25
26fn xml_serialize_status<W: ::std::io::Write>(
27 status: &StatusCode,
28 ns: Option<Namespace>,
29 tag: Option<&[u8]>,
30 namespaces: &HashMap<Namespace, &[u8]>,
31 writer: &mut quick_xml::Writer<W>,
32) -> std::io::Result<()> {
33 XmlSerialize::serialize(&format!("HTTP/1.1 {}", status), ns, tag, namespaces, writer)
34}
35
36#[derive(XmlSerialize)]
37#[xml(untagged)]
38pub enum PropstatWrapper<T: XmlSerialize> {
39 Normal(PropstatElement<PropTagWrapper<T>>),
40 TagList(PropstatElement<TagList>),
41}
42
43#[derive(XmlSerialize)]
47#[xml(ns = "crate::namespace::NS_DAV")]
48pub struct ResponseElement<PropstatType: XmlSerialize> {
49 pub href: String,
50 #[xml(serialize_with = "xml_serialize_optional_status")]
51 pub status: Option<StatusCode>,
52 #[xml(flatten)]
53 pub propstat: Vec<PropstatWrapper<PropstatType>>,
54}
55
56fn xml_serialize_optional_status<W: ::std::io::Write>(
57 val: &Option<StatusCode>,
58 ns: Option<Namespace>,
59 tag: Option<&[u8]>,
60 namespaces: &HashMap<Namespace, &[u8]>,
61 writer: &mut quick_xml::Writer<W>,
62) -> std::io::Result<()> {
63 XmlSerialize::serialize(
64 &val.map(|status| format!("HTTP/1.1 {}", status)),
65 ns,
66 tag,
67 namespaces,
68 writer,
69 )
70}
71
72impl<PT: XmlSerialize> Default for ResponseElement<PT> {
73 fn default() -> Self {
74 Self {
75 href: String::new(),
76 status: None,
77 propstat: vec![],
78 }
79 }
80}
81
82#[derive(XmlSerialize, XmlRootTag)]
86#[xml(root = b"multistatus", ns = "crate::namespace::NS_DAV")]
87#[xml(ns_prefix(
88 crate::namespace::NS_DAV = b"",
89 crate::namespace::NS_CARDDAV = b"CARD",
90 crate::namespace::NS_CALDAV = b"CAL",
91 crate::namespace::NS_CALENDARSERVER = b"CS",
92 crate::namespace::NS_DAVPUSH = b"PUSH"
93))]
94pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSerialize> {
95 #[xml(rename = b"response", flatten)]
96 pub responses: Vec<ResponseElement<PropType>>,
97 #[xml(rename = b"response", flatten)]
98 pub member_responses: Vec<ResponseElement<MemberPropType>>,
99 pub sync_token: Option<String>,
100}
101
102impl<T1: XmlSerialize, T2: XmlSerialize> Default for MultistatusElement<T1, T2> {
103 fn default() -> Self {
104 Self {
105 responses: vec![],
106 member_responses: vec![],
107 sync_token: None,
108 }
109 }
110}
111
112impl<T1: XmlSerialize, T2: XmlSerialize> Responder for MultistatusElement<T1, T2> {
113 type Body = BoxBody;
114
115 fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
116 let mut output: Vec<_> = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".into();
117 let mut writer = quick_xml::Writer::new_with_indent(&mut output, b' ', 4);
118 if let Err(err) = self.serialize_root(&mut writer) {
119 return crate::Error::from(err).error_response();
120 }
121
122 HttpResponse::MultiStatus()
123 .content_type(ContentType::xml())
124 .body(String::from_utf8(output).unwrap())
125 }
126}