Skip to content

Commit

Permalink
Add reserved namespace bindings.
Browse files Browse the repository at this point in the history
This adds xml and xmlns namespace bindings. These are defined at
https://www.w3.org/TR/xml-names11/#xmlReserved.
  • Loading branch information
wt committed Aug 14, 2023
1 parent f957002 commit 2e32b59
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde = { version = "1.0.100", optional = true }
tokio = { version = "1.10", optional = true, default-features = false, features = ["io-util"] }
memchr = "2.1"
arbitrary = { version = "1.2.3", features = ["derive"], optional = true }
once_cell = "1.18.0"

[dev-dependencies]
criterion = "0.4"
Expand Down
59 changes: 58 additions & 1 deletion src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::events::attributes::Attribute;
use crate::events::BytesStart;
use crate::utils::write_byte_string;
use memchr::memchr;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Formatter};

Expand Down Expand Up @@ -399,6 +401,32 @@ pub(crate) struct NamespaceResolver {
nesting_level: i32,
}

/// These constants define the reserved namespaces for the xml standard.
///
/// The prefix `xml` is by definition bound to the namespace name
/// `http://www.w3.org/XML/1998/namespace`. It may, but need not, be declared, and must not be
/// undeclared or bound to any other namespace name. Other prefixes must not be bound to this
/// namespace name, and it must not be declared as the default namespace.
///
/// The prefix `xmlns` is used only to declare namespace bindings and is by definition bound
/// to the namespace name http://www.w3.org/2000/xmlns/. It must not be declared or
/// undeclared. Other prefixes must not be bound to this namespace name, and it must not be
/// declared as the default namespace. Element names must not have the prefix xmlns.
///
/// [reserved namespaces]: https://www.w3.org/TR/xml-names11/#xmlReserved
static WELL_KNOWN_NAMESPACES: Lazy<HashMap<Prefix, Namespace>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert(
Prefix(b"xml"),
Namespace(b"http://www.w3.org/XML/1998/namespace"),
);
m.insert(
Prefix(b"xmlns"),
Namespace(b"http://www.w3.org/2000/xmlns/"),
);
m
});

impl NamespaceResolver {
/// Begins a new scope and add to it all [namespace bindings] that found in
/// the specified start element.
Expand Down Expand Up @@ -542,7 +570,10 @@ impl NamespaceResolver {
#[inline]
fn maybe_unknown(prefix: Option<Prefix>) -> ResolveResult<'static> {
match prefix {
Some(p) => ResolveResult::Unknown(p.into_inner().to_vec()),
Some(p) => WELL_KNOWN_NAMESPACES.get(&p).map_or_else(
|| ResolveResult::Unknown(p.into_inner().to_vec()),
|p| ResolveResult::Bound(*p),
),
None => ResolveResult::Unbound,
}
}
Expand Down Expand Up @@ -787,6 +818,32 @@ mod namespaces {
}
}

mod builtin_prefixes {
use super::*;
use pretty_assertions::assert_eq;

#[test]
fn undeclared_reserved_prefixes() {
let resolver = NamespaceResolver::default();
let tag = b"random";

for (prefix, namespace) in WELL_KNOWN_NAMESPACES.iter() {
let name_buf = [prefix.into_inner(), tag].join(&b":"[..]);
let name = QName(&name_buf);

assert_eq!(
resolver.resolve(name, b"", true),
(Bound(*namespace), LocalName(tag))
);
assert_eq!(
resolver.resolve(name.clone(), b"", false),
(Bound(*namespace), LocalName(tag))
);
assert_eq!(resolver.find(name.clone(), b""), Bound(*namespace));
}
}
}

#[test]
fn undeclared_prefix() {
let name = QName(b"unknown:prefix");
Expand Down

0 comments on commit 2e32b59

Please sign in to comment.