futures_retry/lib.rs
1//! [](https://gitlab.com/mexus/futures-retry/commits/master)
2//! [](https://crates.io/crates/futures-retry)
3//! [](https://docs.rs/futures-retry)
4//!
5//! [[Release docs]](https://docs.rs/futures-retry/)
6//!
7//! [[Master docs]](https://mexus.gitlab.io/futures-retry/futures_retry/)
8//!
9//! A tool that helps you retry your future :) Well, `Future`s and `Stream`s, to be precise.
10//!
11//! It's quite a common task when you need to repeat some action if you've got an error, be it a
12//! connection timeout or some temporary OS error.
13//!
14//! When you do stuff in a synchronous manner it's quite easy to implement the attempts logic, but
15//! when it comes to asynchronous programming you suddenly need to write a fool load of a
16//! boilerplate code, introduce state machines and everything.
17//!
18//! This library aims to make your life easier and let you write more straightword and nice code,
19//! concentrating on buisness logic rathen than on handling all the mess.
20//!
21//! I was inspired to write this library after coming over a [`hyper`
22//! issue](https://github.com/hyperium/hyper/issues/1358), and I came to an understanding that the
23//! problem is more common than I used to think.
24//!
25//! For examples have a look in the `examples/` folder in the git repo.
26//!
27//! Suggestions and critiques are welcome!
28//!
29//! ```rust
30//! // ...
31//! # use tokio::io;
32//! # use tokio::net::{TcpListener, TcpStream};
33//! # use std::time::Duration;
34//! # use futures::{future::{ok, select}, TryStreamExt, TryFutureExt, FutureExt, stream};
35//! use futures_retry::{RetryPolicy, StreamRetryExt};
36//!
37//! // In this example we use a free function to handle errors, while in your project you have
38//! // more options: for simple cases a simple closure will do, for complex cases you might go
39//! // as far as implementing an `ErrorHandler` trait for a custom type with some complex logic.
40//! fn handle_error(e: io::Error) -> RetryPolicy<io::Error> {
41//! match e.kind() {
42//! io::ErrorKind::Interrupted => RetryPolicy::Repeat,
43//! io::ErrorKind::PermissionDenied => RetryPolicy::ForwardError(e),
44//! _ => RetryPolicy::WaitRetry(Duration::from_millis(5)),
45//! }
46//! }
47//!
48//! async fn serve_connection(stream: TcpStream) {
49//! // ...
50//! }
51//!
52//! #[tokio::main]
53//! async fn main() {
54//! let addr = //...
55//! # "127.0.0.1:12345";
56//! let mut listener = TcpListener::bind(addr).await.unwrap();
57//! let server = stream::try_unfold(listener, |listener| async move {
58//! Ok(Some((listener.accept().await?.0, listener)))
59//! })
60//! .retry(handle_error) // Magic happens here
61//! .and_then(|(stream, _attempt)| {
62//! tokio::spawn(serve_connection(stream));
63//! ok(())
64//! })
65//! .try_for_each(|_| ok(()))
66//! .map_err(|(e, _attempt)| eprintln!("Caught an error {}", e));
67//! # // This nasty hack is required to exit immediately when running the doc tests.
68//! # futures::pin_mut!(server);
69//! # let server = select(ok::<_, ()>(()), server).map(|_| ());
70//! server.await
71//! }
72//! ```
73//!
74//! ## License
75//!
76//! Licensed under either of
77//!
78//! * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
79//! * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
80//!
81//! at your option.
82//!
83//! ### Contribution
84//!
85//! Unless you explicitly state otherwise, any contribution intentionally submitted
86//! for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
87//! additional terms or conditions.
88
89#![deny(missing_docs)]
90#![allow(clippy::needless_doctest_main)]
91
92use std::time::Duration;
93
94mod error_handler;
95mod future;
96mod stream;
97
98pub use crate::{
99 error_handler::ErrorHandler,
100 future::{FutureFactory, FutureRetry},
101 stream::{StreamRetry, StreamRetryExt},
102};
103
104/// What to do when a future returns an error. Used in `FutureRetry::new` and `StreamRetry::new`.
105#[derive(Debug, Eq, PartialEq)]
106pub enum RetryPolicy<E> {
107 /// Create and poll a new future immediately.
108 ///
109 /// # Be careful!
110 ///
111 /// Please be careful when using this variant since it might lead to a high (actually 100%) CPU
112 /// usage in case a future instantly resolves into an error every time.
113 Repeat,
114 /// Wait for a given duration and make another attempt then.
115 WaitRetry(Duration),
116 /// Don't give it another try, just pass the error further to the user.
117 ForwardError(E),
118}