minidom: Add NSChoice enum to extent element.is and .has_ns API

Maxime “pep” Buquet created

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>

Change summary

minidom-rs/CHANGELOG.md         |  1 
minidom-rs/src/element.rs       | 32 ++++++++++++++++++++++++++----
minidom-rs/src/lib.rs           |  1 
minidom-rs/src/namespace_set.rs | 36 ++++++++++++++++++++++++++++++++--
4 files changed, 62 insertions(+), 8 deletions(-)

Detailed changes

minidom-rs/CHANGELOG.md 🔗

@@ -1,6 +1,7 @@
 Version XXX, released YYY:
   * Changes
     * Update edition to 2018
+    * Add NSChoice enum to allow comparing NSs differently
   * Fixes
     * Update old CI configuration with newer Rust images
 Version 0.11.1, released 2019-09-06:

minidom-rs/src/element.rs 🔗

@@ -2,7 +2,7 @@
 
 use crate::convert::IntoAttributeValue;
 use crate::error::{Error, Result};
-use crate::namespace_set::NamespaceSet;
+use crate::namespace_set::{NamespaceSet, NSChoice};
 use crate::node::Node;
 
 use std::collections::{btree_map, BTreeMap};
@@ -246,7 +246,7 @@ impl Element {
     /// # Examples
     ///
     /// ```rust
-    /// use minidom::Element;
+    /// use minidom::{Element, NSChoice};
     ///
     /// let elem = Element::builder("name").ns("namespace").build();
     ///
@@ -254,8 +254,19 @@ impl Element {
     /// assert_eq!(elem.is("name", "wrong"), false);
     /// assert_eq!(elem.is("wrong", "namespace"), false);
     /// assert_eq!(elem.is("wrong", "wrong"), false);
+    ///
+    /// assert_eq!(elem.is("name", NSChoice::None), false);
+    /// assert_eq!(elem.is("name", NSChoice::OneOf("namespace")), true);
+    /// assert_eq!(elem.is("name", NSChoice::OneOf("foo")), false);
+    /// assert_eq!(elem.is("name", NSChoice::AnyOf(vec!["foo", "namespace"])), true);
+    /// assert_eq!(elem.is("name", NSChoice::Any), true);
+    ///
+    /// let elem2 = Element::builder("name").build();
+    ///
+    /// assert_eq!(elem2.is("name", NSChoice::None), true);
+    /// assert_eq!(elem2.is("name", NSChoice::Any), true);
     /// ```
-    pub fn is<N: AsRef<str>, NS: AsRef<str>>(&self, name: N, namespace: NS) -> bool {
+    pub fn is<'a, N: AsRef<str>, NS: Into<NSChoice<'a>>>(&self, name: N, namespace: NS) -> bool {
         self.name == name.as_ref() && self.has_ns(namespace)
     }
 
@@ -264,14 +275,25 @@ impl Element {
     /// # Examples
     ///
     /// ```rust
-    /// use minidom::Element;
+    /// use minidom::{Element, NSChoice};
     ///
     /// let elem = Element::builder("name").ns("namespace").build();
     ///
     /// assert_eq!(elem.has_ns("namespace"), true);
     /// assert_eq!(elem.has_ns("wrong"), false);
+    ///
+    /// assert_eq!(elem.has_ns(NSChoice::None), false);
+    /// assert_eq!(elem.has_ns(NSChoice::OneOf("namespace")), true);
+    /// assert_eq!(elem.has_ns(NSChoice::OneOf("foo")), false);
+    /// assert_eq!(elem.has_ns(NSChoice::AnyOf(vec!["foo", "namespace"])), true);
+    /// assert_eq!(elem.has_ns(NSChoice::Any), true);
+    ///
+    /// let elem2 = Element::builder("name").build();
+    ///
+    /// assert_eq!(elem2.has_ns(NSChoice::None), true);
+    /// assert_eq!(elem2.has_ns(NSChoice::Any), true);
     /// ```
-    pub fn has_ns<NS: AsRef<str>>(&self, namespace: NS) -> bool {
+    pub fn has_ns<'a, NS: Into<NSChoice<'a>>>(&self, namespace: NS) -> bool {
         self.namespaces.has(&self.prefix, namespace)
     }
 

minidom-rs/src/lib.rs 🔗

@@ -77,5 +77,6 @@ mod tests;
 
 pub use convert::IntoAttributeValue;
 pub use element::{Children, ChildrenMut, Element, ElementBuilder};
+pub use namespace_set::NSChoice;
 pub use error::{Error, Result};
 pub use node::Node;

minidom-rs/src/namespace_set.rs 🔗

@@ -3,6 +3,25 @@ use std::collections::BTreeMap;
 use std::fmt;
 use std::rc::Rc;
 
+#[derive(Clone, PartialEq, Eq, Debug)]
+/// Use to compare namespaces
+pub enum NSChoice<'a> {
+    /// The element must have no namespace
+    None,
+    /// The element's namespace must match the specified namespace
+    OneOf(&'a str),
+    /// The element's namespace must be in the specified vector
+    AnyOf(Vec<&'a str>),
+    /// The element can have any namespace, or no namespace
+    Any,
+}
+
+impl<'a> From<&'a str> for NSChoice<'a> {
+    fn from(ns: &'a str) -> NSChoice<'a> {
+        NSChoice::OneOf(ns)
+    }
+}
+
 #[derive(Clone, PartialEq, Eq)]
 pub struct NamespaceSet {
     parent: RefCell<Option<Rc<NamespaceSet>>>,
@@ -51,11 +70,22 @@ impl NamespaceSet {
         }
     }
 
-    pub fn has<NS: AsRef<str>>(&self, prefix: &Option<String>, wanted_ns: NS) -> bool {
+    fn compare_ns<'a>(&self, ns: Option<&str>, wanted_ns: NSChoice<'a>) -> bool {
+        match (ns, wanted_ns) {
+            (None, NSChoice::None) | (None, NSChoice::Any) => true,
+            (None, NSChoice::OneOf(_)) | (None, NSChoice::AnyOf(_)) => false,
+            (Some(_), NSChoice::None) => false,
+            (Some(_), NSChoice::Any) => true,
+            (Some(ns), NSChoice::OneOf(wanted_ns)) => ns == wanted_ns,
+            (Some(ns), NSChoice::AnyOf(wanted_nss)) => wanted_nss.iter().any(|w| &ns == w),
+        }
+    }
+
+    pub fn has<'a, NS: Into<NSChoice<'a>>>(&self, prefix: &Option<String>, wanted_ns: NS) -> bool {
         match self.namespaces.get(prefix) {
-            Some(ns) => ns == wanted_ns.as_ref(),
+            Some(ns) => self.compare_ns(Some(ns), wanted_ns.into()),
             None => match *self.parent.borrow() {
-                None => false,
+                None => self.compare_ns(None, wanted_ns.into()),
                 Some(ref parent) => parent.has(prefix, wanted_ns),
             },
         }