namespace_set.rs

  1use std::cell::RefCell;
  2use std::collections::BTreeMap;
  3use std::fmt;
  4use std::rc::Rc;
  5
  6#[derive(Clone, Copy, PartialEq, Eq, Debug)]
  7/// Use to compare namespaces
  8pub enum NSChoice<'a> {
  9    /// The element must have no namespace
 10    None,
 11    /// The element's namespace must match the specified namespace
 12    OneOf(&'a str),
 13    /// The element's namespace must be in the specified vector
 14    AnyOf(&'a [&'a str]),
 15    /// The element can have any namespace, or no namespace
 16    Any,
 17}
 18
 19impl<'a> From<&'a str> for NSChoice<'a> {
 20    fn from(ns: &'a str) -> NSChoice<'a> {
 21        NSChoice::OneOf(ns)
 22    }
 23}
 24
 25impl<'a> NSChoice<'a> {
 26    fn compare(&self, ns: Option<&str>) -> bool {
 27        match (ns, &self) {
 28            (None, NSChoice::None) | (None, NSChoice::Any) => true,
 29            (None, NSChoice::OneOf(_)) | (None, NSChoice::AnyOf(_)) => false,
 30            (Some(_), NSChoice::None) => false,
 31            (Some(_), NSChoice::Any) => true,
 32            (Some(ns), NSChoice::OneOf(wanted_ns)) => &ns == wanted_ns,
 33            (Some(ns), NSChoice::AnyOf(wanted_nss)) => wanted_nss.iter().any(|w| &ns == w),
 34        }
 35    }
 36}
 37
 38#[derive(Clone, PartialEq, Eq)]
 39pub struct NamespaceSet {
 40    parent: RefCell<Option<Rc<NamespaceSet>>>,
 41    namespaces: BTreeMap<Option<String>, String>,
 42}
 43
 44impl Default for NamespaceSet {
 45    fn default() -> Self {
 46        NamespaceSet {
 47            parent: RefCell::new(None),
 48            namespaces: BTreeMap::new(),
 49        }
 50    }
 51}
 52
 53impl fmt::Debug for NamespaceSet {
 54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 55        write!(f, "NamespaceSet(")?;
 56        for (prefix, namespace) in &self.namespaces {
 57            write!(
 58                f,
 59                "xmlns{}={:?}, ",
 60                match prefix {
 61                    None => String::new(),
 62                    Some(prefix) => format!(":{}", prefix),
 63                },
 64                namespace
 65            )?;
 66        }
 67        write!(f, "parent: {:?})", *self.parent.borrow())
 68    }
 69}
 70
 71impl NamespaceSet {
 72    pub fn declared_ns(&self) -> &BTreeMap<Option<String>, String> {
 73        &self.namespaces
 74    }
 75
 76    pub fn get(&self, prefix: &Option<String>) -> Option<String> {
 77        match self.namespaces.get(prefix) {
 78            Some(ns) => Some(ns.clone()),
 79            None => match *self.parent.borrow() {
 80                None => None,
 81                Some(ref parent) => parent.get(prefix),
 82            },
 83        }
 84    }
 85
 86    pub fn has<'a, NS: Into<NSChoice<'a>>>(&self, prefix: &Option<String>, wanted_ns: NS) -> bool {
 87        match self.namespaces.get(prefix) {
 88            Some(ns) => wanted_ns.into().compare(Some(ns)),
 89            None => match *self.parent.borrow() {
 90                None => wanted_ns.into().compare(None),
 91                Some(ref parent) => parent.has(prefix, wanted_ns),
 92            },
 93        }
 94    }
 95
 96    pub fn set_parent(&self, parent: Rc<NamespaceSet>) {
 97        let mut parent_ns = self.parent.borrow_mut();
 98        let new_set = parent;
 99        *parent_ns = Some(new_set);
100    }
101}
102
103impl From<BTreeMap<Option<String>, String>> for NamespaceSet {
104    fn from(namespaces: BTreeMap<Option<String>, String>) -> Self {
105        NamespaceSet {
106            parent: RefCell::new(None),
107            namespaces,
108        }
109    }
110}
111
112impl From<Option<String>> for NamespaceSet {
113    fn from(namespace: Option<String>) -> Self {
114        match namespace {
115            None => Self::default(),
116            Some(namespace) => Self::from(namespace),
117        }
118    }
119}
120
121impl From<String> for NamespaceSet {
122    fn from(namespace: String) -> Self {
123        let mut namespaces = BTreeMap::new();
124        namespaces.insert(None, namespace);
125
126        NamespaceSet {
127            parent: RefCell::new(None),
128            namespaces,
129        }
130    }
131}
132
133impl From<(Option<String>, String)> for NamespaceSet {
134    fn from(prefix_namespace: (Option<String>, String)) -> Self {
135        let (prefix, namespace) = prefix_namespace;
136        let mut namespaces = BTreeMap::new();
137        namespaces.insert(prefix, namespace);
138
139        NamespaceSet {
140            parent: RefCell::new(None),
141            namespaces,
142        }
143    }
144}
145
146impl From<(String, String)> for NamespaceSet {
147    fn from(prefix_namespace: (String, String)) -> Self {
148        let (prefix, namespace) = prefix_namespace;
149        Self::from((Some(prefix), namespace))
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn get_has() {
159        let namespaces = NamespaceSet::from("foo".to_owned());
160        assert_eq!(namespaces.get(&None), Some("foo".to_owned()));
161        assert!(namespaces.has(&None, "foo"));
162    }
163
164    #[test]
165    fn get_has_prefixed() {
166        let namespaces = NamespaceSet::from(("x".to_owned(), "bar".to_owned()));
167        assert_eq!(
168            namespaces.get(&Some("x".to_owned())),
169            Some("bar".to_owned())
170        );
171        assert!(namespaces.has(&Some("x".to_owned()), "bar"));
172    }
173
174    #[test]
175    fn get_has_recursive() {
176        let mut parent = NamespaceSet::from("foo".to_owned());
177        for _ in 0..1000 {
178            let namespaces = NamespaceSet::default();
179            namespaces.set_parent(Rc::new(parent));
180            assert_eq!(namespaces.get(&None), Some("foo".to_owned()));
181            assert!(namespaces.has(&None, "foo"));
182            parent = namespaces;
183        }
184    }
185
186    #[test]
187    fn get_has_prefixed_recursive() {
188        let mut parent = NamespaceSet::from(("x".to_owned(), "bar".to_owned()));
189        for _ in 0..1000 {
190            let namespaces = NamespaceSet::default();
191            namespaces.set_parent(Rc::new(parent));
192            assert_eq!(
193                namespaces.get(&Some("x".to_owned())),
194                Some("bar".to_owned())
195            );
196            assert!(namespaces.has(&Some("x".to_owned()), "bar"));
197            parent = namespaces;
198        }
199    }
200
201    #[test]
202    fn debug_looks_correct() {
203        let parent = NamespaceSet::from("http://www.w3.org/2000/svg".to_owned());
204        let namespaces = NamespaceSet::from((
205            "xhtml".to_owned(),
206            "http://www.w3.org/1999/xhtml".to_owned(),
207        ));
208        namespaces.set_parent(Rc::new(parent));
209        assert_eq!(format!("{:?}", namespaces), "NamespaceSet(xmlns:xhtml=\"http://www.w3.org/1999/xhtml\", parent: Some(NamespaceSet(xmlns=\"http://www.w3.org/2000/svg\", parent: None)))");
210    }
211}