From d12b6c31ea30ea756a5ffd19826d34781bb38696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Tue, 9 Jul 2024 16:46:11 +0200 Subject: [PATCH] xso: implement AsXml for minidom::Element --- xso/src/minidom_compat.rs | 153 +++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 2 deletions(-) diff --git a/xso/src/minidom_compat.rs b/xso/src/minidom_compat.rs index 6f32758cdc031781b8f5958e7c9c004d3fe516d2..f27a98289eb28282de46b3db0ac6f0ff3175a8b3 100644 --- a/xso/src/minidom_compat.rs +++ b/xso/src/minidom_compat.rs @@ -5,6 +5,7 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use std::borrow::Cow; use std::marker::PhantomData; use std::vec::IntoIter; @@ -13,12 +14,13 @@ use minidom::{Element, Node}; use rxml::{ parser::EventMetrics, writer::{SimpleNamespaces, TrackNamespace}, - AttrMap, Event, Name, Namespace, NcName, + AttrMap, Event, Name, NameStr, Namespace, NcName, NcNameStr, }; use crate::{ error::{Error, FromEventsError}, - FromEventsBuilder, FromXml, IntoXml, + rxml_util::Item, + AsXml, FromEventsBuilder, FromXml, IntoXml, }; /// State machine for converting a minidom Element into rxml events. @@ -152,6 +154,153 @@ impl IntoXml for Element { } } +enum AsXmlState<'a> { + /// Element header: we need to generate the + /// [`Item::ElementHeadStart`] item from the namespace and name. + Header { element: &'a Element }, + + /// Element header: we now generate the attributes. + Attributes { + /// The element (needed for the contents later and to access the + /// prefix mapping). + element: &'a Element, + + /// Attribute iterator. + attributes: minidom::element::Attrs<'a>, + }, + + /// Content: The contents of the element are streamed as events. + Nodes { + /// Remaining child nodes (text and/or children) to emit. + nodes: minidom::element::Nodes<'a>, + + /// When emitting a child element, this is a nested [`IntoEvents`] + /// instance for that child element. + nested: Option>>, + }, +} + +/// Convert a [`minidom::Element`] to [`Item`][`crate::rxml_util::Item`]s. +/// +/// This can be constructed from the +/// [`AsXml::as_xml_iter`][`crate::AsXml::as_xml_iter`] +/// implementation on [`minidom::Element`]. +pub struct ElementAsXml<'a>(Option>); + +impl<'a> Iterator for ElementAsXml<'a> { + type Item = Result, Error>; + + fn next(&mut self) -> Option { + match self.0 { + None => None, + Some(AsXmlState::Header { ref element }) => { + let item = Item::ElementHeadStart( + Namespace::from(element.ns()), + Cow::Borrowed(match <&NcNameStr>::try_from(element.name()) { + Ok(v) => v, + Err(e) => { + self.0 = None; + return Some(Err(e.into())); + } + }), + ); + self.0 = Some(AsXmlState::Attributes { + element, + attributes: element.attrs(), + }); + Some(Ok(item)) + } + Some(AsXmlState::Attributes { + ref mut attributes, + ref 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 + }; + Some(Ok(Item::Attribute( + namespace, + Cow::Borrowed(name), + Cow::Borrowed(value), + ))) + } else { + self.0 = Some(AsXmlState::Nodes { + nodes: element.nodes(), + nested: None, + }); + Some(Ok(Item::ElementHeadEnd)) + } + } + Some(AsXmlState::Nodes { + ref mut nodes, + ref mut nested, + }) => { + if let Some(nested) = nested.as_mut() { + if let Some(next) = nested.next() { + return Some(next); + } + } + *nested = None; + match nodes.next() { + None => { + self.0 = None; + Some(Ok(Item::ElementFoot)) + } + Some(minidom::Node::Text(ref text)) => { + Some(Ok(Item::Text(Cow::Borrowed(text)))) + } + Some(minidom::Node::Element(ref element)) => { + let mut iter = match element.as_xml_iter() { + Ok(v) => v, + Err(e) => { + self.0 = None; + return Some(Err(e.into())); + } + }; + let item = iter.next().unwrap(); + *nested = Some(Box::new(iter)); + Some(item) + } + } + } + } + } +} + +impl AsXml for minidom::Element { + type ItemIter<'a> = ElementAsXml<'a>; + + fn as_xml_iter(&self) -> Result, Error> { + Ok(ElementAsXml(Some(AsXmlState::Header { element: self }))) + } +} + /// Construct a [`minidom::Element`] from [`rxml::Event`]s /// /// This can be constructed from the