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}