use core::future::Future;
use core::{fmt, task, time, mem};
use core::pin::Pin;
use crate::oneshot::Oneshot;
use crate::oneshot::Timer as PlatformTimer;
#[must_use = "Timed does nothing unless polled"]
pub enum Timed<F, T=PlatformTimer> {
#[doc(hidden)]
Ongoing(T, F, time::Duration),
#[doc(hidden)]
Stopped,
}
impl<F: Future + Unpin> Timed<F> {
#[inline]
pub fn platform_new(inner: F, timeout: time::Duration) -> Self {
Timed::<F, PlatformTimer>::new(inner, timeout)
}
}
impl<F: Future> Timed<F> {
#[inline]
pub unsafe fn platform_new_unchecked(inner: F, timeout: time::Duration) -> Self {
Timed::<F, PlatformTimer>::new_unchecked(inner, timeout)
}
}
impl<F: Future + Unpin, T: Oneshot> Timed<F, T> {
pub fn new(inner: F, timeout: time::Duration) -> Self {
Timed::Ongoing(T::new(timeout), inner, timeout)
}
}
impl<F: Future, T: Oneshot> Timed<F, T> {
pub unsafe fn new_unchecked(inner: F, timeout: time::Duration) -> Self {
Timed::Ongoing(T::new(timeout), inner, timeout)
}
}
impl<F: Future, T: Oneshot> Future for Timed<F, T> {
type Output = Result<F::Output, Expired<F, T>>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
let mut state = Timed::Stopped;
let mut this = unsafe { self.get_unchecked_mut() };
mem::swap(&mut state, &mut this);
match state {
Timed::Ongoing(mut timer, mut future, timeout) => {
match Future::poll(unsafe { Pin::new_unchecked(&mut future) }, ctx) {
task::Poll::Pending => (),
task::Poll::Ready(result) => return task::Poll::Ready(Ok(result)),
}
match Future::poll(Pin::new(&mut timer), ctx) {
task::Poll::Pending => (),
task::Poll::Ready(_) => return task::Poll::Ready(Err(Expired {
inner: Timed::Ongoing(timer, future, timeout),
})),
}
*this = Timed::Ongoing(timer, future, timeout);
task::Poll::Pending
},
Timed::Stopped => task::Poll::Pending,
}
}
}
impl<F: Future + Unpin, T: Oneshot> Unpin for Timed<F, T> {}
pub struct Expired<F, T> {
inner: Timed<F, T>,
}
impl<F: Future, T: Oneshot> Expired<F, T> {
pub fn into_inner(self) -> F {
match self.inner {
Timed::Ongoing(_, fut, _) => fut,
_ => unreach!(),
}
}
}
impl<F: Future, T: Oneshot> Future for Expired<F, T> {
type Output = Timed<F, T>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
let mut state = Timed::Stopped;
let this = unsafe { self.get_unchecked_mut() };
mem::swap(&mut this.inner, &mut state);
match state {
Timed::Ongoing(mut timer, future, timeout) => {
timer.restart(timeout, ctx.waker());
task::Poll::Ready(Timed::Ongoing(timer, future, timeout))
},
_ => task::Poll::Pending,
}
}
}
impl<F: Future + Unpin, T: Oneshot> Unpin for Expired<F, T> {}
#[cfg(not(feature = "no_std"))]
impl<F, T: Oneshot> crate::std::error::Error for Expired<F, T> {}
impl<F, T: Oneshot> fmt::Debug for Expired<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl<F, T: Oneshot> fmt::Display for Expired<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
Timed::Stopped => write!(f, "Future is being re-tried."),
Timed::Ongoing(_, _, timeout) => match timeout.as_secs() {
0 => write!(f, "Future expired in {} ms", timeout.as_millis()),
secs => write!(f, "Future expired in {} seconds and {} ms", secs, timeout.subsec_millis()),
},
}
}
}