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
//
// Copyright (c) Dell Inc., or its subsidiaries. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//

use std::time::Duration;
/// The RetryResult that the operation should return.
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum RetryResult<T, E> {
    /// Contains the return value if the operation succeed.
    Success(T),
    /// Contains the error value if duration is exceeded.
    Retry(E),
    /// Contains an error value to return immediately.
    Fail(E),
}

/// An error that the Retry function would give.
#[derive(Debug, PartialEq, Eq)]
pub struct RetryError<E> {
    /// The error returned by the operation on the last try.
    pub error: E,
    /// The duration spent waiting between retries of the operation.
    pub total_delay: Duration,
    /// The total number of times the operation was tried.
    pub tries: u64,
}

///
/// Trait which is used check if the Error is Retryable.
///
pub trait Retryable {
    fn can_retry(&self) -> bool;
}

///
/// `wrap_with_async_retry!` macro wraps any arbitrary async function with `pravega_rust_client_retry::retry_async::retry_async`
/// This macro takes two parameters. The first parameter is the Retry policy which implements `trait BackoffSchedule`.
/// The second parameter is the async function that needs to be wrapped within the retry logic.
/// The function invocation will be retried only error returned by the function returns `can_retry()` as true.
///
/// E.g: usage
///
/// ```ignore
/// use pravega_rust_client_retry::retry_policy::RetryWithBackoff;
/// use pravega_rust_client_retry::retry_async::retry_async;
/// use pravega_rust_client_retry::wrap_with_async_retry;
/// //CustomError implements Retrayable trait
/// async fn function_a(param1: &str, param2:u8) -> Result<(), CustomError> {
///
/// }
/// let retry_policy = RetryWithBackoff::default().max_tries(10);
/// // the below invocation wraps function_a with the retry logic.
/// wrap_with_async_retry!(retry_policy, function_a("test", 1));
/// ```
///
#[macro_export]
macro_rules! wrap_with_async_retry {
    ($retrypolicy:expr, $expression:expr) => {
        retry_async($retrypolicy, || async {
            let r = $expression.await;
            match r {
                Ok(res) => RetryResult::Success(res),
                Err(e) => {
                    if e.can_retry() {
                        RetryResult::Retry(e)
                    } else {
                        RetryResult::Fail(e)
                    }
                }
            }
        })
        .await
    };
}

///
/// `wrap_with_sync_retry!` macro wraps any arbitrary synchronous function with `pravega_rust_client_retry::retry_sync::retry_sync`
/// This macro takes two parameters. The first parameter is the Retry policy which implements `trait BackoffSchedule`.
/// The second parameter is the synchrounous function that needs to be wrapped within the retry logic.
/// The function invocation will be retried only error returned by the function returns `can_retry()` as true.
///
/// E.g: usage
///
/// ```ignore
/// use pravega_rust_client_retry::retry_policy::RetryWithBackoff;
/// use pravega_rust_client_retry::retry_sync::retry_sync;
/// use pravega_rust_client_retry::wrap_with_sync_retry;
/// // CustomError implements Retryable trait
/// fn function_a(param1: &str, param2:u8) -> Result<(), CustomError>{
///
/// }
/// let retry_policy = RetryWithBackoff::default().max_tries(5);
/// // the below invocation wraps function_a with the retry logic.
/// wrap_with_sync_retry!(retry_policy, function_a("test", 1));
/// ```
///
#[macro_export]
macro_rules! wrap_with_sync_retry {
    ($retrypolicy:expr, $expression:expr) => {
        retry_sync($retrypolicy, || {
            let r = $expression;
            match r {
                Ok(res) => RetryResult::Success(res),
                Err(e) => {
                    if e.can_retry() {
                        RetryResult::Retry(e)
                    } else {
                        RetryResult::Fail(e)
                    }
                }
            }
        })
        .await
    };
}