adventure/retry/
error.rs

1use core::convert::Infallible;
2use core::fmt::{self, Display};
3
4#[cfg(feature = "std")]
5use std::error::Error as StdError;
6
7/// Errors encountered by the retrial operation.
8#[derive(Debug)]
9pub struct RetryError<E = Infallible> {
10    inner: RetryErrorKind<E>,
11}
12
13#[derive(Debug)]
14enum RetryErrorKind<E> {
15    Aborted(E),
16    Timeout,
17    #[allow(dead_code)]
18    TimerShutdown,
19}
20
21impl<E> From<Infallible> for RetryError<E> {
22    fn from(e: Infallible) -> Self {
23        match e {}
24    }
25}
26
27impl<E: Display> Display for RetryError<E> {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        use RetryErrorKind::*;
30        match &self.inner {
31            Aborted(e) => e.fmt(f),
32            Timeout => "Timeout reached".fmt(f),
33            TimerShutdown => "Timer has gone".fmt(f),
34        }
35    }
36}
37
38#[cfg(feature = "std")]
39impl<E: StdError + 'static> StdError for RetryError<E> {
40    fn source(&self) -> Option<&(dyn StdError + 'static)> {
41        use RetryErrorKind::*;
42        match &self.inner {
43            Aborted(e) => Some(&*e),
44            _ => None,
45        }
46    }
47}
48
49impl<E> RetryError<E> {
50    pub fn from_err(e: E) -> Self {
51        RetryError {
52            inner: RetryErrorKind::Aborted(e),
53        }
54    }
55
56    pub(crate) const fn timeout() -> Self {
57        RetryError {
58            inner: RetryErrorKind::Timeout,
59        }
60    }
61
62    #[allow(dead_code)]
63    pub(crate) const fn shutdown() -> Self {
64        RetryError {
65            inner: RetryErrorKind::TimerShutdown,
66        }
67    }
68
69    pub fn as_inner(&self) -> Option<&E> {
70        if let RetryErrorKind::Aborted(e) = &self.inner {
71            Some(e)
72        } else {
73            None
74        }
75    }
76
77    pub fn into_inner(self) -> Option<E> {
78        if let RetryErrorKind::Aborted(e) = self.inner {
79            Some(e)
80        } else {
81            None
82        }
83    }
84
85    /// Returns `true` if the error was caused by the retrial has aborted.
86    pub fn is_aborted(&self) -> bool {
87        self.as_inner().is_some()
88    }
89
90    /// Returns `true` if the error was caused by the operation timed out.
91    pub fn is_timeout(&self) -> bool {
92        if let RetryErrorKind::Timeout = &self.inner {
93            true
94        } else {
95            false
96        }
97    }
98
99    /// Returns `true` if the error was caused by the timer begin shutdown.
100    ///
101    /// This is related the internal state of the timer implementation,
102    /// meaning the operation will never be able to complete. This is a
103    /// permanent error, this is, once this error is observed, retries will
104    /// never succeed in the future.
105    pub fn is_shutdown(&self) -> bool {
106        if let RetryErrorKind::TimerShutdown = &self.inner {
107            true
108        } else {
109            false
110        }
111    }
112}
113
114impl RetryError {
115    pub(crate) fn transform<E>(self) -> RetryError<E> {
116        use RetryErrorKind::*;
117        let inner = match self.inner {
118            Aborted(_) => unreachable!(),
119            Timeout => Timeout,
120            TimerShutdown => TimerShutdown,
121        };
122        RetryError { inner }
123    }
124}