Skip to main content

generator_light/
lib.rs

1#![no_std]
2
3mod fut_generator;
4
5pub(crate) mod fn_trait;
6pub(crate) mod gen_context;
7
8pub mod ext;
9pub mod generator_state;
10
11use core::{ops::DerefMut, pin::Pin};
12
13use crate::fn_trait::FnOnceOutput;
14pub use crate::gen_context::Yielder;
15pub use crate::generator_state::GeneratorState;
16
17pub trait Generator<R = ()> {
18    type Yield;
19    type Return;
20    fn resume(self: Pin<&mut Self>, value: R) -> GeneratorState<Self::Yield, Self::Return>;
21}
22
23impl<R, G> Generator<R> for &mut G
24where
25    G: Generator<R> + ?Sized + Unpin,
26{
27    type Return = G::Return;
28    type Yield = G::Yield;
29    fn resume(mut self: Pin<&mut Self>, value: R) -> GeneratorState<Self::Yield, Self::Return> {
30        G::resume(Pin::new(&mut **self), value)
31    }
32}
33
34impl<R, P> Generator<R> for Pin<P>
35where
36    P: DerefMut<Target: Generator<R>>,
37{
38    type Return = <P::Target as Generator<R>>::Return;
39    type Yield = <P::Target as Generator<R>>::Yield;
40
41    fn resume(self: Pin<&mut Self>, value: R) -> GeneratorState<Self::Yield, Self::Return> {
42        <P::Target as Generator<R>>::resume(self.as_deref_mut(), value)
43    }
44}
45
46/// To build generator, pass an async function with two parameters:
47/// First parameter is Yielder -- context handle to yield generated item
48/// The second parameter -- it's a Resume value passed
49///     to the Generator::resume() method at the first time
50pub const fn generator<F, Resume, Yield, Return>(
51    f: F,
52) -> impl Generator<Resume, Yield = Yield, Return = Return>
53where
54    // F: AsyncFnOnce(Yielder<Yield, Resume>, Resume) -> Return,
55    // but this has to be expressed in such ugly form
56    for<'a> F: FnOnceOutput<Yielder<'a, Yield, Resume>, Resume, Out: Future<Output = Return>>,
57{
58    fut_generator::Generator::new(f)
59}
60
61#[cfg(test)]
62mod tests {
63
64    use core::pin::pin;
65
66    use crate::{ext::GeneratorIterator, *};
67
68    fn squares(n: usize) -> impl Generator<Yield = usize, Return = ()> {
69        generator(async move |mut this: Yielder<_, _>, _| {
70            for x in 1..=n {
71                yield_!(this, x * x);
72            }
73        })
74    }
75
76    fn tok_generator<'a>(s: &'a str) -> impl Generator<Yield = &'a str, Return = ()> {
77        generator(async move |mut this: Yielder<_, _>, _| {
78            for tok in s.split_whitespace() {
79                yield_!(this, tok);
80            }
81        })
82    }
83
84    #[test]
85    fn test_squares() {
86        let g = pin!(squares(5));
87        let mut g = g.into_iter();
88        assert_eq!(g.next(), Some(1));
89        assert_eq!(g.next(), Some(4));
90        assert_eq!(g.next(), Some(9));
91        assert_eq!(g.next(), Some(16));
92        assert_eq!(g.next(), Some(25));
93        assert_eq!(g.next(), None);
94    }
95
96    #[test]
97    fn test_tokens() {
98        let g = pin!(tok_generator("hello world"));
99        let mut g = g.into_iter();
100        assert_eq!(g.next(), Some("hello"));
101        assert_eq!(g.next(), Some("world"));
102        assert_eq!(g.next(), None);
103    }
104}