backoff/
error.rs

1use std::error;
2use std::fmt;
3
4use instant::Duration;
5
6/// Error is the error value in an operation's
7/// result.
8///
9/// Based on the two possible values, the operation
10/// may be retried.
11pub enum Error<E> {
12    /// Permanent means that it's impossible to execute the operation
13    /// successfully. This error is immediately returned from `retry()`.
14    Permanent(E),
15
16    /// Transient means that the error is temporary. If the `retry_after` is `None`
17    /// the operation should be retried according to the backoff policy, else after
18    /// the specified duration. Useful for handling ratelimits like a HTTP 429 response.
19    Transient {
20        err: E,
21        retry_after: Option<Duration>,
22    },
23}
24
25impl<E> Error<E> {
26    // Creates an permanent error.
27    pub fn permanent(err: E) -> Self {
28        Error::Permanent(err)
29    }
30
31    // Creates an transient error which is retried according to the backoff
32    // policy.
33    pub fn transient(err: E) -> Self {
34        Error::Transient {
35            err,
36            retry_after: None,
37        }
38    }
39
40    /// Creates a transient error which is retried after the specified duration.
41    /// Useful for handling ratelimits like a HTTP 429 response.
42    pub fn retry_after(err: E, duration: Duration) -> Self {
43        Error::Transient {
44            err,
45            retry_after: Some(duration),
46        }
47    }
48}
49
50impl<E> fmt::Display for Error<E>
51where
52    E: fmt::Display,
53{
54    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
55        match *self {
56            Error::Permanent(ref err)
57            | Error::Transient {
58                ref err,
59                retry_after: _,
60            } => err.fmt(f),
61        }
62    }
63}
64
65impl<E> fmt::Debug for Error<E>
66where
67    E: fmt::Debug,
68{
69    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
70        let (name, err) = match *self {
71            Error::Permanent(ref err) => ("Permanent", err as &dyn fmt::Debug),
72            Error::Transient {
73                ref err,
74                retry_after: _,
75            } => ("Transient", err as &dyn fmt::Debug),
76        };
77        f.debug_tuple(name).field(err).finish()
78    }
79}
80
81impl<E> error::Error for Error<E>
82where
83    E: error::Error,
84{
85    fn description(&self) -> &str {
86        match *self {
87            Error::Permanent(_) => "permanent error",
88            Error::Transient { .. } => "transient error",
89        }
90    }
91
92    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
93        match *self {
94            Error::Permanent(ref err)
95            | Error::Transient {
96                ref err,
97                retry_after: _,
98            } => err.source(),
99        }
100    }
101
102    fn cause(&self) -> Option<&dyn error::Error> {
103        self.source()
104    }
105}
106
107/// By default all errors are transient. Permanent errors can
108/// be constructed explicitly. This implementation is for making
109/// the question mark operator (?) and the `try!` macro to work.
110impl<E> From<E> for Error<E> {
111    fn from(err: E) -> Error<E> {
112        Error::Transient {
113            err,
114            retry_after: None,
115        }
116    }
117}
118
119impl<E> PartialEq for Error<E>
120where
121    E: PartialEq,
122{
123    fn eq(&self, other: &Self) -> bool {
124        match (self, other) {
125            (Error::Permanent(ref self_err), Error::Permanent(ref other_err)) => {
126                self_err == other_err
127            }
128            (
129                Error::Transient {
130                    err: self_err,
131                    retry_after: self_retry_after,
132                },
133                Error::Transient {
134                    err: other_err,
135                    retry_after: other_retry_after,
136                },
137            ) => self_err == other_err && self_retry_after == other_retry_after,
138            _ => false,
139        }
140    }
141}
142
143#[test]
144fn create_permanent_error() {
145    let e = Error::permanent("err");
146    assert_eq!(e, Error::Permanent("err"));
147}
148
149#[test]
150fn create_transient_error() {
151    let e = Error::transient("err");
152    assert_eq!(
153        e,
154        Error::Transient {
155            err: "err",
156            retry_after: None
157        }
158    );
159}
160
161#[test]
162fn create_transient_error_with_retry_after() {
163    let retry_after = Duration::from_secs(42);
164    let e = Error::retry_after("err", retry_after);
165    assert_eq!(
166        e,
167        Error::Transient {
168            err: "err",
169            retry_after: Some(retry_after),
170        }
171    );
172}