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}