#![warn(clippy::all)]
pub mod fast_timeout;
pub mod timer;
pub use fast_timeout::fast_sleep as sleep;
pub use fast_timeout::fast_timeout as timeout;
use pin_project_lite::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::task::{self, Poll};
use tokio::time::{sleep as tokio_sleep, Duration};
pub trait ToTimeout {
fn timeout(&self) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>>;
fn create(d: Duration) -> Self;
}
pub struct TokioTimeout(Duration);
impl ToTimeout for TokioTimeout {
fn timeout(&self) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> {
Box::pin(tokio_sleep(self.0))
}
fn create(d: Duration) -> Self {
TokioTimeout(d)
}
}
#[derive(Debug)]
pub struct Elapsed;
impl std::fmt::Display for Elapsed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Timeout Elapsed")
}
}
impl std::error::Error for Elapsed {}
pub fn tokio_timeout<T>(duration: Duration, future: T) -> Timeout<T, TokioTimeout>
where
T: Future,
{
Timeout::<T, TokioTimeout>::new_with_delay(future, duration)
}
pin_project! {
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Timeout<T, F> {
#[pin]
value: T,
#[pin]
delay: Option<Pin<Box<dyn Future<Output = ()> + Send + Sync>>>,
callback: F, }
}
impl<T, F> Timeout<T, F>
where
F: ToTimeout,
{
pub(crate) fn new_with_delay(value: T, d: Duration) -> Timeout<T, F> {
Timeout {
value,
delay: None,
callback: F::create(d),
}
}
}
impl<T, F> Future for Timeout<T, F>
where
T: Future,
F: ToTimeout,
{
type Output = Result<T::Output, Elapsed>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut me = self.project();
if let Poll::Ready(v) = me.value.poll(cx) {
return Poll::Ready(Ok(v));
}
let delay = me
.delay
.get_or_insert_with(|| Box::pin(me.callback.timeout()));
match delay.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(()) => Poll::Ready(Err(Elapsed {})),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_timeout() {
let fut = tokio_sleep(Duration::from_secs(1000));
let to = timeout(Duration::from_secs(1), fut);
assert!(to.await.is_err())
}
#[tokio::test]
async fn test_instantly_return() {
let fut = async { 1 };
let to = timeout(Duration::from_secs(1), fut);
assert_eq!(to.await.unwrap(), 1)
}
#[tokio::test]
async fn test_delayed_return() {
let fut = async {
tokio_sleep(Duration::from_secs(1)).await;
1
};
let to = timeout(Duration::from_secs(1000), fut);
assert_eq!(to.await.unwrap(), 1)
}
}