yaaral 0.5.3

yet another async runtime abstraction library
Documentation
//! Extension for time.

use std::{fmt::Debug, time::Duration};

#[cfg(feature = "bevy_runtime_018")]
use bevy_tasks_018 as bevy_tasks;

/// Default timeout error
#[derive(Debug)]
pub struct Elapsed;

/// Interface related to time
pub trait TimeInterface {
    /// Error type for timeout.
    type Elapsed: Debug;

    /// Sleep for the given duration.
    fn sleep(duration: Duration) -> impl Future<Output = ()> + Send;

    /// Wait for the future to complete, return an error if the future does not complete before the given duration.
    fn timeout<T>(
        duration: Duration,
        future: impl Future<Output = T> + Send,
    ) -> impl Future<Output = Result<T, Self::Elapsed>> + Send;
}

#[cfg(any(feature = "futures_runtime", feature = "bevy_runtime"))]
async fn futures_impl<T, RT: TimeInterface>(
    duration: Duration,
    future: impl Future<Output = T> + Send,
) -> Result<T, Elapsed> {
    let sleep = RT::sleep(duration);
    futures::pin_mut!(sleep);
    futures::pin_mut!(future);
    match futures::future::select(sleep, future).await {
        futures::future::Either::Left(_) => Err(Elapsed),
        futures::future::Either::Right((v, _)) => Ok(v),
    }
}

#[cfg(feature = "futures_runtime")]
impl TimeInterface for crate::futures::Runtime {
    type Elapsed = Elapsed;
    async fn sleep(duration: std::time::Duration) {
        async_io::Timer::after(duration).await;
    }
    fn timeout<T>(
        duration: Duration,
        future: impl Future<Output = T> + Send,
    ) -> impl Future<Output = Result<T, Self::Elapsed>> + Send {
        futures_impl::<T, Self>(duration, future)
    }
}

#[cfg(feature = "tokio_runtime")]
impl TimeInterface for crate::tokio::Runtime {
    type Elapsed = tokio::time::error::Elapsed;
    fn sleep(duration: std::time::Duration) -> impl Future<Output = ()> + Send {
        tokio::time::sleep(duration)
    }
    fn timeout<T>(
        duration: Duration,
        future: impl Future<Output = T> + Send,
    ) -> impl Future<Output = Result<T, Self::Elapsed>> + Send {
        tokio::time::timeout(duration, future)
    }
}

#[cfg(any(feature = "bevy_runtime", feature = "bevy_runtime_018"))]
impl<TP> TimeInterface for crate::bevy::Runtime<TP>
where
    TP: std::ops::Deref<Target = bevy_tasks::TaskPool> + Sync + 'static,
{
    type Elapsed = Elapsed;
    async fn sleep(duration: std::time::Duration) {
        async_io::Timer::after(duration).await;
    }
    fn timeout<T>(
        duration: Duration,
        future: impl Future<Output = T> + Send,
    ) -> impl Future<Output = Result<T, Self::Elapsed>> + Send {
        futures_impl::<T, Self>(duration, future)
    }
}

#[cfg(test)]
mod test {
    use crate::common;

    use super::TimeInterface;
    use crate::{Config, CreationInterface};

    fn test_full_runtime<RT: CreationInterface + TimeInterface>() {
        let rt = RT::new(Config::new().prefix("test")).unwrap();
        common::test_sleep(&rt);
        common::test_timeout(&rt);
    }
    #[cfg(feature = "futures_runtime")]
    #[test]
    fn test_futures() {
        test_full_runtime::<crate::futures::Runtime>();
    }
    #[cfg(feature = "tokio_runtime")]
    #[test]
    fn test_tokio() {
        test_full_runtime::<crate::tokio::Runtime>();
    }
}