unwrap_retry/
lib.rs

1#![cfg_attr(
2    feature = "track-caller",
3    feature(async_fn_track_caller)
4)]
5#![allow(async_fn_in_trait)]
6
7use std::{error::Error, future::Future, panic::Location, thread::sleep, time::Duration};
8
9const INTERVAL_MS: Duration = Duration::from_millis(50);
10
11pub trait RetryableResultFn<T> {
12    fn unwrap_blocking(self) -> T;
13}
14
15impl<T, E: Error, F: FnMut() -> Result<T, E>> RetryableResultFn<T> for F {
16    #[cfg_attr(
17        feature = "track-caller",
18        track_caller
19    )]
20    fn unwrap_blocking(mut self) -> T {
21        let caller = Location::caller();
22        let mut res = self();
23        let mut err = None;
24
25        loop {
26            match res {
27                Ok(o) => return o,
28                Err(ref e) => {
29                    let e = format!("{e:#?}");
30                    if err.as_ref() != Some(&e) {
31                        if cfg!(feature = "track-caller") {
32                            println!(
33                                "Error at {}:{}:{}: {e}, will block till success...",
34                                caller.file(),
35                                caller.line(),
36                                caller.column()
37                            );
38                        } else {
39                            println!("Error: {e}, will block till success...");
40                        }
41                        err = Some(e);
42                    }
43                    res = self();
44                }
45            }
46        }
47    }
48}
49
50pub trait RetryableResultAsyncFn<T> {
51    async fn unwrap_res(self, wait: Option<Duration>) -> T;
52}
53
54impl<T, E: Error, Fut: Future<Output = Result<T, E>>, F: FnMut() -> Fut> RetryableResultAsyncFn<T> for F {
55    #[cfg_attr(
56        feature = "track-caller",
57        track_caller
58    )]
59    async fn unwrap_res(mut self, wait: Option<Duration>) -> T {
60        let caller = Location::caller();
61        let mut res = self().await;
62        let mut err: Option<String> = None;
63
64        loop {
65            match res {
66                Ok(o) => return o,
67                Err(ref e) => {
68                    let e = format!("{e:#?}");
69                    if err.as_ref() != Some(&e) {
70                        if cfg!(feature = "track-caller") {
71                            println!(
72                                "Error at {}:{}:{}: {e}, will block till success...",
73                                caller.file(),
74                                caller.line(),
75                                caller.column()
76                            );
77                        } else {
78                            println!("Error: {e}, will block till success...");
79                        }
80                        err = Some(e);
81                    }
82                    res = self().await;
83                }
84            }
85            sleep(wait.unwrap_or(INTERVAL_MS));
86        }
87    }
88}
89
90pub trait RetryableOptionFn<T> {
91    fn unwrap_blocking(self) -> T;
92}
93
94impl<T, F: FnMut() -> Option<T>> RetryableOptionFn<T> for F {
95    #[cfg_attr(
96        feature = "track-caller",
97        track_caller
98    )]
99    fn unwrap_blocking(mut self) -> T {
100        let caller = Location::caller();
101        let mut printed = false;
102
103        loop {
104            match self() {
105                Some(v) => return v,
106                None => {
107                    if !printed {
108                        if cfg!(feature = "track-caller") {
109                            println!(
110                                "None at {}:{}:{}, will block till Some...",
111                                caller.file(),
112                                caller.line(),
113                                caller.column()
114                            );
115                        } else {
116                            println!("None, will block till Some...");
117                        }
118                        printed = true;
119                    }
120                }
121            }
122        }
123    }
124}
125
126pub trait RetryableOptionAsyncFn<T> {
127    async fn unwrap_opt(self, wait: Option<Duration>) -> T;
128}
129
130impl<T, Fut: Future<Output = Option<T>>, F: FnMut() -> Fut> RetryableOptionAsyncFn<T> for F {
131    #[cfg_attr(
132        feature = "track-caller",
133        track_caller
134    )]
135    async fn unwrap_opt(mut self, wait: Option<Duration>) -> T {
136        let caller = Location::caller();
137        let mut printed = false;
138
139        loop {
140            match self().await {
141                Some(v) => return v,
142                None => {
143                    if !printed {
144                        if cfg!(feature = "track-caller") {
145                            println!(
146                                "None at {}:{}:{}, will block till Some...",
147                                caller.file(),
148                                caller.line(),
149                                caller.column()
150                            );
151                        } else {
152                            println!("None, will block till Some...");
153                        }
154                        printed = true;
155                    }
156                }
157            }
158            sleep(wait.unwrap_or(INTERVAL_MS));
159        }
160    }
161}