[][src]Crate restartables

Say, for example, that you want to keep pinging a URL until it returns 200, or five seconds pass. And if the URL does return 200, you'd like to know how long that took.

This library contains a Future wrapper named Restartable. It wraps up a Future you want to retry, and it keeps retrying the future until it passes a Test you provide. If the inner future passes the Test, then the wrapper resolves your value. But if the inner future fails the Test, the wrapper will just restart the future. Assuming the timeout hasn't expired.

To do this, you need to provide three things when instantiating the wrapper:

  • A future to poll
  • A test, i.e. a closure which takes values from the inner future, runs a test on it, and returns Result
  • A factory to make new futures if the previous future resolved a value that failed the test.

The wrapper will also return some metrics, i.e. how much time elapsed before the future resolved, and how many restarts were necessary.

Example

use restartables::{Failure, Restartable};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;

// A Future that yields a random u16 when it resolves.
struct RandomNum {}
impl Future for RandomNum {
    type Output = u16;
    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        cx.waker().wake_by_ref();
        Poll::Ready(rand::random())
    }
}

async fn print_random_even_number() {
    // This closure produces futures that the Restartable will poll
    let factory = || RandomNum {};

    // This test returns even numbers, and fails odd numbers.
    let test_is_even = |num| {
        if num % 2 == 0 {
            Ok(num)
        } else {
            Err("number wasn't even")
        }
    };

    // Wrap the inner `RandomNum` future into a `Restartable` future.
    let retrying = Restartable::new(
        factory,
        Some(Duration::from_millis(1)),
        test_is_even,
    );

    match retrying.await {
        Ok(success) => println!(
            "Final number was {}, which took {}us and {} restarts to get",
            success.value,
            success.duration.as_micros(),
            success.restarts
        ),
        Err(Failure::Timeout) => println!("Never found an even number :("),
        Err(Failure::Err { error, restarts }) => {
            println!("Error {} after {} restarts", error, restarts)
        }
    };
}

Structs

Restartable

Wraps an inner future, restarting it until it resolves a value that passes a test, or times out.

Success

Value returned from a successful test, along with metrics.

Enums

Failure

Different ways a Restartable can fail