1use core::ops::{Deref, DerefMut};
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use core::time::Duration;
5
6use futures_core::future::FusedFuture;
7use futures_core::stream::FusedStream;
8use futures_core::{Future, Stream};
9use futures_timer::Delay;
10use pin_project::pin_project;
11
12pub trait TimeoutStreamExt: Stream + Sized {
13 fn timeout(self, duration: Duration) -> Timeout<Self> {
17 Timeout {
18 inner: self,
19 timer: Some(Delay::new(duration)),
20 duration,
21 }
22 }
23}
24
25pub trait TimeoutFutureExt: Future + Sized {
26 fn timeout(self, duration: Duration) -> Timeout<Self> {
28 Timeout {
29 inner: self,
30 timer: Some(Delay::new(duration)),
31 duration,
32 }
33 }
34}
35
36impl<T> TimeoutFutureExt for T where T: Future {}
37impl<T> TimeoutStreamExt for T where T: Stream {}
38
39#[derive(Debug)]
40pub struct TimeoutError;
41impl core::fmt::Display for TimeoutError {
42 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43 write!(f, "Operation timed out")
44 }
45}
46
47impl core::error::Error for TimeoutError {}
48
49#[derive(Debug)]
50#[pin_project]
51pub struct Timeout<T> {
52 #[pin]
53 inner: T,
54 timer: Option<Delay>,
55 duration: Duration,
56}
57
58impl<T> Timeout<T> {
59
60 pub fn new(inner: T, duration: Duration) -> Self {
62 Timeout {
63 inner,
64 timer: Some(Delay::new(duration)),
65 duration,
66 }
67 }
68
69 pub fn from_stream(inner: T, duration: Duration) -> Self
71 where
72 T: Stream,
73 {
74 Timeout {
75 inner,
76 timer: Some(Delay::new(duration)),
77 duration,
78 }
79 }
80
81 pub fn from_future(inner: T, duration: Duration) -> Self
83 where
84 T: Future,
85 {
86 Timeout {
87 inner,
88 timer: Some(Delay::new(duration)),
89 duration,
90 }
91 }
92
93 pub fn into_inner(self) -> T {
95 self.inner
96 }
97}
98
99impl<T> Deref for Timeout<T> {
100 type Target = T;
101 fn deref(&self) -> &Self::Target {
102 &self.inner
103 }
104}
105
106impl<T> DerefMut for Timeout<T> {
107 fn deref_mut(&mut self) -> &mut Self::Target {
108 &mut self.inner
109 }
110}
111
112impl<T: Future> Future for Timeout<T> {
113 type Output = Result<T::Output, TimeoutError>;
114
115 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
116 let this = self.project();
117
118 let Some(timer) = this.timer.as_mut() else {
119 return Poll::Ready(Err(TimeoutError));
120 };
121
122 match this.inner.poll(cx) {
123 Poll::Ready(value) => {
124 this.timer.take();
125 return Poll::Ready(Ok(value));
126 }
127 Poll::Pending => {}
128 }
129
130 core::task::ready!(Pin::new(timer).poll(cx));
131 this.timer.take();
132 Poll::Ready(Err(TimeoutError))
133 }
134}
135
136impl<T: Future> FusedFuture for Timeout<T> {
137 fn is_terminated(&self) -> bool {
138 self.timer.is_none()
139 }
140}
141
142impl<T: Stream> Stream for Timeout<T> {
143 type Item = Result<T::Item, TimeoutError>;
144
145 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
146 let this = self.project();
147
148 let Some(timer) = this.timer.as_mut() else {
149 return Poll::Ready(None);
150 };
151
152 match this.inner.poll_next(cx) {
153 Poll::Ready(Some(value)) => {
154 timer.reset(*this.duration);
155 return Poll::Ready(Some(Ok(value)));
156 }
157 Poll::Ready(None) => {
158 this.timer.take();
159 return Poll::Ready(None);
160 }
161 Poll::Pending => {}
162 }
163
164 core::task::ready!(Pin::new(timer).poll(cx));
165 this.timer.take();
166 Poll::Ready(Some(Err(TimeoutError)))
167 }
168
169 fn size_hint(&self) -> (usize, Option<usize>) {
170 self.inner.size_hint()
171 }
172}
173
174impl<T: Stream> FusedStream for Timeout<T> {
175 fn is_terminated(&self) -> bool {
176 self.timer.is_none()
177 }
178}
179
180#[cfg(test)]
181mod test {
182 use core::time::Duration;
183
184 use futures::{StreamExt, TryStreamExt};
185
186 #[test]
187 fn fut_timeout() {
188 use crate::TimeoutFutureExt;
189 futures::executor::block_on(
190 futures_timer::Delay::new(Duration::from_secs(10)).timeout(Duration::from_secs(5)),
191 )
192 .expect_err("timeout after timer elapsed");
193 }
194
195 #[test]
196 fn stream_timeout() {
197 use crate::TimeoutStreamExt;
198 futures::executor::block_on(async move {
199 let mut st = futures::stream::once(async move {
200 futures_timer::Delay::new(Duration::from_secs(10)).await;
201 0
202 })
203 .timeout(Duration::from_secs(5))
204 .boxed();
205
206 st.try_next()
207 .await
208 .expect_err("timeout after timer elapsed");
209 });
210 }
211}