finchers-ext 0.11.0

Extensions for constructing Endpoints
Documentation
#![allow(missing_docs)]

use super::maybe_done::MaybeDone;
use finchers_core::endpoint::{Context, Endpoint, IntoEndpoint};
use finchers_core::task::{self, Task};
use finchers_core::{Error, Poll, PollResult};
use std::fmt;

pub fn new<E1, E2>(e1: E1, e2: E2) -> And<E1::Endpoint, E2::Endpoint>
where
    E1: IntoEndpoint,
    E2: IntoEndpoint,
    E1::Output: Send,
    E2::Output: Send,
{
    And {
        e1: e1.into_endpoint(),
        e2: e2.into_endpoint(),
    }
}

#[derive(Copy, Clone, Debug)]
pub struct And<E1, E2> {
    e1: E1,
    e2: E2,
}

impl<E1, E2> Endpoint for And<E1, E2>
where
    E1: Endpoint,
    E2: Endpoint,
    E1::Output: Send,
    E2::Output: Send,
{
    type Output = (E1::Output, E2::Output);
    type Task = AndTask<E1::Task, E2::Task>;

    fn apply(&self, cx: &mut Context) -> Option<Self::Task> {
        let f1 = self.e1.apply(cx)?;
        let f2 = self.e2.apply(cx)?;
        Some(AndTask {
            f1: MaybeDone::Pending(f1),
            f2: MaybeDone::Pending(f2),
        })
    }
}

pub struct AndTask<F1: Task, F2: Task> {
    f1: MaybeDone<F1>,
    f2: MaybeDone<F2>,
}

impl<T1, T2> fmt::Debug for AndTask<T1, T2>
where
    T1: Task + fmt::Debug,
    T2: Task + fmt::Debug,
    T1::Output: fmt::Debug,
    T2::Output: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("AndTask")
            .field("t1", &self.f1)
            .field("t2", &self.f2)
            .finish()
    }
}

impl<F1, F2> Task for AndTask<F1, F2>
where
    F1: Task,
    F2: Task,
    F1::Output: Send,
    F2::Output: Send,
{
    type Output = (F1::Output, F2::Output);

    fn poll_task(&mut self, cx: &mut task::Context) -> PollResult<Self::Output, Error> {
        let mut all_done = match self.f1.poll_done(cx) {
            Ok(done) => done,
            Err(e) => {
                self.f1.erase();
                self.f2.erase();
                return Poll::Ready(Err(e));
            }
        };
        all_done = match self.f2.poll_done(cx) {
            Ok(done) => all_done && done,
            Err(e) => {
                self.f1.erase();
                self.f2.erase();
                return Poll::Ready(Err(e));
            }
        };

        if all_done {
            Poll::Ready(Ok((self.f1.take_item(), self.f2.take_item())))
        } else {
            Poll::Pending
        }
    }
}