xsd_parser/xml/
attributes.rs

1use std::borrow::{Borrow, Cow};
2use std::fmt::{Debug, Formatter, Result as FmtResult};
3use std::ops::{Deref, DerefMut};
4
5use encoding_rs::{Encoding, UTF_8};
6use indexmap::map::Entry;
7use quick_xml::{
8    encoding::decode,
9    escape::{resolve_predefined_entity, unescape_with},
10    events::attributes::Attribute,
11};
12
13use indexmap::IndexMap;
14use quick_xml::name::QName;
15
16use crate::models::{format_utf8_slice, RawByteStr};
17use crate::quick_xml::{Error, ErrorKind};
18
19/// Represents a list of unstructured XML attributes.
20#[derive(Default, Debug, Clone, Eq, PartialEq)]
21pub struct Attributes<'a>(pub IndexMap<Key<'a>, Value<'a>>);
22
23/// Helper type for attributes with static lifetime
24pub type AnyAttributes = Attributes<'static>;
25
26impl<'a> Attributes<'a> {
27    /// Push a attribute to the list of attributes.
28    ///
29    /// This will push a new owned (static lifetime) attribute to the list of
30    /// attributes. If the attribute already exists, an error is raised.
31    ///
32    /// # Errors
33    ///
34    /// Raises a [`DuplicateAttribute`](ErrorKind::DuplicateAttribute) error if
35    /// the passed attribute is already part of the list.
36    pub fn push(&mut self, attrib: Attribute<'_>) -> Result<(), Error> {
37        let key = Key(Cow::Owned(attrib.key.0.to_owned()));
38
39        match self.0.entry(key) {
40            Entry::Vacant(e) => {
41                e.insert(Value(Cow::Owned(attrib.value.into_owned())));
42
43                Ok(())
44            }
45            Entry::Occupied(e) => {
46                Err(ErrorKind::DuplicateAttribute(RawByteStr::from_slice(e.key())).into())
47            }
48        }
49    }
50
51    /// Insert a new attribute into the list.
52    ///
53    /// Returns the value that already exists for the specified `key`.
54    pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<Value<'a>>
55    where
56        K: Into<Cow<'a, [u8]>>,
57        V: Into<Cow<'a, [u8]>>,
58    {
59        let key = Key(key.into());
60        let value = Value(value.into());
61
62        self.0.insert(key, value)
63    }
64
65    /// Create an iterator that yields [`Attribute`] items.
66    pub fn attributes(&self) -> impl Iterator<Item = Attribute<'_>> {
67        self.iter().map(|(k, v)| Attribute {
68            key: QName(k.as_ref()),
69            value: Cow::Borrowed(v.as_ref()),
70        })
71    }
72}
73
74impl<'a> Deref for Attributes<'a> {
75    type Target = IndexMap<Key<'a>, Value<'a>>;
76
77    fn deref(&self) -> &Self::Target {
78        &self.0
79    }
80}
81
82impl DerefMut for Attributes<'_> {
83    fn deref_mut(&mut self) -> &mut Self::Target {
84        &mut self.0
85    }
86}
87
88impl<'a> Extend<Attribute<'a>> for Attributes<'a> {
89    fn extend<T: IntoIterator<Item = Attribute<'a>>>(&mut self, iter: T) {
90        self.0.extend(iter.into_iter().map(|a| {
91            let key = Key(Cow::Borrowed(a.key.0));
92            let value = Value(a.value.clone());
93
94            (key, value)
95        }));
96    }
97}
98
99impl<'a> Extend<(Key<'a>, Value<'a>)> for Attributes<'a> {
100    fn extend<T: IntoIterator<Item = (Key<'a>, Value<'a>)>>(&mut self, iter: T) {
101        self.0.extend(iter);
102    }
103}
104
105impl<'a> FromIterator<(Key<'a>, Value<'a>)> for Attributes<'a> {
106    fn from_iter<T: IntoIterator<Item = (Key<'a>, Value<'a>)>>(iter: T) -> Self {
107        Self(FromIterator::from_iter(iter))
108    }
109}
110
111/// Wrapper type that is used as key for the [`Attributes`] map.
112#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
113pub struct Key<'a>(pub Cow<'a, [u8]>);
114
115impl Key<'_> {
116    /// Return the key as [`QName`].
117    #[must_use]
118    pub fn qname(&self) -> QName<'_> {
119        QName(&self.0)
120    }
121}
122
123impl Deref for Key<'_> {
124    type Target = [u8];
125
126    fn deref(&self) -> &Self::Target {
127        &self.0
128    }
129}
130
131impl Debug for Key<'_> {
132    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
133        write!(f, "Key(\"")?;
134        format_utf8_slice(&self.0, f)?;
135        write!(f, "\")")?;
136
137        Ok(())
138    }
139}
140
141impl AsRef<[u8]> for Key<'_> {
142    fn as_ref(&self) -> &[u8] {
143        &self.0
144    }
145}
146
147impl Borrow<[u8]> for Key<'_> {
148    fn borrow(&self) -> &[u8] {
149        &self.0
150    }
151}
152
153/// Wrapper type that is used as value for the [`Attributes`] map.
154#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
155pub struct Value<'a>(pub Cow<'a, [u8]>);
156
157impl Value<'_> {
158    /// Return the unescaped value using UTF-8 encoding.
159    ///
160    /// # Errors
161    ///
162    /// Throws an error if the value could not be unescaped.
163    #[inline]
164    pub fn unescape(&self) -> Result<Cow<'_, str>, Error> {
165        self.unescape_with(resolve_predefined_entity)
166    }
167
168    /// Return the unescaped value using UTF-8 encoding and the passed function
169    /// to resolve the escaped string.
170    ///
171    /// For details see [`unescape_with`].
172    ///
173    /// # Errors
174    ///
175    /// Throws an error if the value could not be unescaped.
176    #[inline]
177    pub fn unescape_with<'entity>(
178        &self,
179        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
180    ) -> Result<Cow<'_, str>, Error> {
181        self.decode_and_unescape_with(UTF_8, resolve_entity)
182    }
183
184    /// Decodes and unescape the value using the passed `encoding`.
185    ///
186    /// # Errors
187    ///
188    /// Throws an error if the value could not be unescaped.
189    #[inline]
190    pub fn decode_and_unescape(&self, encoding: &'static Encoding) -> Result<Cow<'_, str>, Error> {
191        self.decode_and_unescape_with(encoding, resolve_predefined_entity)
192    }
193
194    /// Return the unescaped value using the passed `encoding` and the passed
195    /// function to resolve the escaped string.
196    ///
197    /// For details see [`unescape_with`].
198    ///
199    /// # Errors
200    ///
201    /// Throws an error if the value could not be unescaped.
202    #[inline]
203    pub fn decode_and_unescape_with<'entity>(
204        &self,
205        encoding: &'static Encoding,
206        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
207    ) -> Result<Cow<'_, str>, Error> {
208        let decoded = decode(&self.0, encoding)?;
209
210        match unescape_with(&decoded, resolve_entity)? {
211            Cow::Borrowed(_) => Ok(decoded),
212            Cow::Owned(s) => Ok(s.into()),
213        }
214    }
215}
216
217impl Debug for Value<'_> {
218    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
219        write!(f, "Value(\"")?;
220        format_utf8_slice(&self.0, f)?;
221        write!(f, "\")")?;
222
223        Ok(())
224    }
225}
226
227impl AsRef<[u8]> for Value<'_> {
228    fn as_ref(&self) -> &[u8] {
229        &self.0
230    }
231}
232
233impl Borrow<[u8]> for Value<'_> {
234    fn borrow(&self) -> &[u8] {
235        &self.0
236    }
237}