webdav_xml/elements/
multistatus.rs

1// SPDX-FileCopyrightText: d-k-bo <d-k-bo@mailbox.org>
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use nonempty::NonEmpty;
6
7use crate::{
8    elements::{response::Response, ResponseDescription},
9    value::ValueMap,
10    Element, Error, Value, DAV_NAMESPACE, DAV_PREFIX,
11};
12
13/// The `multistatus` XML element as defined in [RFC 4918](http://webdav.org/specs/rfc4918.html#ELEMENT_multistatus).
14#[derive(Clone, Debug, Default, PartialEq)]
15pub struct Multistatus {
16    pub response: Vec<Response>,
17    pub responsedescription: Option<ResponseDescription>,
18}
19
20impl Element for Multistatus {
21    const NAMESPACE: &'static str = DAV_NAMESPACE;
22    const PREFIX: &'static str = DAV_PREFIX;
23    const LOCAL_NAME: &'static str = "multistatus";
24}
25
26impl TryFrom<&Value> for Multistatus {
27    type Error = Error;
28
29    fn try_from(value: &Value) -> Result<Self, Self::Error> {
30        let map = value.to_map()?;
31
32        Ok(Multistatus {
33            response: map.iter_all().collect::<Result<_, _>>()?,
34            responsedescription: map.get().transpose()?,
35        })
36    }
37}
38
39impl From<Multistatus> for Value {
40    fn from(
41        Multistatus {
42            response,
43            responsedescription,
44        }: Multistatus,
45    ) -> Value {
46        let mut map = ValueMap::new();
47
48        map.insert::<Response>(
49            match NonEmpty::collect(response.into_iter().map(Value::from)) {
50                Some(responses) => Value::List(Box::new(responses)),
51                None => Value::Empty,
52            },
53        );
54        if let Some(responsedescription) = responsedescription {
55            map.insert::<ResponseDescription>(responsedescription.into())
56        }
57
58        Value::Map(map)
59    }
60}
61
62#[cfg(test)]
63#[test]
64fn test() -> eyre::Result<()> {
65    use http::StatusCode;
66
67    use crate::{
68        elements::{Href, Status},
69        FromXml,
70    };
71
72    // http://webdav.org/specs/rfc4918.html#n-example---retrieving-named-properties
73    let xml = r#"
74    <?xml version="1.0" encoding="utf-8" ?>
75    <D:multistatus xmlns:D="DAV:">
76      <D:response xmlns:R="http://ns.example.com/boxschema/">
77        <D:href>http://www.example.com/file</D:href>
78        <D:propstat>
79          <D:prop>
80            <R:bigbox>
81              <R:BoxType>Box type A</R:BoxType>
82            </R:bigbox>
83            <R:author>
84              <R:Name>J.J. Johnson</R:Name>
85            </R:author>
86          </D:prop>
87          <D:status>HTTP/1.1 200 OK</D:status>
88        </D:propstat>
89        <D:propstat>
90          <D:prop><R:DingALing/><R:Random/></D:prop>
91          <D:status>HTTP/1.1 403 Forbidden</D:status>
92          <D:responsedescription> The user does not have access to the
93     DingALing property.
94          </D:responsedescription>
95        </D:propstat>
96      </D:response>
97      <D:responsedescription> There has been an access violation error.
98      </D:responsedescription>
99    </D:multistatus>
100    "#;
101    let multistatus = Multistatus::from_xml(xml)?;
102
103    assert!(matches!(
104      &multistatus.response[0],
105        Response::Propstat {
106            href, propstat,
107            responsedescription: None,
108            ..
109        } if href == &Href(http::Uri::from_static("http://www.example.com/file"))
110          && propstat[0].status == Status(StatusCode::OK)
111          && propstat[1].status == Status(StatusCode::FORBIDDEN)
112          && propstat[1].responsedescription.is_some()
113    ));
114
115    Ok(())
116}