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}