minidom: breaking: Allow getting Element attributes by namespace

- Move Element.attributes to `AttrMap`, slowly using rxml's features and
  unrolling our own.
- Add Element::attr_ns that requires the attribute namespace.
  Element:attr defaults to rxml::Namespace::none() but the interface
  changes nonetheless for a &NcNameStr. Similar changes on
  `ElementBuilder` methods.
- Remove iterator structs for attributes, return a ref on the AttrMap
  directly as we don't need to keep attributes' internals hidden
  anymore.
- Enable rxml's `macros` feature within tests to access the `xml_ncname`
  macro.

Signed-off-by: pep <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2025-09-27 16:49:42 +02:00 committed by pep
commit 19865e5f1e
14 changed files with 272 additions and 236 deletions

View file

@ -15,11 +15,7 @@ use core::marker::PhantomData;
use minidom::{Element, Node};
use rxml::{
parser::EventMetrics,
writer::{SimpleNamespaces, TrackNamespace},
AttrMap, Event, Name, NameStr, Namespace, NcName, NcNameStr,
};
use rxml::{parser::EventMetrics, AttrMap, Event, Namespace, NcName, NcNameStr};
use crate::{
error::{Error, FromEventsError},
@ -74,26 +70,8 @@ pub fn make_start_ev_parts(el: &Element) -> Result<(rxml::QName, AttrMap), Error
let namespace = Namespace::from(el.ns());
let mut attrs = AttrMap::new();
for (name, value) in el.attrs() {
let name = Name::try_from(name)?;
let (prefix, name) = name.split_name()?;
let namespace = if let Some(prefix) = prefix {
if prefix == "xml" {
Namespace::XML
} else {
let ns = match el.prefixes.get(&Some(prefix.into())) {
Some(v) => v,
None => {
panic!("undeclared xml namespace prefix in minidom::Element")
}
};
Namespace::from(ns.to_owned())
}
} else {
Namespace::NONE
};
attrs.insert(namespace, name, value.to_owned());
for ((namespace, name), value) in el.attrs() {
attrs.insert(namespace.clone(), name.clone(), value.to_owned());
}
Ok(((namespace, name), attrs))
@ -178,7 +156,7 @@ enum AsXmlState<'a> {
element: &'a Element,
/// Attribute iterator.
attributes: minidom::element::Attrs<'a>,
attributes: rxml::xml_map::Iter<'a, std::string::String>,
},
/// Content: The contents of the element are streamed as events.
@ -218,7 +196,7 @@ impl<'a> Iterator for ElementAsXml<'a> {
);
self.0 = Some(AsXmlState::Attributes {
element,
attributes: element.attrs(),
attributes: element.attrs().iter(),
});
Some(Ok(item))
}
@ -226,38 +204,9 @@ impl<'a> Iterator for ElementAsXml<'a> {
ref mut attributes,
element,
}) => {
if let Some((name, value)) = attributes.next() {
let name = match <&NameStr>::try_from(name) {
Ok(v) => v,
Err(e) => {
self.0 = None;
return Some(Err(e.into()));
}
};
let (prefix, name) = match name.split_name() {
Ok(v) => v,
Err(e) => {
self.0 = None;
return Some(Err(e.into()));
}
};
let namespace = if let Some(prefix) = prefix {
if prefix == "xml" {
Namespace::XML
} else {
let ns = match element.prefixes.get(&Some(prefix.as_str().to_owned())) {
Some(v) => v,
None => {
panic!("undeclared xml namespace prefix in minidom::Element")
}
};
Namespace::from(ns.to_owned())
}
} else {
Namespace::NONE
};
if let Some(((namespace, name), value)) = attributes.next() {
Some(Ok(Item::Attribute(
namespace,
namespace.clone(),
Cow::Borrowed(name),
Cow::Borrowed(value),
)))
@ -330,24 +279,9 @@ impl ElementFromEvents {
/// [`minidom::Element`], this is contractually infallible. Using this may
/// thus save you an `unwrap()` call.
pub fn new(qname: rxml::QName, attrs: rxml::AttrMap) -> Self {
let mut prefixes = SimpleNamespaces::new();
let mut builder = Element::builder(qname.1, qname.0);
for ((namespace, name), value) in attrs.into_iter() {
if namespace.is_none() {
builder = builder.attr(name, value);
} else {
let (is_new, prefix) = prefixes.declare_with_auto_prefix(namespace.clone());
let name = prefix.with_suffix(&name);
if is_new {
builder = builder
.prefix(
Some(prefix.as_str().to_owned()),
namespace.as_str().to_owned(),
)
.unwrap();
}
builder = builder.attr(name, value);
}
builder = builder.attr_ns(namespace, name, value);
}
let element = builder.build();