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
use std::time::Duration;
use std::thread;

use crate::error::Error;
use crate::backoff::Backoff;

/// Operation is an operation that can be retried if it fails.
///
/// [`Operation`]: backoff/trait.Operation.html#tymethod.next_backoff
/// [`retry`]: backoff/trait.Operation.html#tymethod.retry
/// [`retry_notify`]: backoff/trait.Operation.html#tymethod.retry_notify
///
/// Operation is an operation that can be retried if it fails.
pub trait Operation<T, E> {
    /// call_op implements the effective operation.
    fn call_op(&mut self) -> Result<T, Error<E>>;

    /// Retries this operation according to the backoff policy.
    /// backoff is reset before it is used.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use backoff::{ExponentialBackoff, Operation, Error};
    /// let mut f = || -> Result<(), Error<&str>> {
    ///     // Business logic...
    ///     // Give up.
    ///     Err(Error::Permanent("error"))
    /// };
    ///
    /// let mut backoff = ExponentialBackoff::default();
    /// let _ = f.retry(&mut backoff).err().unwrap();
    /// ```
    fn retry<B>(&mut self, backoff: &mut B) -> Result<T, Error<E>>
        where B: Backoff
    {
        let nop = |_, _| ();
        self.retry_notify(backoff, nop)
    }

    /// Retries this operation according to the backoff policy.
    /// Calls notify on failed attempts (in case of transient errors).
    /// backoff is reset before it is used.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use backoff::{Operation, Error};
    /// # use backoff::backoff::Stop;
    /// # use std::time::Duration;
    /// let notify = |err, dur| { println!("Error happened at {:?}: {}", dur, err); };
    /// let mut f = || -> Result<(), Error<&str>> {
    ///     // Business logic...
    ///     Err(Error::Transient("error"))
    /// };
    ///
    /// let mut backoff = Stop{};
    /// let _ = f.retry_notify(&mut backoff, notify).err().unwrap();
    /// ```
    fn retry_notify<B, N>(&mut self, backoff: &mut B, mut notify: N) -> Result<T, Error<E>>
        where N: Notify<E>,
              B: Backoff
    {
        backoff.reset();

        loop {
            let err = match self.call_op() {
                Ok(v) => return Ok(v),
                Err(err) => err,
            };

            let err = match err {
                Error::Permanent(err) => return Err(Error::Permanent(err)),
                Error::Transient(err) => err,
            };

            let next = match backoff.next_backoff() {
                Some(next) => next,
                None => return Err(Error::Transient(err)),
            };

            notify.notify(err, next);
            thread::sleep(next);
        }
    }
}


impl<T, E, F> Operation<T, E> for F
    where F: FnMut() -> Result<T, Error<E>>
{
    fn call_op(&mut self) -> Result<T, Error<E>> {
        self()
    }
}

/// Notify is called in [`retry_notify`](trait.Operation.html#method.retry_notify) in case of errors.
pub trait Notify<E> {
    fn notify(&mut self, err: E, duration: Duration);
}

impl<E, F> Notify<E> for F
    where F: Fn(E, Duration)
{
    fn notify(&mut self, err: E, duration: Duration) {
        self(err, duration)
    }
}