dewit 0.0.1

Define scheduling and execution of code separately from data flow
Documentation
use core::marker::PhantomData;

use crate::{
    mode::Mode,
    task::{
        Task,
        TaskOutput,
    },
};

struct MapResult<A, B, E, IO> {
    prev: A,
    next: B,
    _phantom: PhantomData<(E, IO)>,
}

impl<'src, AI, E, BI, BO, A, B> Task<'src, AI, Result<BO, E>> for MapResult<A, B, E, BI>
where
    E: Send + 'src,
    BI: Send + 'src,
    BO: Send + 'src,
    A: Task<'src, AI, Result<BI, E>>,
    B: Task<'src, BI, BO> + Send + 'src,
{
    go_impl!('src, AI, Result<BO, E>);

    #[inline]
    fn go<M>(self, mode: &'src M, input: AI) -> TaskOutput<'src, M, Result<BO, E>>
    where
        Self: Sized,
        M: Mode<'src> + Send + Sync,
    {
        mode.map_result(self.prev.go(mode, input), move |mode, output| {
            self.next.go(mode, output)
        })
    }
}

/// Combine two tasks by feeding the output of one into the next, mapping a
/// [`Result`] to another and passing errors through. Analogous to
/// [`Result::map`](core::result::Result::map).
///
/// For ergonomics, [`TaskResultExt::map`](crate::task::TaskResultExt::map)
/// should be preferred, but until `const` trait functions are stabilized this
/// function permits _constructing_ a [`Task`] to be evaluated at compile time.
#[inline(always)]
pub const fn map_result<'src, I, E, BI, BO>(
    a: impl Task<'src, I, Result<BI, E>>,
    b: impl Task<'src, BI, BO> + Send + 'src,
) -> impl Task<'src, I, Result<BO, E>>
where
    E: Send + 'src,
    BI: Send + 'src,
    BO: Send + 'src,
{
    MapResult {
        prev: a,
        next: b,
        _phantom: PhantomData,
    }
}

struct MapOption<A, B, IO> {
    prev: A,
    next: B,
    _phantom: PhantomData<IO>,
}

impl<'src, AI, BI, BO, A, B> Task<'src, AI, Option<BO>> for MapOption<A, B, BI>
where
    BI: Send + 'src,
    BO: Send + 'src,
    A: Task<'src, AI, Option<BI>>,
    B: Task<'src, BI, BO> + Send + 'src,
{
    go_impl!('src, AI, Option<BO>);

    #[inline]
    fn go<M>(self, mode: &'src M, input: AI) -> TaskOutput<'src, M, Option<BO>>
    where
        Self: Sized,
        M: Mode<'src> + Send + Sync,
    {
        mode.map_option(self.prev.go(mode, input), move |mode, output| {
            self.next.go(mode, output)
        })
    }
}

/// Combine two tasks by feeding the output of one into the next, mapping a
/// [`Option`] to another and passing [`None`] through. Analogous to
/// [`Option::map`](core::option::Option::map).
///
/// For ergonomics, [`TaskOptionExt::map`](crate::task::TaskOptionExt::map)
/// should be preferred, but until `const` trait functions are stabilized this
/// function permits _constructing_ a [`Task`] to be evaluated at compile time.
#[inline(always)]
pub const fn map_option<'src, I, BI, BO>(
    a: impl Task<'src, I, Option<BI>>,
    b: impl Task<'src, BI, BO> + Send + 'src,
) -> impl Task<'src, I, Option<BO>>
where
    BI: Send + 'src,
    BO: Send + 'src,
{
    MapOption {
        prev: a,
        next: b,
        _phantom: PhantomData,
    }
}

struct MapIterator<A, B, IO> {
    prev: A,
    next: B,
    _phantom: PhantomData<IO>,
}

impl<'src, I, O, IO, BO, IBO, A, B> Task<'src, I, IBO> for MapIterator<A, B, IO>
where
    IO: Iterator<Item = O> + Send + 'src,
    BO: Send + 'src,
    IBO: Iterator<Item = BO> + Send + 'src,
    A: Task<'src, I, IO>,
    B: Task<'src, O, BO> + Clone + Send + 'src,
{
    go_impl!('src, I, IBO);

    #[inline]
    fn go<M>(self, mode: &'src M, input: I) -> TaskOutput<'src, M, IBO>
    where
        Self: Sized,
        M: Mode<'src> + Send + Sync,
    {
        unimplemented!("I probably need to have a Self::StreamOutput type - because Iterator is a trait we would have to constraint all the go_whatever sites to be the same concrete type")
        // mode.map_iterator(self.prev.go(mode, input), move |mode, output| {
        //     (&self.next).clone().go(mode, output)
        // })
    }
}

/// Combine two tasks by feeding the output of one into the next, mapping a
/// [`Option`] to another and passing [`None`] through. Analogous to
/// [`Option::map`](core::option::Option::map).
///
/// For ergonomics, [`TaskOptionExt::map`](crate::task::TaskOptionExt::map)
/// should be preferred, but until `const` trait functions are stabilized this
/// function permits _constructing_ a [`Task`] to be evaluated at compile time.
#[inline(always)]
pub const fn map_iterator<'src, I, O, IO, BO, IBO>(
    a: impl Task<'src, I, IO>,
    b: impl Task<'src, O, BO> + Clone + Send + 'src,
) -> impl Task<'src, I, IBO>
where
    IO: Iterator<Item = O> + Send + 'src,
    BO: Send + 'src,
    IBO: Iterator<Item = BO> + Send + 'src,
{
    MapIterator {
        prev: a,
        next: b,
        _phantom: PhantomData,
    }
}