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}