asxml.rs

  1//! # Generic iterator type implementations
  2//!
  3//! This module contains [`AsXml`] iterator implementations for types from
  4//! foreign libraries (such as the standard library).
  5//!
  6//! In order to not clutter the `xso` crate's main namespace, they are
  7//! stashed away in a separate module.
  8
  9// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
 10//
 11// This Source Code Form is subject to the terms of the Mozilla Public
 12// License, v. 2.0. If a copy of the MPL was not distributed with this
 13// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 14
 15use crate::error::Error;
 16use crate::rxml_util::Item;
 17use crate::AsXml;
 18
 19/// Helper iterator to convert an `Option<T>` to XML.
 20pub struct OptionAsXml<T: Iterator>(Option<T>);
 21
 22impl<T: Iterator> OptionAsXml<T> {
 23    /// Construct a new iterator, wrapping the given iterator.
 24    ///
 25    /// If `inner` is `None`, this iterator terminates immediately. Otherwise,
 26    /// it yields the elements yielded by `inner` until `inner` finishes,
 27    /// after which this iterator completes, too.
 28    pub fn new(inner: Option<T>) -> Self {
 29        Self(inner)
 30    }
 31}
 32
 33impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for OptionAsXml<T> {
 34    type Item = Result<Item<'x>, Error>;
 35
 36    fn next(&mut self) -> Option<Self::Item> {
 37        self.0.as_mut()?.next()
 38    }
 39}
 40
 41/// Emits the contents of `Some(.)` unchanged if present and nothing
 42/// otherwise.
 43impl<T: AsXml> AsXml for Option<T> {
 44    type ItemIter<'x> = OptionAsXml<T::ItemIter<'x>> where T: 'x;
 45
 46    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 47        match self {
 48            Some(ref value) => Ok(OptionAsXml(Some(T::as_xml_iter(value)?))),
 49            None => Ok(OptionAsXml(None)),
 50        }
 51    }
 52}
 53
 54/// Helper iterator to convert an `Box<T>` to XML.
 55pub struct BoxAsXml<T: Iterator>(Box<T>);
 56
 57impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for BoxAsXml<T> {
 58    type Item = Result<Item<'x>, Error>;
 59
 60    fn next(&mut self) -> Option<Self::Item> {
 61        self.0.next()
 62    }
 63}
 64
 65/// Emits the contents of `T` unchanged.
 66impl<T: AsXml> AsXml for Box<T> {
 67    type ItemIter<'x> = BoxAsXml<T::ItemIter<'x>> where T: 'x;
 68
 69    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 70        Ok(BoxAsXml(Box::new(T::as_xml_iter(&self)?)))
 71    }
 72}
 73
 74/// Emits the items of `T` if `Ok(.)` or returns the error from `E` otherwise.
 75impl<T: AsXml, E> AsXml for Result<T, E>
 76where
 77    for<'a> Error: From<&'a E>,
 78{
 79    type ItemIter<'x> = T::ItemIter<'x> where Self: 'x;
 80
 81    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 82        match self {
 83            Self::Ok(v) => Ok(v.as_xml_iter()?),
 84            Self::Err(e) => Err(e.into()),
 85        }
 86    }
 87}
 88
 89#[cfg(test)]
 90mod tests {
 91    use super::*;
 92
 93    use std::borrow::Cow;
 94
 95    #[test]
 96    fn option_as_xml_terminates_immediately_for_none() {
 97        let mut iter = OptionAsXml::<std::iter::Empty<_>>(None);
 98        match iter.next() {
 99            None => (),
100            other => panic!("unexpected item: {:?}", other),
101        }
102    }
103
104    #[test]
105    fn option_as_xml_passes_values_from_inner_some() {
106        let inner = vec![
107            Ok(Item::Text(Cow::Borrowed("hello world"))),
108            Ok(Item::ElementFoot),
109        ];
110        let mut iter = OptionAsXml(Some(inner.into_iter()));
111        match iter.next() {
112            Some(Ok(Item::Text(text))) => {
113                assert_eq!(text, "hello world");
114            }
115            other => panic!("unexpected item: {:?}", other),
116        }
117        match iter.next() {
118            Some(Ok(Item::ElementFoot)) => (),
119            other => panic!("unexpected item: {:?}", other),
120        }
121        match iter.next() {
122            None => (),
123            other => panic!("unexpected item: {:?}", other),
124        }
125    }
126
127    #[test]
128    fn box_as_xml_passes_values_from_inner() {
129        let inner = vec![
130            Ok(Item::Text(Cow::Borrowed("hello world"))),
131            Ok(Item::ElementFoot),
132        ];
133        let mut iter = BoxAsXml(Box::new(inner.into_iter()));
134        match iter.next() {
135            Some(Ok(Item::Text(text))) => {
136                assert_eq!(text, "hello world");
137            }
138            other => panic!("unexpected item: {:?}", other),
139        }
140        match iter.next() {
141            Some(Ok(Item::ElementFoot)) => (),
142            other => panic!("unexpected item: {:?}", other),
143        }
144        match iter.next() {
145            None => (),
146            other => panic!("unexpected item: {:?}", other),
147        }
148    }
149}