matrix_sdk_common/
timeout.rs1use std::{error::Error, fmt, time::Duration};
16
17use futures_core::Future;
18#[cfg(target_arch = "wasm32")]
19use futures_util::future::{select, Either};
20#[cfg(target_arch = "wasm32")]
21use gloo_timers::future::TimeoutFuture;
22#[cfg(not(target_arch = "wasm32"))]
23use tokio::time::timeout as tokio_timeout;
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct ElapsedError(());
28
29impl fmt::Display for ElapsedError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "time waiting for future has elapsed!")
32 }
33}
34
35impl Error for ElapsedError {}
36
37pub async fn timeout<F, T>(future: F, duration: Duration) -> Result<T, ElapsedError>
43where
44 F: Future<Output = T>,
45{
46 #[cfg(not(target_arch = "wasm32"))]
47 return tokio_timeout(duration, future).await.map_err(|_| ElapsedError(()));
48
49 #[cfg(target_arch = "wasm32")]
50 {
51 let timeout_future =
52 TimeoutFuture::new(u32::try_from(duration.as_millis()).expect("Overlong duration"));
53
54 match select(std::pin::pin!(future), timeout_future).await {
55 Either::Left((res, _)) => Ok(res),
56 Either::Right((_, _)) => Err(ElapsedError(())),
57 }
58 }
59}
60
61#[cfg(test)]
62pub(crate) mod tests {
63 use std::{future, time::Duration};
64
65 use matrix_sdk_test_macros::async_test;
66
67 use super::timeout;
68
69 #[cfg(target_arch = "wasm32")]
70 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
71
72 #[async_test]
73 async fn test_without_timeout() {
74 timeout(future::ready(()), Duration::from_millis(100))
75 .await
76 .expect("future should have completed without ElapsedError");
77 }
78
79 #[async_test]
80 async fn test_with_timeout() {
81 timeout(future::pending::<()>(), Duration::from_millis(100))
82 .await
83 .expect_err("future should return an ElapsedError");
84 }
85}