1use std::time::Duration;
2use std::io;
3use futures::{self, Future};
4
5#[cfg(not(target_family = "wasm"))]
6use tokio::time::sleep;
7
8#[cfg(target_family = "wasm")]
9use futures::FutureExt;
10
11#[cfg(target_family = "wasm")]
12pub async fn sleep(duration: Duration) {
13 gloo_timers::future::TimeoutFuture::new(duration.as_millis() as u32).await;
14}
15
16pub const INFINITE_RETRIES: u32 = 0;
17
18pub async fn retry_with_delay<F, T, E>(mut f: F, max_attempts: u32, delay: Duration) -> Result<T, E>
29where
30 F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, E>> + Send>>,
31 E: std::fmt::Debug,
32{
33 let mut retries = 0;
34 loop {
35 match f().await {
36 Ok(result) => return Ok(result),
37 Err(e) => {
38 retries += 1;
39 if max_attempts != INFINITE_RETRIES && retries >= max_attempts {
40 return Err(e);
41 }
42 tracing::warn!("Retry {}/{} after {:?}: {:?}", retries, max_attempts, delay, e);
43 sleep(delay).await;
44 }
45 }
46 }
47}
48
49pub async fn retry_with_increasing_delay<F, T, E>(mut f: F, max_retries: u32, initial_delay: Duration) -> Result<T, E>
50where
51 F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, E>> + Send>>,
52 E: std::fmt::Debug,
53{
54 let mut retries = 0;
55 let mut delay = initial_delay;
56 loop {
57 match f().await {
58 Ok(result) => return Ok(result),
59 Err(e) => {
60 retries += 1;
61 if retries >= max_retries {
62 return Err(e);
63 }
64 tracing::warn!("Retry {}/{} after {:?}: {:?}", retries, max_retries, delay, e);
65 sleep(delay).await;
66 delay *= 2;
67 }
68 }
69 }
70}
71
72#[cfg(target_family = "wasm")]
73pub async fn timeout<F, T>(duration: Duration, future: F) -> Result<T, io::Error>
74where
75 F: Future<Output = T>,
76{
77 if duration.is_zero() {
78 return Ok(future.await);
79 }
80 let timeout_fut = gloo_timers::future::TimeoutFuture::new(duration.as_millis() as u32);
81 futures::select! {
82 result = future.fuse() => Ok(result),
83 _ = timeout_fut.fuse() => Err(io::Error::new(io::ErrorKind::TimedOut, "Operation timed out")),
84 }
85}
86
87#[cfg(not(target_family = "wasm"))]
88pub async fn timeout<F, T>(duration: Duration, future: F) -> Result<T, io::Error>
89where
90 F: Future<Output = T>,
91{
92 if duration.is_zero() {
93 return Ok(future.await);
94 }
95 tokio::time::timeout(duration, future)
96 .await
97 .map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "Operation timed out"))
98}
99
100#[cfg(target_family = "wasm")]
101pub fn now_unix_secs() -> u64 {
102 let millis: f64 = wasm_bindgen_futures::js_sys::Date::now(); let secs_f64 = (millis / 1000.0).floor();
105 u64::try_from(secs_f64 as i128).unwrap_or(u64::MAX)
107}
108
109#[cfg(not(target_family = "wasm"))]
110pub fn now_unix_secs() -> u64 {
111 use std::time::{SystemTime, UNIX_EPOCH};
112 SystemTime::now()
113 .duration_since(UNIX_EPOCH)
114 .unwrap()
115 .as_secs()
116}