1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
//! [![Build Status]][actions] [![Latest Version]][crates.io] [](https://discord.gg/8ARnvtJePD)
//!
//! [Build Status]: https://img.shields.io/github/actions/workflow/status/Xuanwo/backon/ci.yml?branch=main
//! [actions]: https://github.com/Xuanwo/backon/actions?query=branch%3Amain
//! [Latest Version]: https://img.shields.io/crates/v/backon.svg
//! [crates.io]: https://crates.io/crates/backon
//!
//! <img src="https://raw.githubusercontent.com/Xuanwo/backon/main/.github/assets/logo.jpeg" alt="BackON" width="38.2%"/>
//!
//! Make **retry** like a built-in feature provided by Rust.
//!
//! - **Simple**: Just like a built-in feature: `your_fn.retry(ExponentialBuilder::default()).await`.
//! - **Flexible**: Supports both blocking and async functions.
//! - **Powerful**: Allows control over retry behavior such as [`when`](https://docs.rs/backon/latest/backon/struct.Retry.html#method.when) and [`notify`](https://docs.rs/backon/latest/backon/struct.Retry.html#method.notify).
//! - **Customizable**: Supports custom retry strategies like [exponential](https://docs.rs/backon/latest/backon/struct.ExponentialBuilder.html), [constant](https://docs.rs/backon/latest/backon/struct.ConstantBuilder.html), etc.
//!
//! # Backoff
//!
//! Retry in BackON requires a backoff strategy. BackON will accept a [`BackoffBuilder`] which will generate a new [`Backoff`] for each retry. It also accepts any object that implements [`Backoff`]. You can therefore easily implement your own custom backoff strategy.
//!
//! BackON provides several backoff implementations with reasonable defaults:
//!
//! - [`ConstantBuilder`]: backoff with a constant delay, limited to a specific number of attempts.
//! - [`ExponentialBuilder`]: backoff with an exponential delay, also supports jitter.
//! - [`FibonacciBuilder`]: backoff with a fibonacci delay, also supports jitter.
//!
//! # Sleep
//!
//! Retry in BackON requires an implementation for sleeping, such an implementation
//! is called a Sleeper, it will implement [`Sleeper`] or [`BlockingSleeper`] depending
//! on if it is going to be used in an asynchronous context.
//!
//! ## Default Sleeper
//!
//! Currently, BackON has 3 built-in Sleeper implementations for different
//! environments, they are gated under their own features, which are enabled
//! by default:
//!
//! | `Sleeper` | feature | Environment | Asynchronous |
//! |-------------------------|---------------------|-------------|---------------|
//! | [`TokioSleeper`] | tokio-sleep | non-wasm32 | Yes |
//! | [`GlooTimersSleep`] | gloo-timers-sleep | wasm32 | Yes |
//! | [`FuturesTimerSleeper`] | futures-timer-sleep |wasm/non-wasm| Yes |
//! | [`EmbassySleep`] | embassy-sleep | no_std | Yes |
//! | [`StdSleeper`] | std-blocking-sleep | std | No |
//!
//! ## Custom Sleeper
//!
//! If you do not want to use the built-in Sleeper, you CAN provide a custom
//! implementation, let's implement an asynchronous dummy Sleeper that does
//! not sleep at all. You will find it pretty similar when you implement a
//! blocking one.
//!
//! ```
//! use std::time::Duration;
//!
//! use backon::Sleeper;
//!
//! /// A dummy `Sleeper` impl that prints then becomes ready!
//! struct DummySleeper;
//!
//! impl Sleeper for DummySleeper {
//! type Sleep = std::future::Ready<()>;
//!
//! fn sleep(&self, dur: Duration) -> Self::Sleep {
//! println!("Hello from DummySleeper!");
//! std::future::ready(())
//! }
//! }
//! ```
//!
//! ## The empty Sleeper
//!
//! If neither feature is enabled nor a custom implementation is provided, BackON
//! will fallback to the empty sleeper, in which case, a compile-time error that
//! `PleaseEnableAFeatureOrProvideACustomSleeper needs to implement Sleeper or
//! BlockingSleeper` will be raised to remind you to choose or bring a real Sleeper
//! implementation.
//!
//! # Retry
//!
//! For additional examples, please visit [`docs::examples`].
//!
//! ## Retry an async function
//!
//! ```rust
//! use anyhow::Result;
//! use backon::ExponentialBuilder;
//! use backon::Retryable;
//! use core::time::Duration;
//!
//! async fn fetch() -> Result<String> {
//! Ok("hello, world!".to_string())
//! }
//!
//! #[tokio::main(flavor = "current_thread")]
//! async fn main() -> Result<()> {
//! let content = fetch
//! // Retry with exponential backoff
//! .retry(ExponentialBuilder::default())
//! // Sleep implementation, default to tokio::time::sleep if `tokio-sleep` has been enabled.
//! .sleep(tokio::time::sleep)
//! // When to retry
//! .when(|e| e.to_string() == "EOF")
//! // Notify when retrying
//! .notify(|err: &anyhow::Error, dur: Duration| {
//! println!("retrying {:?} after {:?}", err, dur);
//! })
//! .await?;
//! println!("fetch succeeded: {}", content);
//!
//! Ok(())
//! }
//! ```
//!
//! ## Retry a blocking function
//!
//! ```rust
//! use anyhow::Result;
//! use backon::BlockingRetryable;
//! use backon::ExponentialBuilder;
//! use core::time::Duration;
//!
//! fn fetch() -> Result<String> {
//! Ok("hello, world!".to_string())
//! }
//!
//! fn main() -> Result<()> {
//! let content = fetch
//! // Retry with exponential backoff
//! .retry(ExponentialBuilder::default())
//! // Sleep implementation, default to std::thread::sleep if `std-blocking-sleep` has been enabled.
//! .sleep(std::thread::sleep)
//! // When to retry
//! .when(|e| e.to_string() == "EOF")
//! // Notify when retrying
//! .notify(|err: &anyhow::Error, dur: Duration| {
//! println!("retrying {:?} after {:?}", err, dur);
//! })
//! .call()?;
//! println!("fetch succeeded: {}", content);
//!
//! Ok(())
//! }
//! ```
//!
//! ## Retry an async function with context
//!
//! Sometimes users can meet the problem that the async function is needs to take `FnMut`:
//!
//! ```shell
//! error: captured variable cannot escape `FnMut` closure body
//! --> src/retry.rs:404:27
//! |
//! 400 | let mut test = Test;
//! | -------- variable defined here
//! ...
//! 404 | let result = { || async { test.hello().await } }
//! | - ^^^^^^^^----^^^^^^^^^^^^^^^^
//! | | | |
//! | | | variable captured here
//! | | returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
//! | inferred to be a `FnMut` closure
//! |
//! = note: `FnMut` closures only have access to their captured variables while they are executing...
//! = note: ...therefore, they cannot allow references to captured variables to escape
//! ```
//!
//! `RetryableWithContext` is designed for this, it allows you to pass a context
//! to the retry function, and return it back after the retry is done.
//!
//! ```no_run
//! use anyhow::anyhow;
//! use anyhow::Result;
//! use backon::ExponentialBuilder;
//! use backon::RetryableWithContext;
//!
//! struct Test;
//!
//! impl Test {
//! async fn hello(&mut self) -> Result<usize> {
//! Err(anyhow!("not retryable"))
//! }
//! }
//!
//! #[tokio::main(flavor = "current_thread")]
//! async fn main() -> Result<()> {
//! let mut test = Test;
//!
//! // (Test, Result<usize>)
//! let (_, result) = {
//! |mut v: Test| async {
//! let res = v.hello().await;
//! (v, res)
//! }
//! }
//! .retry(ExponentialBuilder::default())
//! .context(test)
//! .await;
//!
//! Ok(())
//! }
//! ```
extern crate std;
pub use *;
pub use Retry;
pub use Retryable;
pub use RetryWithContext;
pub use RetryableWithContext;
pub use DefaultSleeper;
pub use FuturesTimerSleeper;
pub use GlooTimersSleep;
pub use Sleeper;
pub use TokioSleeper;
pub use BlockingRetry;
pub use BlockingRetryable;
pub use BlockingRetryWithContext;
pub use BlockingRetryableWithContext;
pub use BlockingSleeper;
pub use DefaultBlockingSleeper;
pub use StdSleeper;
pub use EmbassySleeper;