Skip to main content

xdoc/core/
namespace.rs

1use std::collections::BTreeMap;
2
3use super::{validate_namespace_binding, NamespacePrefix, NamespaceUri, XmlResult};
4
5/// Namespace declaration attached to an XML element or namespace table.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct NamespaceDeclaration {
8    prefix: Option<NamespacePrefix>,
9    uri: NamespaceUri,
10}
11
12impl NamespaceDeclaration {
13    pub fn default(uri: impl Into<String>) -> XmlResult<Self> {
14        let uri = uri.into();
15        validate_namespace_binding(None, &uri)?;
16        Ok(Self {
17            prefix: None,
18            uri: NamespaceUri::new(uri)?,
19        })
20    }
21
22    pub fn prefixed(prefix: impl Into<String>, uri: impl Into<String>) -> XmlResult<Self> {
23        let prefix = prefix.into();
24        let uri = uri.into();
25        validate_namespace_binding(Some(&prefix), &uri)?;
26        Ok(Self {
27            prefix: Some(NamespacePrefix::new(prefix)?),
28            uri: NamespaceUri::new(uri)?,
29        })
30    }
31
32    pub fn prefix(&self) -> Option<&NamespacePrefix> {
33        self.prefix.as_ref()
34    }
35
36    pub fn uri(&self) -> &NamespaceUri {
37        &self.uri
38    }
39}
40
41/// Namespace bindings available while constructing, parsing, or writing XML.
42#[derive(Debug, Clone, Default, PartialEq, Eq)]
43pub struct NamespaceTable {
44    default_namespace: Option<NamespaceUri>,
45    prefixed: BTreeMap<NamespacePrefix, NamespaceUri>,
46}
47
48impl NamespaceTable {
49    pub fn new() -> Self {
50        Self::default()
51    }
52
53    pub fn declare(&mut self, declaration: NamespaceDeclaration) {
54        match declaration.prefix {
55            Some(prefix) => {
56                self.prefixed.insert(prefix, declaration.uri);
57            }
58            None => {
59                self.default_namespace = Some(declaration.uri);
60            }
61        }
62    }
63
64    pub fn declare_default(&mut self, uri: impl Into<String>) -> XmlResult<()> {
65        self.declare(NamespaceDeclaration::default(uri)?);
66        Ok(())
67    }
68
69    pub fn declare_prefix(
70        &mut self,
71        prefix: impl Into<String>,
72        uri: impl Into<String>,
73    ) -> XmlResult<()> {
74        self.declare(NamespaceDeclaration::prefixed(prefix, uri)?);
75        Ok(())
76    }
77
78    pub fn default_namespace(&self) -> Option<&NamespaceUri> {
79        self.default_namespace.as_ref()
80    }
81
82    pub fn resolve_prefix(&self, prefix: &NamespacePrefix) -> Option<&NamespaceUri> {
83        self.prefixed.get(prefix)
84    }
85
86    pub fn iter_prefixed(&self) -> impl Iterator<Item = (&NamespacePrefix, &NamespaceUri)> {
87        self.prefixed.iter()
88    }
89
90    pub fn len(&self) -> usize {
91        self.prefixed.len() + usize::from(self.default_namespace.is_some())
92    }
93
94    pub fn is_empty(&self) -> bool {
95        self.default_namespace.is_none() && self.prefixed.is_empty()
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn namespace_table_registers_and_resolves_prefixes() {
105        let mut table = NamespaceTable::new();
106        table
107            .declare_prefix("cbc", "urn:example:cbc")
108            .expect("valid namespace");
109
110        let prefix = NamespacePrefix::new("cbc").expect("valid prefix");
111        assert_eq!(
112            table.resolve_prefix(&prefix).map(NamespaceUri::as_str),
113            Some("urn:example:cbc")
114        );
115    }
116
117    #[test]
118    fn namespace_table_resolves_default_namespace() {
119        let mut table = NamespaceTable::new();
120        table
121            .declare_default("urn:example:document")
122            .expect("valid namespace");
123
124        assert_eq!(
125            table.default_namespace().map(NamespaceUri::as_str),
126            Some("urn:example:document")
127        );
128    }
129
130    #[test]
131    fn namespace_declaration_rejects_invalid_prefix() {
132        let error = NamespaceDeclaration::prefixed("", "urn:example")
133            .expect_err("empty namespace prefix must fail");
134
135        assert_eq!(error.kind(), &super::super::ErrorKind::InvalidName);
136    }
137
138    #[test]
139    fn namespace_declaration_respects_reserved_xml_bindings() {
140        NamespaceDeclaration::prefixed("xml", super::super::XML_NAMESPACE_URI)
141            .expect("xml prefix binding");
142
143        assert_eq!(
144            NamespaceDeclaration::prefixed("xml", "urn:wrong")
145                .expect_err("xml prefix must use XML namespace URI")
146                .kind(),
147            &super::super::ErrorKind::InvalidNamespace
148        );
149        assert_eq!(
150            NamespaceDeclaration::prefixed("doc", super::super::XML_NAMESPACE_URI)
151                .expect_err("XML namespace URI is reserved for xml prefix")
152                .kind(),
153            &super::super::ErrorKind::InvalidNamespace
154        );
155        assert_eq!(
156            NamespaceDeclaration::prefixed("xmlns", "urn:any")
157                .expect_err("xmlns prefix is reserved")
158                .kind(),
159            &super::super::ErrorKind::InvalidNamespace
160        );
161        assert_eq!(
162            NamespaceDeclaration::default(super::super::XMLNS_NAMESPACE_URI)
163                .expect_err("XMLNS namespace URI cannot be declared")
164                .kind(),
165            &super::super::ErrorKind::InvalidNamespace
166        );
167    }
168
169    #[test]
170    fn namespace_table_keeps_prefixed_bindings_deterministic() {
171        let mut table = NamespaceTable::new();
172        table.declare_prefix("z", "urn:z").expect("valid namespace");
173        table.declare_prefix("a", "urn:a").expect("valid namespace");
174
175        let prefixes = table
176            .iter_prefixed()
177            .map(|(prefix, _)| prefix.as_str())
178            .collect::<Vec<_>>();
179
180        assert_eq!(prefixes, vec!["a", "z"]);
181    }
182}