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>
 45        = OptionAsXml<T::ItemIter<'x>>
 46    where
 47        T: 'x;
 48
 49    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 50        match self {
 51            Some(ref value) => Ok(OptionAsXml(Some(T::as_xml_iter(value)?))),
 52            None => Ok(OptionAsXml(None)),
 53        }
 54    }
 55}
 56
 57/// Helper iterator to convert an `Box<T>` to XML.
 58pub struct BoxAsXml<T: Iterator>(Box<T>);
 59
 60impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for BoxAsXml<T> {
 61    type Item = Result<Item<'x>, Error>;
 62
 63    fn next(&mut self) -> Option<Self::Item> {
 64        self.0.next()
 65    }
 66}
 67
 68/// Emits the contents of `T` unchanged.
 69impl<T: AsXml> AsXml for Box<T> {
 70    type ItemIter<'x>
 71        = BoxAsXml<T::ItemIter<'x>>
 72    where
 73        T: 'x;
 74
 75    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 76        Ok(BoxAsXml(Box::new(T::as_xml_iter(&self)?)))
 77    }
 78}
 79
 80/// Emits the items of `T` if `Ok(.)` or returns the error from `E` otherwise.
 81impl<T: AsXml, E> AsXml for Result<T, E>
 82where
 83    for<'a> Error: From<&'a E>,
 84{
 85    type ItemIter<'x>
 86        = T::ItemIter<'x>
 87    where
 88        Self: 'x;
 89
 90    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 91        match self {
 92            Self::Ok(v) => Ok(v.as_xml_iter()?),
 93            Self::Err(e) => Err(e.into()),
 94        }
 95    }
 96}
 97
 98#[cfg(test)]
 99mod tests {
100    use super::*;
101
102    use std::borrow::Cow;
103
104    #[test]
105    fn option_as_xml_terminates_immediately_for_none() {
106        let mut iter = OptionAsXml::<std::iter::Empty<_>>(None);
107        match iter.next() {
108            None => (),
109            other => panic!("unexpected item: {:?}", other),
110        }
111    }
112
113    #[test]
114    fn option_as_xml_passes_values_from_inner_some() {
115        let inner = vec![
116            Ok(Item::Text(Cow::Borrowed("hello world"))),
117            Ok(Item::ElementFoot),
118        ];
119        let mut iter = OptionAsXml(Some(inner.into_iter()));
120        match iter.next() {
121            Some(Ok(Item::Text(text))) => {
122                assert_eq!(text, "hello world");
123            }
124            other => panic!("unexpected item: {:?}", other),
125        }
126        match iter.next() {
127            Some(Ok(Item::ElementFoot)) => (),
128            other => panic!("unexpected item: {:?}", other),
129        }
130        match iter.next() {
131            None => (),
132            other => panic!("unexpected item: {:?}", other),
133        }
134    }
135
136    #[test]
137    fn box_as_xml_passes_values_from_inner() {
138        let inner = vec![
139            Ok(Item::Text(Cow::Borrowed("hello world"))),
140            Ok(Item::ElementFoot),
141        ];
142        let mut iter = BoxAsXml(Box::new(inner.into_iter()));
143        match iter.next() {
144            Some(Ok(Item::Text(text))) => {
145                assert_eq!(text, "hello world");
146            }
147            other => panic!("unexpected item: {:?}", other),
148        }
149        match iter.next() {
150            Some(Ok(Item::ElementFoot)) => (),
151            other => panic!("unexpected item: {:?}", other),
152        }
153        match iter.next() {
154            None => (),
155            other => panic!("unexpected item: {:?}", other),
156        }
157    }
158}