use pin_project_lite::pin_project;
use std::error::Error;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
#[derive(Debug)]
pub struct TimedOutError;
impl Error for TimedOutError {}
impl fmt::Display for TimedOutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "timed out")
}
}
pin_project! {
#[non_exhaustive]
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
pub struct Timeout<T, S> {
#[pin]
value: T,
#[pin]
sleep: S,
}
}
impl<T, S> Timeout<T, S> {
pub fn new(value: T, sleep: S) -> Timeout<T, S> {
Timeout { value, sleep }
}
}
impl<T, S> Future for Timeout<T, S>
where
T: Future,
S: Future,
{
type Output = Result<T::Output, TimedOutError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.project();
if let Poll::Ready(v) = me.value.poll(cx) {
return Poll::Ready(Ok(v));
}
match me.sleep.poll(cx) {
Poll::Ready(_) => Poll::Ready(Err(TimedOutError)),
Poll::Pending => Poll::Pending,
}
}
}
#[cfg(test)]
mod tests {
use super::{TimedOutError, Timeout};
use crate::future::never::Never;
#[tokio::test]
async fn success() {
assert!(matches!(
Timeout::new(async { Ok::<isize, isize>(5) }, Never).await,
Ok(Ok(5))
));
}
#[tokio::test]
async fn failure() {
assert!(matches!(
Timeout::new(async { Err::<isize, isize>(0) }, Never).await,
Ok(Err(0))
));
}
#[tokio::test]
async fn timeout() {
assert!(matches!(
Timeout::new(Never, async {}).await,
Err(TimedOutError)
));
}
#[tokio::test]
async fn prefer_value_to_timeout() {
assert!(matches!(Timeout::new(async { 5 }, async {}).await, Ok(5)));
}
}