webdav_xml/value.rs
1// SPDX-FileCopyrightText: d-k-bo <d-k-bo@mailbox.org>
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use bytestring::ByteString;
6use indexmap::IndexMap;
7use nonempty::{nonempty, NonEmpty};
8
9use crate::{
10 element::{Element, ElementExt, ElementName},
11 Error,
12};
13
14/// Represents the content of an XML element.
15///
16/// This data structure is intended to be similar to
17/// [`serde_json::Value`](https://docs.rs/serde_json/latest/serde_json/enum.Value.html).
18/// Unlike when deserializing JSON, which has explicit arrays, we have to
19/// manually group multiple adjacent elements into an array-like structure.
20#[derive(Clone, Debug, Default, PartialEq)]
21pub enum Value {
22 /// The element is empty, e.g. `<foo />`
23 #[default]
24 Empty,
25 /// The element contains a text node, e.g. `<foo>bar</foo>`
26 Text(ByteString),
27 /// The element contains other elements, e.g. `<foo><bar /></foo>`
28 Map(ValueMap),
29 /// The parent element contains multiple elements of this type, e.g. `<foo
30 /// /><foo />`
31 List(Box<NonEmpty<Value>>),
32}
33
34impl Value {
35 pub fn to_str(&self) -> Result<&ByteString, Error> {
36 match self {
37 Self::Text(s) => Ok(s),
38 _ => Err(Error::InvalidValueType("expected text")),
39 }
40 }
41 pub fn to_map(&self) -> Result<&ValueMap, Error> {
42 match self {
43 Self::Map(map) => Ok(map),
44 _ => Err(Error::InvalidValueType("expected a map")),
45 }
46 }
47}
48
49impl From<String> for Value {
50 fn from(s: String) -> Self {
51 Value::Text(s.into())
52 }
53}
54
55type InnerValueMap = IndexMap<ElementName<ByteString>, Value>;
56
57/// A mapping from tag names to [`Value`]s.
58#[derive(Clone, Debug, Default, PartialEq)]
59pub struct ValueMap(pub(crate) InnerValueMap);
60
61impl ValueMap {
62 pub fn new() -> Self {
63 Self(IndexMap::new())
64 }
65 /// Extract a child element of a specific type.
66 ///
67 /// # Returns
68 ///
69 /// - `None` if the element doesn't exist
70 /// - `Some(Ok(_))` if the element exists and was successfully extracted
71 /// - `Some(Err(_))` if the element exists and extraction failed
72 pub fn get<'v, E>(&'v self) -> Option<Result<E, Error>>
73 where
74 E: Element + TryFrom<&'v Value, Error = Error>,
75 {
76 self.0
77 .get(&E::element_name::<&'static str>())
78 .map(E::try_from)
79 }
80 /// Extract a non-empty child element of a specific type.
81 ///
82 /// # Returns
83 ///
84 /// - `None` if the element doesn't exist
85 /// - `Some(None)` if the element exists and is empty
86 /// - `Some(Some(Ok(_)))` if the element exists, is not empty and was
87 /// successfully extracted
88 /// - `Some(Some(Err(_)))` if the element exists, is not empty and
89 /// extraction failed
90 pub fn get_optional<'v, E>(&'v self) -> Option<Option<Result<E, Error>>>
91 where
92 E: Element + TryFrom<&'v Value, Error = Error>,
93 {
94 self.0
95 .get(&E::element_name::<&'static str>())
96 .map(|value| match value {
97 Value::Empty => None,
98 v => Some(v.try_into()),
99 })
100 }
101 /// Insert a child value into the map.
102 pub fn insert<E: Element>(&mut self, value: Value) {
103 let key = E::element_name();
104 self.insert_raw(key, value)
105 }
106}
107
108impl ValueMap {
109 pub(crate) fn iter_all<'v, E>(&'v self) -> impl Iterator<Item = Result<E, Error>> + 'v
110 where
111 E: Element + TryFrom<&'v Value, Error = Error> + 'v,
112 {
113 enum ElementIter<'a> {
114 List(nonempty::Iter<'a, Value>),
115 Single(std::iter::Once<&'a Value>),
116 Empty,
117 }
118
119 impl<'a> Iterator for ElementIter<'a> {
120 type Item = &'a Value;
121
122 fn next(&mut self) -> Option<Self::Item> {
123 match self {
124 Self::List(inner) => inner.next(),
125 Self::Single(value) => value.next(),
126 Self::Empty => None,
127 }
128 }
129 }
130
131 match self.0.get(&E::element_name::<&'static str>()) {
132 Some(Value::List(list)) => ElementIter::List(list.iter()),
133 Some(value) => ElementIter::Single(std::iter::once(value)),
134 None => ElementIter::Empty,
135 }
136 .map(E::try_from)
137 }
138 // pub(crate) fn iter_all_nonempty<'v, E>(&'v self) -> impl Iterator<Item =
139 // Result<E, Error>> + 'v where
140 // E: Element + TryFrom<&'v Value, Error = Error>,
141 // {
142 // self.iter_all()
143 // .map(|v| v.ok_or(Error::EmptyElement(E::LOCAL_NAME))?)
144 // }
145 // pub(crate) fn get_all_nonempty<'v, E>(&'v self) ->
146 // Result<Option<NonEmpty<E>>, Error> where
147 // E: Element + TryFrom<&'v Value, Error = Error>,
148 // {
149 // NonEmpty::try_collect(
150 // self.iter_all()
151 // .map(|v| v.ok_or(Error::EmptyElement(E::LOCAL_NAME))?),
152 // )
153 // }
154 // pub(crate) fn get_all_required<'v, E>(&'v self) -> Result<NonEmpty<E>, Error>
155 // where
156 // E: Element + TryFrom<&'v Value, Error = Error>,
157 // {
158 // NonEmpty::try_collect(
159 // self.iter_all()
160 // .map(|v| v.ok_or(Error::EmptyElement(E::LOCAL_NAME))?),
161 // )
162 // .transpose()
163 // .ok_or(Error::MissingElement(E::LOCAL_NAME))?
164 // }
165 pub(crate) fn insert_raw(&mut self, key: ElementName<ByteString>, value: Value) {
166 match self.0.get_mut(&key) {
167 Some(old_value) => {
168 *old_value = Value::List(Box::new(nonempty![std::mem::take(old_value), value]));
169 }
170 None => {
171 self.0.insert(key, value);
172 }
173 }
174 }
175}
176
177impl AsRef<InnerValueMap> for ValueMap {
178 fn as_ref(&self) -> &InnerValueMap {
179 &self.0
180 }
181}
182
183impl AsMut<InnerValueMap> for ValueMap {
184 fn as_mut(&mut self) -> &mut InnerValueMap {
185 &mut self.0
186 }
187}
188
189impl From<InnerValueMap> for ValueMap {
190 fn from(map: InnerValueMap) -> Self {
191 Self(map)
192 }
193}