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 AndThenResult<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 AndThenResult<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, Result<BO, E>> + 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.and_then_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::and_then`](core::result::Result::and_then).
///
/// For ergonomics,
/// [`TaskResultExt::and_then`](crate::task::TaskResultExt::and_then) 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 and_then_result<'src, I, E, BI, BO>(
    a: impl Task<'src, I, Result<BI, E>>,
    b: impl Task<'src, BI, Result<BO, E>> + Send + 'src,
) -> impl Task<'src, I, Result<BO, E>>
where
    E: Send + 'src,
    BI: Send + 'src,
    BO: Send + 'src,
{
    AndThenResult {
        prev: a,
        next: b,
        _phantom: PhantomData,
    }
}

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

impl<'src, AI, BI, BO, A, B> Task<'src, AI, Option<BO>> for AndThenOption<A, B, BI>
where
    BI: Send + 'src,
    BO: Send + 'src,
    A: Task<'src, AI, Option<BI>>,
    B: Task<'src, BI, Option<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.and_then_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 errors through. Analogous to
/// [`Option::and_then`](core::option::Option::and_then).
///
/// For ergonomics,
/// [`TaskOptionExt::and_then`](crate::task::TaskOptionExt::and_then) 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 and_then_option<'src, I, BI, BO>(
    a: impl Task<'src, I, Option<BI>>,
    b: impl Task<'src, BI, Option<BO>> + Send + 'src,
) -> impl Task<'src, I, Option<BO>>
where
    BI: Send + 'src,
    BO: Send + 'src,
{
    AndThenOption {
        prev: a,
        next: b,
        _phantom: PhantomData,
    }
}