generator-light 0.1.0

Light stackless generators based on Futures
Documentation
#![no_std]

mod fut_generator;

pub(crate) mod fn_trait;
pub(crate) mod gen_context;

pub mod ext;
pub mod generator_state;

use core::{ops::DerefMut, pin::Pin};

use crate::fn_trait::FnOnceOutput;
pub use crate::gen_context::Yielder;
pub use crate::generator_state::GeneratorState;

pub trait Generator<R = ()> {
    type Yield;
    type Return;
    fn resume(self: Pin<&mut Self>, value: R) -> GeneratorState<Self::Yield, Self::Return>;
}

impl<R, G> Generator<R> for &mut G
where
    G: Generator<R> + ?Sized + Unpin,
{
    type Return = G::Return;
    type Yield = G::Yield;
    fn resume(mut self: Pin<&mut Self>, value: R) -> GeneratorState<Self::Yield, Self::Return> {
        G::resume(Pin::new(&mut **self), value)
    }
}

impl<R, P> Generator<R> for Pin<P>
where
    P: DerefMut<Target: Generator<R>>,
{
    type Return = <P::Target as Generator<R>>::Return;
    type Yield = <P::Target as Generator<R>>::Yield;

    fn resume(self: Pin<&mut Self>, value: R) -> GeneratorState<Self::Yield, Self::Return> {
        <P::Target as Generator<R>>::resume(self.as_deref_mut(), value)
    }
}

/// To build generator, pass an async function with two parameters:
/// First parameter is Yielder -- context handle to yield generated item
/// The second parameter -- it's a Resume value passed
///     to the Generator::resume() method at the first time
pub const fn generator<F, Resume, Yield, Return>(
    f: F,
) -> impl Generator<Resume, Yield = Yield, Return = Return>
where
    // F: AsyncFnOnce(Yielder<Yield, Resume>, Resume) -> Return,
    // but this has to be expressed in such ugly form
    for<'a> F: FnOnceOutput<Yielder<'a, Yield, Resume>, Resume, Out: Future<Output = Return>>,
{
    fut_generator::Generator::new(f)
}

#[cfg(test)]
mod tests {

    use core::pin::pin;

    use crate::{ext::GeneratorIterator, *};

    fn squares(n: usize) -> impl Generator<Yield = usize, Return = ()> {
        generator(async move |mut this: Yielder<_, _>, _| {
            for x in 1..=n {
                yield_!(this, x * x);
            }
        })
    }

    fn tok_generator<'a>(s: &'a str) -> impl Generator<Yield = &'a str, Return = ()> {
        generator(async move |mut this: Yielder<_, _>, _| {
            for tok in s.split_whitespace() {
                yield_!(this, tok);
            }
        })
    }

    #[test]
    fn test_squares() {
        let g = pin!(squares(5));
        let mut g = g.into_iter();
        assert_eq!(g.next(), Some(1));
        assert_eq!(g.next(), Some(4));
        assert_eq!(g.next(), Some(9));
        assert_eq!(g.next(), Some(16));
        assert_eq!(g.next(), Some(25));
        assert_eq!(g.next(), None);
    }

    #[test]
    fn test_tokens() {
        let g = pin!(tok_generator("hello world"));
        let mut g = g.into_iter();
        assert_eq!(g.next(), Some("hello"));
        assert_eq!(g.next(), Some("world"));
        assert_eq!(g.next(), None);
    }
}