2017-08-13 02:35:24 +02:00
|
|
|
use std::cell::RefCell;
|
2019-10-23 01:32:41 +02:00
|
|
|
use std::collections::BTreeMap;
|
2019-02-27 18:04:16 +01:00
|
|
|
use std::fmt;
|
2017-08-13 02:35:24 +02:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
2019-11-26 22:47:35 +11:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
2019-11-09 01:26:48 +01:00
|
|
|
/// 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
|
2019-11-26 22:47:35 +11:00
|
|
|
AnyOf(&'a [&'a str]),
|
2019-11-09 01:26:48 +01:00
|
|
|
/// 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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 12:45:31 +01:00
|
|
|
impl<'a> NSChoice<'a> {
|
|
|
|
|
fn compare(&self, ns: Option<&str>) -> bool {
|
|
|
|
|
match (ns, &self) {
|
|
|
|
|
(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),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-27 18:04:16 +01:00
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
2017-08-13 02:35:24 +02:00
|
|
|
pub struct NamespaceSet {
|
|
|
|
|
parent: RefCell<Option<Rc<NamespaceSet>>>,
|
|
|
|
|
namespaces: BTreeMap<Option<String>, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for NamespaceSet {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
NamespaceSet {
|
|
|
|
|
parent: RefCell::new(None),
|
|
|
|
|
namespaces: BTreeMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-27 18:04:16 +01:00
|
|
|
impl fmt::Debug for NamespaceSet {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
write!(f, "NamespaceSet(")?;
|
|
|
|
|
for (prefix, namespace) in &self.namespaces {
|
2019-10-23 01:32:41 +02:00
|
|
|
write!(
|
|
|
|
|
f,
|
|
|
|
|
"xmlns{}={:?}, ",
|
|
|
|
|
match prefix {
|
|
|
|
|
None => String::new(),
|
|
|
|
|
Some(prefix) => format!(":{}", prefix),
|
|
|
|
|
},
|
|
|
|
|
namespace
|
|
|
|
|
)?;
|
2019-02-27 18:04:16 +01:00
|
|
|
}
|
|
|
|
|
write!(f, "parent: {:?})", *self.parent.borrow())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-13 02:35:24 +02:00
|
|
|
impl NamespaceSet {
|
|
|
|
|
pub fn declared_ns(&self) -> &BTreeMap<Option<String>, String> {
|
|
|
|
|
&self.namespaces
|
|
|
|
|
}
|
2019-07-27 13:14:37 +02:00
|
|
|
|
2017-08-13 02:35:24 +02:00
|
|
|
pub fn get(&self, prefix: &Option<String>) -> Option<String> {
|
|
|
|
|
match self.namespaces.get(prefix) {
|
|
|
|
|
Some(ns) => Some(ns.clone()),
|
|
|
|
|
None => match *self.parent.borrow() {
|
|
|
|
|
None => None,
|
2019-10-23 01:32:41 +02:00
|
|
|
Some(ref parent) => parent.get(prefix),
|
2017-08-13 02:35:24 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 01:26:48 +01:00
|
|
|
pub fn has<'a, NS: Into<NSChoice<'a>>>(&self, prefix: &Option<String>, wanted_ns: NS) -> bool {
|
2017-08-13 02:35:24 +02:00
|
|
|
match self.namespaces.get(prefix) {
|
2019-11-09 12:45:31 +01:00
|
|
|
Some(ns) => wanted_ns.into().compare(Some(ns)),
|
2017-08-13 02:35:24 +02:00
|
|
|
None => match *self.parent.borrow() {
|
2019-11-09 12:45:31 +01:00
|
|
|
None => wanted_ns.into().compare(None),
|
2019-10-23 01:32:41 +02:00
|
|
|
Some(ref parent) => parent.has(prefix, wanted_ns),
|
2017-08-13 02:35:24 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_parent(&self, parent: Rc<NamespaceSet>) {
|
|
|
|
|
let mut parent_ns = self.parent.borrow_mut();
|
|
|
|
|
let new_set = parent;
|
|
|
|
|
*parent_ns = Some(new_set);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<BTreeMap<Option<String>, String>> for NamespaceSet {
|
|
|
|
|
fn from(namespaces: BTreeMap<Option<String>, String>) -> Self {
|
|
|
|
|
NamespaceSet {
|
|
|
|
|
parent: RefCell::new(None),
|
2019-02-21 21:06:23 +01:00
|
|
|
namespaces,
|
2017-08-13 02:35:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Option<String>> for NamespaceSet {
|
|
|
|
|
fn from(namespace: Option<String>) -> Self {
|
|
|
|
|
match namespace {
|
|
|
|
|
None => Self::default(),
|
|
|
|
|
Some(namespace) => Self::from(namespace),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<String> for NamespaceSet {
|
|
|
|
|
fn from(namespace: String) -> Self {
|
|
|
|
|
let mut namespaces = BTreeMap::new();
|
|
|
|
|
namespaces.insert(None, namespace);
|
|
|
|
|
|
|
|
|
|
NamespaceSet {
|
|
|
|
|
parent: RefCell::new(None),
|
2019-02-21 21:06:23 +01:00
|
|
|
namespaces,
|
2017-08-13 02:35:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<(Option<String>, String)> for NamespaceSet {
|
|
|
|
|
fn from(prefix_namespace: (Option<String>, String)) -> Self {
|
|
|
|
|
let (prefix, namespace) = prefix_namespace;
|
|
|
|
|
let mut namespaces = BTreeMap::new();
|
|
|
|
|
namespaces.insert(prefix, namespace);
|
|
|
|
|
|
|
|
|
|
NamespaceSet {
|
|
|
|
|
parent: RefCell::new(None),
|
2019-02-21 21:06:23 +01:00
|
|
|
namespaces,
|
2017-08-13 02:35:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<(String, String)> for NamespaceSet {
|
|
|
|
|
fn from(prefix_namespace: (String, String)) -> Self {
|
|
|
|
|
let (prefix, namespace) = prefix_namespace;
|
|
|
|
|
Self::from((Some(prefix), namespace))
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-13 02:48:28 +02:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn get_has() {
|
|
|
|
|
let namespaces = NamespaceSet::from("foo".to_owned());
|
|
|
|
|
assert_eq!(namespaces.get(&None), Some("foo".to_owned()));
|
|
|
|
|
assert!(namespaces.has(&None, "foo"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn get_has_prefixed() {
|
|
|
|
|
let namespaces = NamespaceSet::from(("x".to_owned(), "bar".to_owned()));
|
2019-10-23 01:32:41 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
namespaces.get(&Some("x".to_owned())),
|
|
|
|
|
Some("bar".to_owned())
|
|
|
|
|
);
|
2017-08-13 02:48:28 +02:00
|
|
|
assert!(namespaces.has(&Some("x".to_owned()), "bar"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn get_has_recursive() {
|
|
|
|
|
let mut parent = NamespaceSet::from("foo".to_owned());
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
|
let namespaces = NamespaceSet::default();
|
|
|
|
|
namespaces.set_parent(Rc::new(parent));
|
|
|
|
|
assert_eq!(namespaces.get(&None), Some("foo".to_owned()));
|
|
|
|
|
assert!(namespaces.has(&None, "foo"));
|
|
|
|
|
parent = namespaces;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn get_has_prefixed_recursive() {
|
|
|
|
|
let mut parent = NamespaceSet::from(("x".to_owned(), "bar".to_owned()));
|
|
|
|
|
for _ in 0..1000 {
|
|
|
|
|
let namespaces = NamespaceSet::default();
|
|
|
|
|
namespaces.set_parent(Rc::new(parent));
|
2019-10-23 01:32:41 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
namespaces.get(&Some("x".to_owned())),
|
|
|
|
|
Some("bar".to_owned())
|
|
|
|
|
);
|
2017-08-13 02:48:28 +02:00
|
|
|
assert!(namespaces.has(&Some("x".to_owned()), "bar"));
|
|
|
|
|
parent = namespaces;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-27 18:04:16 +01:00
|
|
|
#[test]
|
|
|
|
|
fn debug_looks_correct() {
|
|
|
|
|
let parent = NamespaceSet::from("http://www.w3.org/2000/svg".to_owned());
|
2019-10-23 01:32:41 +02:00
|
|
|
let namespaces = NamespaceSet::from((
|
|
|
|
|
"xhtml".to_owned(),
|
|
|
|
|
"http://www.w3.org/1999/xhtml".to_owned(),
|
|
|
|
|
));
|
2019-02-27 18:04:16 +01:00
|
|
|
namespaces.set_parent(Rc::new(parent));
|
|
|
|
|
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)))");
|
|
|
|
|
}
|
2017-08-13 02:48:28 +02:00
|
|
|
}
|