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}