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 alloc::boxed::Box;
 16
 17use crate::error::Error;
 18use crate::rxml_util::Item;
 19use crate::AsXml;
 20
 21/// Helper iterator to convert an `Option<T>` to XML.
 22pub struct OptionAsXml<T: Iterator>(Option<T>);
 23
 24impl<T: Iterator> OptionAsXml<T> {
 25    /// Construct a new iterator, wrapping the given iterator.
 26    ///
 27    /// If `inner` is `None`, this iterator terminates immediately. Otherwise,
 28    /// it yields the elements yielded by `inner` until `inner` finishes,
 29    /// after which this iterator completes, too.
 30    pub fn new(inner: Option<T>) -> Self {
 31        Self(inner)
 32    }
 33}
 34
 35impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for OptionAsXml<T> {
 36    type Item = Result<Item<'x>, Error>;
 37
 38    fn next(&mut self) -> Option<Self::Item> {
 39        self.0.as_mut()?.next()
 40    }
 41}
 42
 43/// Emits the contents of `Some(.)` unchanged if present and nothing
 44/// otherwise.
 45impl<T: AsXml> AsXml for Option<T> {
 46    type ItemIter<'x>
 47        = OptionAsXml<T::ItemIter<'x>>
 48    where
 49        T: 'x;
 50
 51    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 52        match self {
 53            Some(ref value) => Ok(OptionAsXml(Some(T::as_xml_iter(value)?))),
 54            None => Ok(OptionAsXml(None)),
 55        }
 56    }
 57}
 58
 59/// Helper iterator to convert an `Box<T>` to XML.
 60pub struct BoxAsXml<T: Iterator>(Box<T>);
 61
 62impl<'x, T: Iterator<Item = Result<Item<'x>, Error>>> Iterator for BoxAsXml<T> {
 63    type Item = Result<Item<'x>, Error>;
 64
 65    fn next(&mut self) -> Option<Self::Item> {
 66        self.0.next()
 67    }
 68}
 69
 70/// Emits the contents of `T` unchanged.
 71impl<T: AsXml> AsXml for Box<T> {
 72    type ItemIter<'x>
 73        = BoxAsXml<T::ItemIter<'x>>
 74    where
 75        T: 'x;
 76
 77    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 78        Ok(BoxAsXml(Box::new(T::as_xml_iter(&self)?)))
 79    }
 80}
 81
 82/// Emits the items of `T` if `Ok(.)` or returns the error from `E` otherwise.
 83impl<T: AsXml, E> AsXml for Result<T, E>
 84where
 85    for<'a> Error: From<&'a E>,
 86{
 87    type ItemIter<'x>
 88        = T::ItemIter<'x>
 89    where
 90        Self: 'x;
 91
 92    fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
 93        match self {
 94            Self::Ok(v) => Ok(v.as_xml_iter()?),
 95            Self::Err(e) => Err(e.into()),
 96        }
 97    }
 98}
 99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    use alloc::{borrow::Cow, vec};
105
106    #[test]
107    fn option_as_xml_terminates_immediately_for_none() {
108        let mut iter = OptionAsXml::<core::iter::Empty<_>>(None);
109        match iter.next() {
110            None => (),
111            other => panic!("unexpected item: {:?}", other),
112        }
113    }
114
115    #[test]
116    fn option_as_xml_passes_values_from_inner_some() {
117        let inner = vec![
118            Ok(Item::Text(Cow::Borrowed("hello world"))),
119            Ok(Item::ElementFoot),
120        ];
121        let mut iter = OptionAsXml(Some(inner.into_iter()));
122        match iter.next() {
123            Some(Ok(Item::Text(text))) => {
124                assert_eq!(text, "hello world");
125            }
126            other => panic!("unexpected item: {:?}", other),
127        }
128        match iter.next() {
129            Some(Ok(Item::ElementFoot)) => (),
130            other => panic!("unexpected item: {:?}", other),
131        }
132        match iter.next() {
133            None => (),
134            other => panic!("unexpected item: {:?}", other),
135        }
136    }
137
138    #[test]
139    fn box_as_xml_passes_values_from_inner() {
140        let inner = vec![
141            Ok(Item::Text(Cow::Borrowed("hello world"))),
142            Ok(Item::ElementFoot),
143        ];
144        let mut iter = BoxAsXml(Box::new(inner.into_iter()));
145        match iter.next() {
146            Some(Ok(Item::Text(text))) => {
147                assert_eq!(text, "hello world");
148            }
149            other => panic!("unexpected item: {:?}", other),
150        }
151        match iter.next() {
152            Some(Ok(Item::ElementFoot)) => (),
153            other => panic!("unexpected item: {:?}", other),
154        }
155        match iter.next() {
156            None => (),
157            other => panic!("unexpected item: {:?}", other),
158        }
159    }
160}