xso: implement AsXml for minidom::Element
This commit is contained in:
parent
d29b89d307
commit
d12b6c31ea
1 changed files with 151 additions and 2 deletions
|
|
@ -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<Box<ElementAsXml<'a>>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// 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<AsXmlState<'a>>);
|
||||
|
||||
impl<'a> Iterator for ElementAsXml<'a> {
|
||||
type Item = Result<Item<'a>, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<Self::ItemIter<'_>, Error> {
|
||||
Ok(ElementAsXml(Some(AsXmlState::Header { element: self })))
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a [`minidom::Element`] from [`rxml::Event`]s
|
||||
///
|
||||
/// This can be constructed from the
|
||||
|
|
|
|||
Loading…
Reference in a new issue