xsd_schema/namespace/
context.rs1use super::table::{well_known, NameTable};
7use crate::ids::NameId;
8use std::collections::HashMap;
9
10pub struct NamespaceContext<'a> {
35 name_table: &'a mut NameTable,
37 scopes: Vec<HashMap<NameId, NameId>>,
39 default_namespace: Option<NameId>,
41 default_ns_stack: Vec<Option<NameId>>,
43}
44
45impl<'a> NamespaceContext<'a> {
46 pub fn new(name_table: &'a mut NameTable) -> Self {
52 let mut ctx = Self {
53 name_table,
54 scopes: Vec::new(),
55 default_namespace: None,
56 default_ns_stack: Vec::new(),
57 };
58
59 ctx.push_scope();
61
62 ctx.scopes[0].insert(well_known::XML_PREFIX, well_known::XML_NAMESPACE);
64 ctx.scopes[0].insert(well_known::XMLNS_PREFIX, well_known::XMLNS_NAMESPACE);
65
66 ctx
67 }
68
69 pub fn name_table(&self) -> &NameTable {
71 self.name_table
72 }
73
74 pub fn name_table_mut(&mut self) -> &mut NameTable {
76 self.name_table
77 }
78
79 pub fn push_scope(&mut self) {
81 self.scopes.push(HashMap::new());
82 self.default_ns_stack.push(self.default_namespace);
83 }
84
85 pub fn pop_scope(&mut self) {
91 self.scopes.pop().expect("No scope to pop");
92 self.default_namespace = self.default_ns_stack.pop().flatten();
93 }
94
95 pub fn add_namespace(&mut self, prefix: &str, uri: &str) {
102 let uri_id = self.name_table.add(uri);
103
104 if prefix.is_empty() {
105 self.default_namespace = if uri.is_empty() {
107 None } else {
109 Some(uri_id)
110 };
111 } else {
112 let prefix_id = self.name_table.add(prefix);
113 if let Some(scope) = self.scopes.last_mut() {
114 scope.insert(prefix_id, uri_id);
115 }
116 }
117 }
118
119 pub fn lookup_namespace(&self, prefix: &str) -> Option<NameId> {
121 if let Some(prefix_id) = self.name_table.get(prefix) {
122 self.lookup_namespace_by_id(prefix_id)
123 } else {
124 None
125 }
126 }
127
128 pub fn lookup_namespace_by_id(&self, prefix_id: NameId) -> Option<NameId> {
130 for scope in self.scopes.iter().rev() {
132 if let Some(&ns_id) = scope.get(&prefix_id) {
133 return Some(ns_id);
134 }
135 }
136 None
137 }
138
139 pub fn default_namespace(&self) -> Option<NameId> {
141 self.default_namespace
142 }
143
144 pub fn set_default_namespace(&mut self, uri: Option<&str>) {
146 self.default_namespace = uri.map(|u| self.name_table.add(u));
147 }
148
149 pub fn set_default_namespace_id(&mut self, ns: Option<NameId>) {
158 self.default_namespace = ns;
159 }
160
161 pub fn get_namespaces_in_scope(&self, scope_filter: NamespaceScope) -> Vec<(NameId, NameId)> {
169 let mut result = HashMap::new();
170
171 for scope in &self.scopes {
173 for (&prefix_id, &ns_id) in scope {
174 result.insert(prefix_id, ns_id);
175 }
176 }
177
178 result
180 .into_iter()
181 .filter(|&(prefix_id, _)| match scope_filter {
182 NamespaceScope::All => true,
183 NamespaceScope::ExcludeXml => {
184 prefix_id != well_known::XML_PREFIX && prefix_id != well_known::XMLNS_PREFIX
185 }
186 })
187 .collect()
188 }
189
190 pub fn depth(&self) -> usize {
192 self.scopes.len()
193 }
194
195 pub fn snapshot(&self) -> NamespaceContextSnapshot {
197 NamespaceContextSnapshot {
198 default_ns: self.default_namespace,
199 bindings: self.get_namespaces_in_scope(NamespaceScope::ExcludeXml),
200 }
201 }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum NamespaceScope {
207 All,
209 ExcludeXml,
211}
212
213#[derive(Debug, Clone, Default)]
217pub struct NamespaceContextSnapshot {
218 pub default_ns: Option<NameId>,
220 pub bindings: Vec<(NameId, NameId)>,
222}
223
224impl NamespaceContextSnapshot {
225 pub fn resolve_prefix(&self, prefix_id: NameId) -> Option<NameId> {
230 for &(p, ns) in &self.bindings {
231 if p == prefix_id {
232 return Some(ns);
233 }
234 }
235 if prefix_id == well_known::XML_PREFIX {
237 return Some(well_known::XML_NAMESPACE);
238 }
239 None
240 }
241
242 pub fn default_namespace(&self) -> Option<NameId> {
244 self.default_ns
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_push_pop_scope() {
254 let mut table = NameTable::new();
255 let mut ctx = NamespaceContext::new(&mut table);
256
257 let initial_depth = ctx.depth();
258 ctx.push_scope();
259 assert_eq!(ctx.depth(), initial_depth + 1);
260 ctx.pop_scope();
261 assert_eq!(ctx.depth(), initial_depth);
262 }
263
264 #[test]
265 fn test_add_and_lookup_namespace() {
266 let mut table = NameTable::new();
267 let mut ctx = NamespaceContext::new(&mut table);
268
269 ctx.push_scope();
270 ctx.add_namespace("foo", "http://example.com/foo");
271
272 let ns = ctx.lookup_namespace("foo");
273 assert!(ns.is_some());
274
275 let ns_str = ctx.name_table().resolve(ns.unwrap());
277 assert_eq!(ns_str, "http://example.com/foo");
278 }
279
280 #[test]
281 fn test_scope_shadowing() {
282 let mut table = NameTable::new();
283 let mut ctx = NamespaceContext::new(&mut table);
284
285 ctx.push_scope();
286 ctx.add_namespace("foo", "http://outer.com");
287
288 ctx.push_scope();
289 ctx.add_namespace("foo", "http://inner.com");
290
291 let ns = ctx.lookup_namespace("foo").unwrap();
293 assert_eq!(ctx.name_table().resolve(ns), "http://inner.com");
294
295 ctx.pop_scope();
296
297 let ns = ctx.lookup_namespace("foo").unwrap();
299 assert_eq!(ctx.name_table().resolve(ns), "http://outer.com");
300 }
301
302 #[test]
303 fn test_default_namespace() {
304 let mut table = NameTable::new();
305 let mut ctx = NamespaceContext::new(&mut table);
306
307 assert!(ctx.default_namespace().is_none());
308
309 ctx.push_scope();
310 ctx.add_namespace("", "http://default.com");
311 assert!(ctx.default_namespace().is_some());
312
313 ctx.pop_scope();
314 assert!(ctx.default_namespace().is_none());
315 }
316
317 #[test]
318 fn test_undeclare_default_namespace() {
319 let mut table = NameTable::new();
320 let mut ctx = NamespaceContext::new(&mut table);
321
322 ctx.push_scope();
323 ctx.add_namespace("", "http://default.com");
324 assert!(ctx.default_namespace().is_some());
325
326 ctx.push_scope();
327 ctx.add_namespace("", ""); assert!(ctx.default_namespace().is_none());
329 }
330
331 #[test]
332 fn test_xml_prefix_always_bound() {
333 let mut table = NameTable::new();
334 let ctx = NamespaceContext::new(&mut table);
335
336 let ns = ctx.lookup_namespace("xml");
337 assert!(ns.is_some());
338 assert_eq!(
339 ctx.name_table().resolve(ns.unwrap()),
340 super::super::table::XML_NAMESPACE
341 );
342 }
343
344 #[test]
345 fn test_snapshot() {
346 let mut table = NameTable::new();
347 let mut ctx = NamespaceContext::new(&mut table);
348
349 ctx.push_scope();
350 ctx.add_namespace("foo", "http://foo.com");
351 ctx.add_namespace("", "http://default.com");
352
353 let snapshot = ctx.snapshot();
354 assert!(snapshot.default_ns.is_some());
355 assert!(!snapshot.bindings.is_empty());
356 }
357}