maybe_backoff/
error.rs

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