Skip to main content

xml/
namespace.rs

1//! Contains namespace manipulation types and functions.
2
3use std::borrow::Cow;
4use std::collections::btree_map::{BTreeMap, Entry, Iter as Entries};
5use std::collections::HashSet;
6use std::iter::{Map, Rev};
7use std::slice::Iter;
8
9/// Designates prefix for namespace definitions.
10///
11/// See [Namespaces in XML][namespace] spec for more information.
12///
13///   [namespace]: http://www.w3.org/TR/xml-names/#ns-decl
14pub const NS_XMLNS_PREFIX: &str = "xmlns";
15
16/// Designates the standard URI for `xmlns` prefix.
17///
18/// See [A Namespace Name for xmlns Attributes][namespace] for more information.
19///
20///   [namespace]: http://www.w3.org/2000/xmlns/
21pub const NS_XMLNS_URI: &str = "http://www.w3.org/2000/xmlns/";
22
23/// Designates prefix for a namespace containing several special predefined attributes.
24///
25/// See [2.10 White Space handling][1],  [2.1 Language Identification][2],
26/// [XML Base specification][3] and [xml:id specification][4] for more information.
27///
28///   [1]: http://www.w3.org/TR/REC-xml/#sec-white-space
29///   [2]: http://www.w3.org/TR/REC-xml/#sec-lang-tag
30///   [3]: http://www.w3.org/TR/xmlbase/
31///   [4]: http://www.w3.org/TR/xml-id/
32pub const NS_XML_PREFIX: &str = "xml";
33
34/// Designates the standard URI for `xml` prefix.
35///
36/// See `NS_XML_PREFIX` documentation for more information.
37pub const NS_XML_URI: &str = "http://www.w3.org/XML/1998/namespace";
38
39/// Designates the absence of prefix in a qualified name.
40///
41/// This constant should be used to define or query default namespace which should be used
42/// for element or attribute names without prefix. For example, if a namespace mapping
43/// at a particular point in the document contains correspondence like
44///
45/// ```none
46///   NS_NO_PREFIX  -->  urn:some:namespace
47/// ```
48///
49/// then all names declared without an explicit prefix `urn:some:namespace` is assumed as
50/// a namespace URI.
51///
52/// By default empty prefix corresponds to absence of namespace, but this can change either
53/// when writing an XML document (manually) or when reading an XML document (based on namespace
54/// declarations).
55pub const NS_NO_PREFIX: &str = "";
56
57/// Designates an empty namespace URI, which is equivalent to absence of namespace.
58///
59/// This constant should not usually be used directly; it is used to designate that
60/// empty prefix corresponds to absent namespace in `NamespaceStack` instances created with
61/// `NamespaceStack::default()`. Therefore, it can be used to restore `NS_NO_PREFIX` mapping
62/// in a namespace back to its default value.
63pub const NS_EMPTY_URI: &str = "";
64
65/// Namespace is a map from prefixes to namespace URIs.
66///
67/// No prefix (i.e. default namespace) is designated by `NS_NO_PREFIX` constant.
68#[derive(PartialEq, Eq, Clone, Debug)]
69pub struct Namespace(pub BTreeMap<String, String>);
70
71impl Namespace {
72    /// Returns an empty namespace.
73    #[inline]
74    #[must_use]
75    pub fn empty() -> Self {
76        Self(BTreeMap::new())
77    }
78
79    /// Checks whether this namespace is empty.
80    #[inline]
81    #[must_use]
82    pub fn is_empty(&self) -> bool {
83        self.0.is_empty()
84    }
85
86    /// Checks whether this namespace is essentially empty, that is, it does not contain
87    /// anything but default mappings.
88    #[must_use]
89    pub fn is_essentially_empty(&self) -> bool {
90        // a shortcut for a namespace which is definitely not empty
91        if self.0.len() > 3 { return false; }
92
93        self.0.iter().all(|(k, v)| matches!((&**k, &**v),
94            (NS_NO_PREFIX,    NS_EMPTY_URI) |
95            (NS_XMLNS_PREFIX, NS_XMLNS_URI) |
96            (NS_XML_PREFIX,   NS_XML_URI))
97        )
98    }
99
100    /// Checks whether this namespace mapping contains the given prefix.
101    ///
102    /// # Parameters
103    /// * `prefix`  --- namespace prefix.
104    ///
105    /// # Return value
106    /// `true` if this namespace contains the given prefix, `false` otherwise.
107    #[inline]
108    pub fn contains<P: ?Sized + AsRef<str>>(&self, prefix: &P) -> bool {
109        self.0.contains_key(prefix.as_ref())
110    }
111
112    /// Puts a mapping into this namespace.
113    ///
114    /// This method does not override any already existing mappings.
115    ///
116    /// Returns a boolean flag indicating whether the map already contained
117    /// the given prefix.
118    ///
119    /// # Parameters
120    /// * `prefix` --- namespace prefix;
121    /// * `uri`    --- namespace URI.
122    ///
123    /// # Return value
124    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
125    /// was already present in the namespace.
126    pub fn put<P, U>(&mut self, prefix: P, uri: U) -> bool
127        where P: Into<String>, U: Into<String>
128    {
129        match self.0.entry(prefix.into()) {
130            Entry::Occupied(_) => false,
131            Entry::Vacant(ve) => {
132                ve.insert(uri.into());
133                true
134            },
135        }
136    }
137
138    /// Puts a mapping into this namespace forcefully.
139    ///
140    /// This method, unlike `put()`, does replace an already existing mapping.
141    ///
142    /// Returns previous URI which was assigned to the given prefix, if it is present.
143    ///
144    /// # Parameters
145    /// * `prefix` --- namespace prefix;
146    /// * `uri`    --- namespace URI.
147    ///
148    /// # Return value
149    /// `Some(uri)` with `uri` being a previous URI assigned to the `prefix`, or
150    /// `None` if such prefix was not present in the namespace before.
151    pub fn force_put<P, U>(&mut self, prefix: P, uri: U) -> Option<String>
152        where P: Into<String>, U: Into<String>
153    {
154        self.0.insert(prefix.into(), uri.into())
155    }
156
157    /// Queries the namespace for the given prefix.
158    ///
159    /// # Parameters
160    /// * `prefix` --- namespace prefix.
161    ///
162    /// # Return value
163    /// Namespace URI corresponding to the given prefix, if it is present.
164    pub fn get<'a, P: ?Sized + AsRef<str>>(&'a self, prefix: &P) -> Option<&'a str> {
165        self.0.get(prefix.as_ref()).map(|s| &**s)
166    }
167
168    /// Borrowed namespace for the writer
169    #[must_use]
170    pub const fn borrow(&self) -> Cow<'_, Self> {
171        Cow::Borrowed(self)
172    }
173
174    /// Namespace mappings contained in a namespace.
175    pub fn iter(&self) -> NamespaceMappings<'_> {
176        self.into_iter()
177    }
178}
179
180/// An alias for iterator type for namespace mappings contained in a namespace.
181pub type NamespaceMappings<'a> = Map<
182    Entries<'a, String, String>,
183    for<'b> fn((&'b String, &'b String)) -> UriMapping<'b>
184>;
185
186impl<'a> IntoIterator for &'a Namespace {
187    type IntoIter = NamespaceMappings<'a>;
188    type Item = UriMapping<'a>;
189
190    fn into_iter(self) -> Self::IntoIter {
191        fn mapper<'a>((prefix, uri): (&'a String, &'a String)) -> UriMapping<'a> {
192            (prefix, uri)
193        }
194        self.0.iter().map(mapper)
195    }
196}
197
198/// Namespace stack is a sequence of namespaces.
199///
200/// Namespace stack is used to represent cumulative namespace consisting of
201/// combined namespaces from nested elements.
202#[derive(Clone, Eq, PartialEq, Debug)]
203pub struct NamespaceStack(pub Vec<Namespace>);
204
205impl NamespaceStack {
206    /// Returns an empty namespace stack.
207    #[inline]
208    #[must_use]
209    pub fn empty() -> Self {
210        Self(Vec::with_capacity(2))
211    }
212
213    /// Returns a namespace stack with default items in it.
214    ///
215    /// Default items are the following:
216    ///
217    /// * `xml` → `http://www.w3.org/XML/1998/namespace`;
218    /// * `xmlns` → `http://www.w3.org/2000/xmlns/`.
219    #[inline]
220    #[must_use]
221    #[allow(clippy::should_implement_trait)]
222    pub fn default() -> Self {
223        let mut nst = Self::empty();
224        nst.push_empty();
225        // xml namespace
226        nst.put(NS_XML_PREFIX, NS_XML_URI);
227        // xmlns namespace
228        nst.put(NS_XMLNS_PREFIX, NS_XMLNS_URI);
229        // empty namespace
230        nst.put(NS_NO_PREFIX, NS_EMPTY_URI);
231        nst
232    }
233
234    /// Adds an empty namespace to the top of this stack.
235    #[inline]
236    pub fn push_empty(&mut self) -> &mut Self {
237        self.0.push(Namespace::empty());
238        self
239    }
240
241    /// Removes the topmost namespace in this stack.
242    ///
243    /// Panics if the stack is empty.
244    #[inline]
245    #[track_caller]
246    pub fn pop(&mut self) -> Namespace {
247        self.0.pop().unwrap()
248    }
249
250    /// Removes the topmost namespace in this stack.
251    ///
252    /// Returns `Some(namespace)` if this stack is not empty and `None` otherwise.
253    #[inline]
254    pub fn try_pop(&mut self) -> Option<Namespace> {
255        self.0.pop()
256    }
257
258    /// Borrows the topmost namespace mutably, leaving the stack intact.
259    ///
260    /// Panics if the stack is empty.
261    #[inline]
262    #[track_caller]
263    pub fn peek_mut(&mut self) -> &mut Namespace {
264        self.0.last_mut().unwrap()
265    }
266
267    /// Borrows the topmost namespace immutably, leaving the stack intact.
268    ///
269    /// Panics if the stack is empty.
270    #[inline]
271    #[must_use]
272    #[track_caller]
273    pub fn peek(&self) -> &Namespace {
274        self.0.last().unwrap()
275    }
276
277    /// Puts a mapping into the topmost namespace if this stack does not already contain one.
278    ///
279    /// Returns a boolean flag indicating whether the insertion has completed successfully.
280    /// Note that both key and value are matched and the mapping is inserted if either
281    /// namespace prefix is not already mapped, or if it is mapped, but to a different URI.
282    ///
283    /// # Parameters
284    /// * `prefix` --- namespace prefix;
285    /// * `uri`    --- namespace URI.
286    ///
287    /// # Return value
288    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
289    /// was already present in the namespace stack.
290    pub fn put_checked<P, U>(&mut self, prefix: P, uri: U) -> bool
291        where P: Into<String> + AsRef<str>,
292              U: Into<String> + AsRef<str>
293    {
294        if self.0.iter().any(|ns| ns.get(&prefix) == Some(uri.as_ref())) {
295            false
296        } else {
297            self.put(prefix, uri);
298            true
299        }
300    }
301
302    /// Puts a mapping into the topmost namespace in this stack.
303    ///
304    /// This method does not override a mapping in the topmost namespace if it is
305    /// already present, however, it does not depend on other namespaces in the stack,
306    /// so it is possible to put a mapping which is present in lower namespaces.
307    ///
308    /// Returns a boolean flag indicating whether the insertion has completed successfully.
309    ///
310    /// # Parameters
311    /// * `prefix` --- namespace prefix;
312    /// * `uri`    --- namespace URI.
313    ///
314    /// # Return value
315    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
316    /// was already present in the namespace.
317    #[inline]
318    pub fn put<P, U>(&mut self, prefix: P, uri: U) -> bool
319        where P: Into<String>, U: Into<String>
320    {
321        if let Some(ns) = self.0.last_mut() {
322            ns.put(prefix, uri)
323        } else {
324            false
325        }
326    }
327
328    /// Performs a search for the given prefix in the whole stack.
329    ///
330    /// This method walks the stack from top to bottom, querying each namespace
331    /// in order for the given prefix. If none of the namespaces contains the prefix,
332    /// `None` is returned.
333    ///
334    /// # Parameters
335    /// * `prefix` --- namespace prefix.
336    #[inline]
337    pub fn get<'a, P: ?Sized + AsRef<str>>(&'a self, prefix: &P) -> Option<&'a str> {
338        let prefix = prefix.as_ref();
339        for ns in self.0.iter().rev() {
340            match ns.get(prefix) {
341                None => {},
342                r => return r,
343            }
344        }
345        None
346    }
347
348    /// Combines this stack of namespaces into a single namespace.
349    ///
350    /// Namespaces are combined in left-to-right order, that is, rightmost namespace
351    /// elements take priority over leftmost ones.
352    #[must_use]
353    pub fn squash(&self) -> Namespace {
354        let mut result = BTreeMap::new();
355        for ns in &self.0 {
356            result.extend(ns.0.iter().map(|(k, v)| (k.clone(), v.clone())));
357        }
358        Namespace(result)
359    }
360
361    /// Returns an object which implements `Extend` using `put_checked()` instead of `put()`.
362    ///
363    /// See `CheckedTarget` for more information.
364    #[inline]
365    pub fn checked_target(&mut self) -> CheckedTarget<'_> {
366        CheckedTarget(self)
367    }
368
369    /// Returns an iterator over all mappings in this namespace stack.
370    #[inline]
371    #[must_use]
372    pub fn iter(&self) -> NamespaceStackMappings<'_> {
373        self.into_iter()
374    }
375}
376
377/// An iterator over mappings from prefixes to URIs in a namespace stack.
378///
379/// # Example
380/// ```
381/// # use xml::namespace::NamespaceStack;
382/// let mut nst = NamespaceStack::empty();
383/// nst.push_empty();
384/// nst.put("a", "urn:A");
385/// nst.put("b", "urn:B");
386/// nst.push_empty();
387/// nst.put("c", "urn:C");
388///
389/// assert_eq!(vec![("c", "urn:C"), ("a", "urn:A"), ("b", "urn:B")], nst.iter().collect::<Vec<_>>());
390/// ```
391pub struct NamespaceStackMappings<'a> {
392    namespaces: Rev<Iter<'a, Namespace>>,
393    current_namespace: Option<NamespaceMappings<'a>>,
394    used_keys: HashSet<&'a str>,
395}
396
397impl NamespaceStackMappings<'_> {
398    fn go_to_next_namespace(&mut self) -> bool {
399        self.current_namespace = self.namespaces.next().map(|ns| ns.into_iter());
400        self.current_namespace.is_some()
401    }
402}
403
404impl<'a> Iterator for NamespaceStackMappings<'a> {
405    type Item = UriMapping<'a>;
406
407    fn next(&mut self) -> Option<UriMapping<'a>> {
408        // If there is no current namespace and no next namespace, we're finished
409        if self.current_namespace.is_none() && !self.go_to_next_namespace() {
410            return None;
411        }
412        let next_item = self.current_namespace.as_mut()?.next();
413
414        match next_item {
415            // There is an element in the current namespace
416            Some((k, v)) => if self.used_keys.contains(&k) {
417                // If the current key is used, go to the next one
418                self.next()
419            } else {
420                // Otherwise insert the current key to the set of used keys and
421                // return the mapping
422                self.used_keys.insert(k);
423                Some((k, v))
424            },
425            // Current namespace is exhausted
426            None => if self.go_to_next_namespace() {
427                // If there is next namespace, continue from it
428                self.next()
429            } else {
430                // No next namespace, exiting
431                None
432            }
433        }
434    }
435}
436
437impl<'a> IntoIterator for &'a NamespaceStack {
438    type IntoIter = NamespaceStackMappings<'a>;
439    type Item = UriMapping<'a>;
440
441    fn into_iter(self) -> Self::IntoIter {
442        NamespaceStackMappings {
443            namespaces: self.0.iter().rev(),
444            current_namespace: None,
445            used_keys: HashSet::new(),
446        }
447    }
448}
449
450/// A type alias for a pair of `(prefix, uri)` values returned by namespace iterators.
451pub type UriMapping<'a> = (&'a str, &'a str);
452
453impl<'a> Extend<UriMapping<'a>> for Namespace {
454    fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'a>> {
455        for (prefix, uri) in iterable {
456            self.put(prefix, uri);
457        }
458    }
459}
460
461impl<'a> Extend<UriMapping<'a>> for NamespaceStack {
462    fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'a>> {
463        for (prefix, uri) in iterable {
464            self.put(prefix, uri);
465        }
466    }
467}
468
469/// A wrapper around `NamespaceStack` which implements `Extend` using `put_checked()`.
470///
471/// # Example
472///
473/// ```
474/// # use xml::namespace::NamespaceStack;
475///
476/// let mut nst = NamespaceStack::empty();
477/// nst.push_empty();
478/// nst.put("a", "urn:A");
479/// nst.put("b", "urn:B");
480/// nst.push_empty();
481/// nst.put("c", "urn:C");
482///
483/// nst.checked_target().extend(vec![("a", "urn:Z"), ("b", "urn:B"), ("c", "urn:Y"), ("d", "urn:D")]);
484/// assert_eq!(
485///     vec![("a", "urn:Z"), ("c", "urn:C"), ("d", "urn:D"), ("b", "urn:B")],
486///     nst.iter().collect::<Vec<_>>()
487/// );
488/// ```
489///
490/// Compare:
491///
492/// ```
493/// # use xml::namespace::NamespaceStack;
494/// # let mut nst = NamespaceStack::empty();
495/// # nst.push_empty();
496/// # nst.put("a", "urn:A");
497/// # nst.put("b", "urn:B");
498/// # nst.push_empty();
499/// # nst.put("c", "urn:C");
500///
501/// nst.extend(vec![("a", "urn:Z"), ("b", "urn:B"), ("c", "urn:Y"), ("d", "urn:D")]);
502/// assert_eq!(
503///     vec![("a", "urn:Z"), ("b", "urn:B"), ("c", "urn:C"), ("d", "urn:D")],
504///     nst.iter().collect::<Vec<_>>()
505/// );
506/// ```
507pub struct CheckedTarget<'a>(&'a mut NamespaceStack);
508
509impl<'b> Extend<UriMapping<'b>> for CheckedTarget<'_> {
510    fn extend<T>(&mut self, iterable: T) where T: IntoIterator<Item=UriMapping<'b>> {
511        for (prefix, uri) in iterable {
512            self.0.put_checked(prefix, uri);
513        }
514    }
515}