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
|
// 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
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::vec::IntoIter;
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
|
|
@ -13,12 +14,13 @@ use minidom::{Element, Node};
|
||||||
use rxml::{
|
use rxml::{
|
||||||
parser::EventMetrics,
|
parser::EventMetrics,
|
||||||
writer::{SimpleNamespaces, TrackNamespace},
|
writer::{SimpleNamespaces, TrackNamespace},
|
||||||
AttrMap, Event, Name, Namespace, NcName,
|
AttrMap, Event, Name, NameStr, Namespace, NcName, NcNameStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{Error, FromEventsError},
|
error::{Error, FromEventsError},
|
||||||
FromEventsBuilder, FromXml, IntoXml,
|
rxml_util::Item,
|
||||||
|
AsXml, FromEventsBuilder, FromXml, IntoXml,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// State machine for converting a minidom Element into rxml events.
|
/// 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
|
/// Construct a [`minidom::Element`] from [`rxml::Event`]s
|
||||||
///
|
///
|
||||||
/// This can be constructed from the
|
/// This can be constructed from the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue