refault 0.3.0

deterministic simulation framework for distributed systems using async
Documentation
use std::{task::Poll, time::Instant};

use agnostic_lite::time::Elapsed;

use crate::{
    send_bind::SimNodeBound,
    time::{Sleep, sleep_until},
};

pin_project_lite::pin_project! {
    pub struct Timeout<F> {
        #[pin]
        fut: F,
        #[pin]
        sleep: SimNodeBound<Sleep>,
    }
}

impl<F: Future + Send> agnostic_lite::time::AsyncTimeout<F> for Timeout<F> {
    type Instant = Instant;

    fn timeout(timeout: std::time::Duration, fut: F) -> Self
    where
        F: Future + Send,
        Self: Future<Output = Result<F::Output, agnostic_lite::time::Elapsed>> + Send + Sized,
    {
        Self::timeout_at(Instant::now() + timeout, fut)
    }

    fn timeout_at(deadline: Self::Instant, fut: F) -> Self
    where
        F: Future + Send,
        Self: Future<Output = Result<F::Output, agnostic_lite::time::Elapsed>> + Send + Sized,
    {
        Timeout {
            fut,
            sleep: sleep_until(deadline).into(),
        }
    }
}

impl<F: Future> agnostic_lite::time::AsyncLocalTimeout<F> for Timeout<F> {
    type Instant = Instant;

    fn timeout_local(timeout: std::time::Duration, fut: F) -> Self
    where
        F: Future,
        Self: Future<Output = Result<F::Output, agnostic_lite::time::Elapsed>> + Sized,
    {
        Self::timeout_local_at(Instant::now() + timeout, fut)
    }

    fn timeout_local_at(deadline: Self::Instant, fut: F) -> Self
    where
        F: Future,
        Self: Future<Output = Result<F::Output, agnostic_lite::time::Elapsed>> + Sized,
    {
        Timeout {
            fut,
            sleep: sleep_until(deadline).into(),
        }
    }
}

impl<F: Future> Future for Timeout<F> {
    type Output = Result<F::Output, Elapsed>;

    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        match this.sleep.poll(cx) {
            Poll::Ready(_) => return Poll::Ready(Err(Elapsed)),
            Poll::Pending => (),
        }
        match this.fut.poll(cx) {
            Poll::Ready(x) => Poll::Ready(Ok(x)),
            Poll::Pending => Poll::Pending,
        }
    }
}