xso: provide adapter for AsXml implementation based on Into<Element>
Analogous to the already existing `IntoXml` implementation helpers based on an `Into<Element>` implementation on a type, this provides the utilities for `AsXml`. This is of course exceptionally inefficient, but it is only needed transitionally until we have everything migrated to derive macros or otherwise rewritten in terms of AsXml/FromXml.
This commit is contained in:
parent
d12b6c31ea
commit
569b6e327d
2 changed files with 247 additions and 1 deletions
|
@ -19,7 +19,7 @@ use rxml::{
|
|||
|
||||
use crate::{
|
||||
error::{Error, FromEventsError},
|
||||
rxml_util::Item,
|
||||
rxml_util::{EventToItem, Item},
|
||||
AsXml, FromEventsBuilder, FromXml, IntoXml,
|
||||
};
|
||||
|
||||
|
@ -453,6 +453,35 @@ impl Iterator for IntoEventsViaElement {
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper struct to stream a struct which implements conversion
|
||||
/// to [`minidom::Element`].
|
||||
pub struct AsItemsViaElement<'x> {
|
||||
iter: EventToItem<IntoEventsViaElement>,
|
||||
lifetime_binding: PhantomData<Item<'x>>,
|
||||
}
|
||||
|
||||
impl<'x> AsItemsViaElement<'x> {
|
||||
/// Create a new streaming parser for `T`.
|
||||
pub fn new<E, T>(value: T) -> Result<Self, crate::error::Error>
|
||||
where
|
||||
Error: From<E>,
|
||||
minidom::Element: TryFrom<T, Error = E>,
|
||||
{
|
||||
Ok(Self {
|
||||
iter: EventToItem::new(IntoEventsViaElement::new(value)?),
|
||||
lifetime_binding: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'x> Iterator for AsItemsViaElement<'x> {
|
||||
type Item = Result<Item<'x>, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|x| x.map(Item::into_owned))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use rxml::{Namespace, NcNameStr, XmlVersion};
|
||||
#[cfg(feature = "minidom")]
|
||||
use rxml::Event;
|
||||
|
||||
/// An encodable item.
|
||||
///
|
||||
|
@ -90,3 +92,218 @@ impl Item<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator adapter which converts an iterator over [`Event`][`rxml::Event`]
|
||||
/// to an iterator over [`Item<'static>`][`Item`].
|
||||
///
|
||||
/// This iterator consumes the events and returns items which contain the data
|
||||
/// in an owned fashion.
|
||||
#[cfg(feature = "minidom")]
|
||||
pub(crate) struct EventToItem<I> {
|
||||
inner: I,
|
||||
attributes: Option<rxml::xml_map::IntoIter<String>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "minidom")]
|
||||
impl<I> EventToItem<I> {
|
||||
pub(crate) fn new(inner: I) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
attributes: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn drain(&mut self) -> Option<Item<'static>> {
|
||||
match self.attributes {
|
||||
Some(ref mut attrs) => {
|
||||
if let Some(((ns, name), value)) = attrs.next() {
|
||||
Some(Item::Attribute(ns, Cow::Owned(name), Cow::Owned(value)))
|
||||
} else {
|
||||
self.attributes = None;
|
||||
Some(Item::ElementHeadEnd)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ev: Event) -> Item<'static> {
|
||||
assert!(self.attributes.is_none());
|
||||
match ev {
|
||||
Event::XmlDeclaration(_, v) => Item::XmlDeclaration(v),
|
||||
Event::StartElement(_, (ns, name), attrs) => {
|
||||
self.attributes = Some(attrs.into_iter());
|
||||
Item::ElementHeadStart(ns, Cow::Owned(name))
|
||||
}
|
||||
Event::Text(_, value) => Item::Text(Cow::Owned(value)),
|
||||
Event::EndElement(_) => Item::ElementFoot,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "minidom")]
|
||||
impl<I: Iterator<Item = Result<Event, crate::error::Error>>> Iterator for EventToItem<I> {
|
||||
type Item = Result<Item<'static>, crate::error::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(item) = self.drain() {
|
||||
return Some(Ok(item));
|
||||
}
|
||||
let next = match self.inner.next() {
|
||||
Some(Ok(v)) => v,
|
||||
Some(Err(e)) => return Some(Err(e)),
|
||||
None => return None,
|
||||
};
|
||||
Some(Ok(self.update(next)))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// we may create an indefinte amount of items for a single event,
|
||||
// so we cannot provide a reasonable upper bound.
|
||||
(self.inner.size_hint().0, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "minidom"))]
|
||||
mod tests_minidom {
|
||||
use std::convert::TryInto;
|
||||
|
||||
use rxml::{parser::EventMetrics, AttrMap};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn events_to_items<I: Iterator<Item = Event>>(events: I) -> Vec<Item<'static>> {
|
||||
let iter = EventToItem {
|
||||
inner: events.map(|ev| Ok(ev)),
|
||||
attributes: None,
|
||||
};
|
||||
let mut result = Vec::new();
|
||||
for item in iter {
|
||||
let item = item.unwrap();
|
||||
result.push(item);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_to_item_xml_declaration() {
|
||||
let events = vec![Event::XmlDeclaration(
|
||||
EventMetrics::zero(),
|
||||
XmlVersion::V1_0,
|
||||
)];
|
||||
let items = events_to_items(events.into_iter());
|
||||
assert_eq!(items.len(), 1);
|
||||
match items[0] {
|
||||
Item::XmlDeclaration(XmlVersion::V1_0) => (),
|
||||
ref other => panic!("unexected item in position 0: {:?}", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_to_item_empty_element() {
|
||||
let events = vec![
|
||||
Event::StartElement(
|
||||
EventMetrics::zero(),
|
||||
(Namespace::NONE, "elem".try_into().unwrap()),
|
||||
AttrMap::new(),
|
||||
),
|
||||
Event::EndElement(EventMetrics::zero()),
|
||||
];
|
||||
let items = events_to_items(events.into_iter());
|
||||
assert_eq!(items.len(), 3);
|
||||
match items[0] {
|
||||
Item::ElementHeadStart(ref ns, ref name) => {
|
||||
assert_eq!(&**ns, Namespace::none());
|
||||
assert_eq!(&**name, "elem");
|
||||
}
|
||||
ref other => panic!("unexected item in position 0: {:?}", other),
|
||||
};
|
||||
match items[1] {
|
||||
Item::ElementHeadEnd => (),
|
||||
ref other => panic!("unexected item in position 1: {:?}", other),
|
||||
};
|
||||
match items[2] {
|
||||
Item::ElementFoot => (),
|
||||
ref other => panic!("unexected item in position 2: {:?}", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_to_item_element_with_attributes() {
|
||||
let mut attrs = AttrMap::new();
|
||||
attrs.insert(
|
||||
Namespace::NONE,
|
||||
"attr".try_into().unwrap(),
|
||||
"value".to_string(),
|
||||
);
|
||||
let events = vec![
|
||||
Event::StartElement(
|
||||
EventMetrics::zero(),
|
||||
(Namespace::NONE, "elem".try_into().unwrap()),
|
||||
attrs,
|
||||
),
|
||||
Event::EndElement(EventMetrics::zero()),
|
||||
];
|
||||
let items = events_to_items(events.into_iter());
|
||||
assert_eq!(items.len(), 4);
|
||||
match items[0] {
|
||||
Item::ElementHeadStart(ref ns, ref name) => {
|
||||
assert_eq!(&**ns, Namespace::none());
|
||||
assert_eq!(&**name, "elem");
|
||||
}
|
||||
ref other => panic!("unexected item in position 0: {:?}", other),
|
||||
};
|
||||
match items[1] {
|
||||
Item::Attribute(ref ns, ref name, ref value) => {
|
||||
assert_eq!(&**ns, Namespace::none());
|
||||
assert_eq!(&**name, "attr");
|
||||
assert_eq!(&**value, "value");
|
||||
}
|
||||
ref other => panic!("unexected item in position 1: {:?}", other),
|
||||
};
|
||||
match items[2] {
|
||||
Item::ElementHeadEnd => (),
|
||||
ref other => panic!("unexected item in position 2: {:?}", other),
|
||||
};
|
||||
match items[3] {
|
||||
Item::ElementFoot => (),
|
||||
ref other => panic!("unexected item in position 3: {:?}", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_to_item_element_with_text() {
|
||||
let events = vec![
|
||||
Event::StartElement(
|
||||
EventMetrics::zero(),
|
||||
(Namespace::NONE, "elem".try_into().unwrap()),
|
||||
AttrMap::new(),
|
||||
),
|
||||
Event::Text(EventMetrics::zero(), "Hello World!".to_owned()),
|
||||
Event::EndElement(EventMetrics::zero()),
|
||||
];
|
||||
let items = events_to_items(events.into_iter());
|
||||
assert_eq!(items.len(), 4);
|
||||
match items[0] {
|
||||
Item::ElementHeadStart(ref ns, ref name) => {
|
||||
assert_eq!(&**ns, Namespace::none());
|
||||
assert_eq!(&**name, "elem");
|
||||
}
|
||||
ref other => panic!("unexected item in position 0: {:?}", other),
|
||||
};
|
||||
match items[1] {
|
||||
Item::ElementHeadEnd => (),
|
||||
ref other => panic!("unexected item in position 1: {:?}", other),
|
||||
};
|
||||
match items[2] {
|
||||
Item::Text(ref value) => {
|
||||
assert_eq!(value, "Hello World!");
|
||||
}
|
||||
ref other => panic!("unexected item in position 2: {:?}", other),
|
||||
};
|
||||
match items[3] {
|
||||
Item::ElementFoot => (),
|
||||
ref other => panic!("unexected item in position 3: {:?}", other),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue