1#![allow(clippy::type_complexity)]
11
12use crate::error::StageError;
13
14#[cfg(feature = "std")]
15use alloc::string::String;
16use core::marker::PhantomData;
17
18pub trait Source {
20 type Item;
22 type Error: StageError;
24
25 fn pull(&mut self) -> Result<Option<Self::Item>, Self::Error>;
34
35 fn close(&mut self) -> Result<(), Self::Error> {
41 Ok(())
42 }
43}
44
45pub struct IterSource<I: Iterator> {
59 iter: I,
60}
61
62impl<I> IterSource<I>
63where
64 I: Iterator,
65{
66 pub fn new<II>(into_iter: II) -> Self
68 where
69 II: IntoIterator<IntoIter = I>,
70 {
71 Self {
72 iter: into_iter.into_iter(),
73 }
74 }
75}
76
77#[derive(Debug)]
79pub enum Infallible {}
80
81impl core::fmt::Display for Infallible {
82 fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83 match *self {}
84 }
85}
86
87impl<I> Source for IterSource<I>
88where
89 I: Iterator,
90{
91 type Item = I::Item;
92 type Error = Infallible;
93
94 fn pull(&mut self) -> Result<Option<Self::Item>, Self::Error> {
95 Ok(self.iter.next())
96 }
97}
98
99pub struct FnSource<F, T, E> {
121 func: F,
122 _marker: PhantomData<fn() -> Result<Option<T>, E>>,
123}
124
125impl<F, T, E> FnSource<F, T, E>
126where
127 F: FnMut() -> Result<Option<T>, E>,
128{
129 pub fn new(func: F) -> Self {
131 Self {
132 func,
133 _marker: PhantomData,
134 }
135 }
136}
137
138impl<F, T, E> Source for FnSource<F, T, E>
139where
140 F: FnMut() -> Result<Option<T>, E>,
141 E: StageError,
142{
143 type Item = T;
144 type Error = E;
145
146 fn pull(&mut self) -> Result<Option<Self::Item>, Self::Error> {
147 (self.func)()
148 }
149}
150
151#[cfg(feature = "std")]
156#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
157pub struct ChannelSource<T> {
158 rx: std::sync::mpsc::Receiver<T>,
159}
160
161#[cfg(feature = "std")]
162impl<T> ChannelSource<T> {
163 #[must_use]
165 pub fn new(rx: std::sync::mpsc::Receiver<T>) -> Self {
166 Self { rx }
167 }
168}
169
170#[cfg(feature = "std")]
171impl<T> Source for ChannelSource<T>
172where
173 T: 'static,
174{
175 type Item = T;
176 type Error = Infallible;
177
178 fn pull(&mut self) -> Result<Option<Self::Item>, Self::Error> {
179 match self.rx.recv() {
180 Ok(item) => Ok(Some(item)),
181 Err(_) => Ok(None),
182 }
183 }
184}
185
186#[cfg(feature = "std")]
189#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
190pub struct ReaderSource<R: std::io::Read> {
191 reader: std::io::BufReader<R>,
192 buf: String,
193}
194
195#[cfg(feature = "std")]
196impl<R: std::io::Read> ReaderSource<R> {
197 pub fn new(reader: R) -> Self {
199 Self {
200 reader: std::io::BufReader::new(reader),
201 buf: String::new(),
202 }
203 }
204
205 pub fn with_capacity(capacity: usize, reader: R) -> Self {
207 Self {
208 reader: std::io::BufReader::with_capacity(capacity, reader),
209 buf: String::new(),
210 }
211 }
212}
213
214#[cfg(feature = "std")]
215impl<R: std::io::Read> Source for ReaderSource<R> {
216 type Item = String;
217 type Error = std::io::Error;
218
219 fn pull(&mut self) -> Result<Option<Self::Item>, Self::Error> {
220 use std::io::BufRead;
221 self.buf.clear();
222 let read = self.reader.read_line(&mut self.buf)?;
223 if read == 0 {
224 return Ok(None);
225 }
226 if self.buf.ends_with('\n') {
227 self.buf.pop();
228 if self.buf.ends_with('\r') {
229 self.buf.pop();
230 }
231 }
232 Ok(Some(core::mem::take(&mut self.buf)))
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use alloc::vec;
240 use alloc::vec::Vec;
241
242 #[test]
243 fn iter_source_exhausts() {
244 let mut s = IterSource::new(vec![10, 20, 30]);
245 let mut out = Vec::new();
246 while let Some(v) = s.pull().unwrap() {
247 out.push(v);
248 }
249 assert_eq!(out, vec![10, 20, 30]);
250 }
251
252 #[test]
253 fn fn_source_can_fail() {
254 let mut state = 0;
255 let mut s = FnSource::new(move || -> Result<Option<u32>, &'static str> {
256 state += 1;
257 if state == 2 {
258 Err("nope")
259 } else {
260 Ok(Some(state))
261 }
262 });
263 assert_eq!(s.pull().unwrap(), Some(1));
264 assert!(s.pull().is_err());
265 }
266
267 #[cfg(feature = "std")]
268 #[test]
269 fn reader_source_splits_lines() {
270 use std::io::Cursor;
271 let data = "alpha\nbeta\r\ngamma";
272 let mut s = ReaderSource::new(Cursor::new(data));
273 assert_eq!(s.pull().unwrap().as_deref(), Some("alpha"));
274 assert_eq!(s.pull().unwrap().as_deref(), Some("beta"));
275 assert_eq!(s.pull().unwrap().as_deref(), Some("gamma"));
276 assert_eq!(s.pull().unwrap(), None);
277 }
278
279 #[cfg(feature = "std")]
280 #[test]
281 fn channel_source_terminates_when_sender_dropped() {
282 let (tx, rx) = std::sync::mpsc::channel::<i32>();
283 let mut s = ChannelSource::new(rx);
284 tx.send(1).unwrap();
285 tx.send(2).unwrap();
286 drop(tx);
287 assert_eq!(s.pull().unwrap(), Some(1));
288 assert_eq!(s.pull().unwrap(), Some(2));
289 assert_eq!(s.pull().unwrap(), None);
290 }
291}