oxidized-builder 0.1.0-delta

Oxidized Builder - Ethereum block and transactions framework
Documentation
use std::future::Future;
use std::time::Duration;
use tokio::time::sleep;

/// Retry an async operation with exponential backoff.
pub async fn retry_async<F, Fut, T, E>(
    mut op: F,
    attempts: usize,
    initial_delay: Duration,
) -> Result<T, E>
where
    F: FnMut(usize) -> Fut,
    Fut: Future<Output = Result<T, E>>,
{
    let mut delay = initial_delay;
    let mut attempt = 1;
    loop {
        match op(attempt).await {
            Ok(v) => return Ok(v),
            Err(e) if attempt < attempts => {
                sleep(delay).await;
                delay = delay.saturating_mul(2);
                attempt += 1;
                continue;
            }
            Err(e) => return Err(e),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::atomic::{AtomicUsize, Ordering};

    #[tokio::test]
    async fn retries_until_success() {
        let counter = AtomicUsize::new(0);
        let res: Result<u32, ()> = retry_async(
            |_| {
                let current = counter.fetch_add(1, Ordering::Relaxed);
                async move {
                    if current < 2 {
                        Err(())
                    } else {
                        Ok(7)
                    }
                }
            },
            4,
            Duration::from_millis(1),
        )
        .await;

        assert_eq!(res.unwrap(), 7);
        assert!(counter.load(Ordering::Relaxed) >= 3);
    }
}