try-again 0.2.2

Retry synchronous and asynchronous operations.
Documentation
mod retry {
    use assertr::assert_that;
    use assertr::prelude::*;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicI32, Ordering};
    use try_again::{IntoStdDuration, delay, retry};

    #[test]
    fn accepts_closure() {
        let test = || -> Result<(), ()> { Ok(()) };
        let out = retry(test).delayed_by(delay::None.take(0));
        assert_that(out).is_ok().is_equal_to(());
    }

    #[test]
    fn accepts_function_pointer() {
        fn test() -> Result<(), ()> {
            Ok(())
        }
        let out = retry(test).delayed_by(delay::None.take(3));
        assert_that(out).is_ok().is_equal_to(());
    }

    #[test]
    fn on_success_never_retries() {
        fn successful(counter: Arc<AtomicI32>) -> Result<i32, ()> {
            counter.fetch_add(1, Ordering::SeqCst);
            Ok(42)
        }

        let counter = Arc::new(AtomicI32::new(0));

        let out = retry(|| successful(counter.clone())).delayed_by(delay::None.take(3));

        assert_that(out).is_ok().is_equal_to(42);
        assert_that(counter.load(Ordering::SeqCst))
            .with_detail_message("Function must have been called 1 time only!")
            .is_equal_to(1);
    }

    #[test]
    fn on_continuous_error_retries_expected_number_of_times() {
        fn erroneous(counter: Arc<AtomicI32>) -> Result<(), i32> {
            counter.fetch_add(1, Ordering::SeqCst);
            Err(42)
        }

        let counter = Arc::new(AtomicI32::new(0));

        let out =
            retry(|| erroneous(counter.clone())).delayed_by(delay::Fixed::of(50.millis()).take(3));

        assert_that(out).is_err().is_equal_to(42);
        assert_that(counter.load(Ordering::SeqCst))
            .with_subject_name("Function")
            .is_equal_to(4);
    }
}

mod retry_with_options {
    use assertr::assert_that;
    use assertr::prelude::*;
    use std::marker::PhantomData;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicI32, Ordering};
    use try_again::{
        IntoStdDuration, RetryOptions, delay, delay_executor::ThreadSleep, retry_with_options,
    };

    #[test]
    fn accepts_closure() {
        let test = || -> Result<(), ()> { Ok(()) };
        let out = retry_with_options(
            test,
            RetryOptions {
                delay_strategy: delay::None.take(0),
                delay_executor: ThreadSleep,
                _marker: PhantomData,
            },
        );
        assert_that(out).is_ok().is_equal_to(());
    }

    #[test]
    fn accepts_function_pointer() {
        fn test() -> Result<(), ()> {
            Ok(())
        }
        let out = retry_with_options(
            test,
            RetryOptions {
                delay_strategy: delay::None.take(0),
                delay_executor: ThreadSleep,
                _marker: PhantomData,
            },
        );
        assert_that(out).is_ok().is_equal_to(());
    }

    #[test]
    fn on_success_never_retries() {
        fn successful(counter: Arc<AtomicI32>) -> Result<i32, ()> {
            counter.fetch_add(1, Ordering::SeqCst);
            Ok(42)
        }

        let counter = Arc::new(AtomicI32::new(0));

        let out = {
            retry_with_options(
                || successful(counter.clone()),
                RetryOptions {
                    delay_strategy: delay::None.take(3),
                    delay_executor: ThreadSleep,
                    _marker: PhantomData,
                },
            )
        };

        assert_that(out).is_ok().is_equal_to(42);
        assert_that(counter.load(Ordering::SeqCst))
            .with_detail_message("Function must have been called 1 time only!")
            .is_equal_to(1);
    }

    #[test]
    fn on_continuous_error_retries_expected_number_of_times() {
        fn erroneous(counter: Arc<AtomicI32>) -> Result<(), i32> {
            counter.fetch_add(1, Ordering::SeqCst);
            Err(42)
        }

        let counter = Arc::new(AtomicI32::new(0));

        let out = {
            retry_with_options(
                || erroneous(counter.clone()),
                RetryOptions {
                    delay_strategy: delay::Fixed::of(50.millis()).take(3),
                    delay_executor: ThreadSleep,
                    _marker: PhantomData,
                },
            )
        };

        assert_that(out).is_err().is_equal_to(42);
        assert_that(counter.load(Ordering::SeqCst))
            .with_subject_name("Function")
            .is_equal_to(4);
    }
}