From bc413c64c939795ad45efcc8bc72925d713f24a1 Mon Sep 17 00:00:00 2001 From: Wren Turkal Date: Tue, 24 Jan 2023 23:58:32 -0800 Subject: [PATCH] Add reserved namespace bindings. This adds xml and xmlns namespace bindings. These are defined at https://www.w3.org/TR/xml-names11/#xmlReserved. This also updates the MSRV to 1.60. --- Cargo.toml | 3 ++- Changelog.md | 4 ++++ src/name.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 805607bc..7c0e19ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,8 @@ repository = "https://github.com/tafia/quick-xml" keywords = ["xml", "serde", "parser", "writer", "html"] categories = ["asynchronous", "encoding", "parsing", "parser-implementations"] license = "MIT" -rust-version = "1.52" include = ["src/*", "LICENSE-MIT.md", "README.md"] +rust-version = "1.60" [dependencies] document-features = { version = "0.2", optional = true } @@ -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" [dev-dependencies] criterion = "0.4" diff --git a/Changelog.md b/Changelog.md index 052697c2..86437480 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,10 +12,14 @@ ### New Features +- [#545]: resolve well-known namespaces to their approrpriate URIs + ### Bug Fixes ### Misc Changes +- [#545]: bump MSRV to 1.60 + ## 0.30.0 -- 2023-07-23 diff --git a/src/name.rs b/src/name.rs index 07d261ab..fff3ec5f 100644 --- a/src/name.rs +++ b/src/name.rs @@ -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}; @@ -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> = 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. @@ -542,7 +570,10 @@ impl NamespaceResolver { #[inline] fn maybe_unknown(prefix: Option) -> 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, } } @@ -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");