@@ -54,6 +54,9 @@ Version NEXT:
`AsXmlText` and `FromXmlText` implementations based on the standard
library's `Display` and `FromStr` traits.
- `AsXmlDyn`, a dyn-compatible variant of `AsXml` (!573).
+ - `FromXml::xml_name_matcher()` and related types, which allow `FromXml`
+ implementors to provide cachable hints to callers about which XML
+ elements are valid candidates for parsing (!573).
* Changes
- Generated AsXml iterator and FromXml builder types are now
doc(hidden), to not clutter hand-written documentation with auto
@@ -17,6 +17,68 @@ use alloc::boxed::Box;
use crate::error::{Error, FromEventsError};
use crate::{FromEventsBuilder, FromXml};
+/// Match an XML element qualified name.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum XmlNameMatcher<'x> {
+ /// Match any XML element
+ Any,
+
+ /// Match any XML element in the given namespace.
+ InNamespace(&'x str),
+
+ /// Match any XML element with the exact namespace/name combination.
+ Specific(&'x str, &'x str),
+}
+
+impl<'x> XmlNameMatcher<'x> {
+ /// Return the superset of two `XmlNameMatcher` instances.
+ pub const fn superset(self, other: Self) -> Self {
+ match self {
+ Self::Any => Self::Any,
+ Self::InNamespace(my_namespace) => match other {
+ Self::Any => Self::Any,
+ Self::InNamespace(other_namespace) | Self::Specific(other_namespace, _) => {
+ if crate::util::const_str_eq(my_namespace, other_namespace) {
+ Self::InNamespace(my_namespace)
+ } else {
+ Self::Any
+ }
+ }
+ },
+ Self::Specific(my_namespace, my_name) => match other {
+ Self::Any => Self::Any,
+ Self::InNamespace(other_namespace) => {
+ if crate::util::const_str_eq(my_namespace, other_namespace) {
+ Self::InNamespace(my_namespace)
+ } else {
+ Self::Any
+ }
+ }
+ Self::Specific(other_namespace, other_name) => {
+ if crate::util::const_str_eq(my_namespace, other_namespace) {
+ if crate::util::const_str_eq(my_name, other_name) {
+ Self::Specific(my_name, other_name)
+ } else {
+ Self::InNamespace(my_namespace)
+ }
+ } else {
+ Self::Any
+ }
+ }
+ },
+ }
+ }
+
+ /// Return true if the given `qname` matches this matcher.
+ pub fn matches(&self, qname: &rxml::QName) -> bool {
+ match self {
+ Self::Any => true,
+ Self::InNamespace(ns) => qname.0.as_str() == *ns,
+ Self::Specific(ns, name) => qname.0.as_str() == *ns && qname.1.as_str() == *name,
+ }
+ }
+}
+
/// # Parsing context for [`FromEventsBuilder`]
///
/// For the most part, [`FromEventsBuilder`] implementations can work with
@@ -38,6 +38,34 @@ pub mod minidom_compat;
mod rxml_util;
pub mod text;
+// This is a hack to not make `const_str_eq` publicly available, except
+// through the `exports` module if the `macros` feature is enabled, but have
+// it available internally in all cases.
+mod util {
+ /// Compile-time comparison of two strings.
+ ///
+ /// Used by macro-generated code.
+ ///
+ /// This is necessary because `<str as PartialEq>::eq` is not `const`.
+ pub const fn const_str_eq(a: &str, b: &str) -> bool {
+ let a = a.as_bytes();
+ let b = b.as_bytes();
+ if a.len() != b.len() {
+ return false;
+ }
+
+ let mut i = 0;
+ while i < a.len() {
+ if a[i] != b[i] {
+ return false;
+ }
+ i += 1;
+ }
+
+ true
+ }
+}
+
#[doc(hidden)]
pub mod exports {
#[cfg(all(feature = "minidom", feature = "macros"))]
@@ -75,35 +103,15 @@ pub mod exports {
#[cfg(feature = "macros")]
pub type CoreU8 = u8;
- /// Compile-time comparison of two strings.
- ///
- /// Used by macro-generated code.
- ///
- /// This is necessary because `<str as PartialEq>::eq` is not `const`.
#[cfg(feature = "macros")]
- pub const fn const_str_eq(a: &'static str, b: &'static str) -> bool {
- let a = a.as_bytes();
- let b = b.as_bytes();
- if a.len() != b.len() {
- return false;
- }
-
- let mut i = 0;
- while i < a.len() {
- if a[i] != b[i] {
- return false;
- }
- i += 1;
- }
-
- true
- }
+ pub use super::util::const_str_eq;
}
use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
#[doc(inline)]
pub use fromxml::Context;
+use fromxml::XmlNameMatcher;
pub use text::TextCodec;
@@ -235,6 +243,21 @@ pub trait FromXml {
attrs: rxml::AttrMap,
ctx: &Context<'_>,
) -> Result<Self::Builder, self::error::FromEventsError>;
+
+ /// Return a predicate which determines if `Self` *may* be parsed from
+ /// a given XML element.
+ ///
+ /// The returned matcher **must** match all elements from which `Self`
+ /// can be parsed, but it may also match elements from which `Self`
+ /// cannot be parsed.
+ ///
+ /// This is an optimisation utility for code locations which have to
+ /// disambiguate between many `FromXml` implementations. The provided
+ /// implementation returns a matcher which matches all elements, which is
+ /// correct, but also very inefficient.
+ fn xml_name_matcher() -> XmlNameMatcher<'static> {
+ XmlNameMatcher::Any
+ }
}
/// Trait allowing to convert XML text to a value.