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