@@ -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),
+ };
+ }
+}