timer_kit/
timeout.rs

1use std::{future::Future, pin::Pin, task::Poll, time::Duration};
2
3use pin_project_lite::pin_project;
4
5use crate::{error::Elapsed, Delay};
6
7/// Creates a new `Timeout` with a specified duration.
8/// 
9/// # Example
10/// 
11/// Creates a timeout with smol's timer
12/// 
13/// ```rust,no_run
14/// use std::time::Duration;
15/// use timer_kit::timeout;
16/// 
17/// let result = timeout::<smol::Timer, _>(Duration::from_millis(100), async { }).await;
18/// ```
19/// 
20/// Creates a timeout with `fluvio_wasm_timer::Delay`
21/// 
22/// ```rust,no_run
23/// use std::time::Duration;
24/// use timer_kit::timeout;
25/// 
26/// let result = timeout::<fluvio_wasm_timer::Delay, _>(Duration::from_millis(100), async { }).await;
27/// ```
28pub fn timeout<D, Fut>(duration: Duration, fut: Fut) -> Timeout<D, Fut>
29where
30    D: Delay,
31    Fut: Future,
32{
33    Timeout::new(duration, fut)
34}
35
36/// Creates a new `Timeout` with a specified deadline.
37/// 
38/// # Example
39/// 
40/// Creates a timeout with smol's timer
41/// 
42/// ```rust,no_run
43/// use std::time::{Duration, Instant};
44/// use timer_kit::timeout_at;
45/// 
46/// let result = timeout_at::<smol::Timer, _>(Instant::now() + Duration::from_millis(100), async { }).await;
47/// ```
48/// 
49/// Creates a timeout with `fluvio_wasm_timer::Delay`
50/// 
51/// ```rust,no_run
52/// use std::time::{Duration};
53/// use fluent_wasm_timer::Instant;
54/// use timer_kit::timeout_at;
55/// 
56/// let result = timeout_at::<fluvio_wasm_timer::Delay, _>(Instant::now() + Duration::from_millis(100), async { }).await;
57/// ```
58pub fn timeout_at<D, Fut>(deadline: D::Instant, fut: Fut) -> Timeout<D, Fut>
59where
60    D: Delay,
61    Fut: Future,
62{
63    Timeout::new_at(deadline, fut)
64}
65
66pin_project! {
67    /// A timeout future.
68    ///
69    /// It returns `<Fut as Future>::Output` if the future completes before the timeout or
70    /// [`Elapsed`] if the timeout elapses before the future completes.
71    ///
72    /// # Warning - Exhaustion
73    ///
74    /// This future is not able to avoid exhaustion if the future never completes and never returns
75    /// `Pending`. The user should ensure that the `Fut` future is able to return `Pending` at some
76    /// point to avoid exhaustion.
77    pub struct Timeout<D, Fut> {
78        #[pin]
79        delay: D,
80
81        #[pin]
82        future: Fut,
83    }
84}
85
86impl<D, Fut> Timeout<D, Fut>
87where
88    D: Delay,
89    Fut: Future,
90{
91    /// Creates a new `Timeout` with a specified duration.
92    ///
93    /// # Example
94    ///
95    /// Creates a timeout with smol's timer
96    /// 
97    /// ```rust,no_run
98    /// use std::time::Duration;
99    /// use timer_kit::Timeout;
100    /// 
101    /// let result = Timeout::<smol::Timer, _>::new(Duration::from_millis(100), async {}).await;
102    /// ```
103    /// 
104    /// Creates a timeout with `fluvio_wasm_timer::Delay`
105    /// 
106    /// ```rust,no_run
107    /// use std::time::Duration;
108    /// use timer_kit::Timeout;
109    /// 
110    /// let result = Timeout::<fluvio_wasm_timer::Delay, _>::new(Duration::from_millis(100), async {}).await;
111    /// ```
112    pub fn new(duration: Duration, future: Fut) -> Self {
113        Self {
114            delay: D::delay(duration),
115            future,
116        }
117    }
118
119    /// Creates a new `Timeout` with a specified deadline.
120    ///
121    /// # Example
122    ///
123    /// Creates a timeout with smol's timer
124    ///
125    /// ```rust,no_run
126    /// use std::time::{Duration, Instant};
127    /// use timer_kit::Timeout;
128    ///
129    /// let result = Timeout::<smol::Timer, _>::new_at(Instant::now() + Duration::from_millis(100), async {}).await;
130    /// ```
131    ///
132    /// Creates a timeout with `fluvio_wasm_timer::Delay`
133    /// 
134    /// ```rust,no_run
135    /// use std::time::Duration;
136    /// use fluent_wasm_timer::Instant;
137    /// use timer_kit::Timeout;
138    /// 
139    /// let result = Timeout::<fluvio_wasm_timer::Delay, _>::new_at(Instant::now() + Duration::from_millis(100), async {}).await;
140    /// ```
141    pub fn new_at(deadline: D::Instant, future: Fut) -> Self {
142        Self {
143            delay: D::delay_until(deadline),
144            future,
145        }
146    }
147}
148
149impl<D, Fut> Future for Timeout<D, Fut>
150where
151    D: Delay,
152    Fut: Future,
153{
154    type Output = Result<Fut::Output, Elapsed>;
155
156    fn poll(
157        self: Pin<&mut Self>,
158        cx: &mut std::task::Context<'_>,
159    ) -> std::task::Poll<Self::Output> {
160        let mut this = self.project();
161
162        if let Poll::Ready(output) = this.future.poll(cx) {
163            return Poll::Ready(Ok(output));
164        }
165
166        match this.delay.as_mut().poll_elapsed(cx) {
167            Poll::Ready(_) => Poll::Ready(Err(Elapsed::new())),
168            Poll::Pending => Poll::Pending,
169        }
170    }
171}