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) } }