use std::error::Error;
use std::fmt;
use std::time::Duration;
use super::AttemptFailure;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RetryError<E> {
Aborted {
attempts: u32,
elapsed: Duration,
failure: AttemptFailure<E>,
},
AttemptsExceeded {
attempts: u32,
max_attempts: u32,
elapsed: Duration,
last_failure: AttemptFailure<E>,
},
MaxElapsedExceeded {
attempts: u32,
elapsed: Duration,
max_elapsed: Duration,
last_failure: Option<AttemptFailure<E>>,
},
}
impl<E> RetryError<E> {
#[inline]
pub fn attempts(&self) -> u32 {
match self {
Self::Aborted { attempts, .. }
| Self::AttemptsExceeded { attempts, .. }
| Self::MaxElapsedExceeded { attempts, .. } => *attempts,
}
}
#[inline]
pub fn elapsed(&self) -> Duration {
match self {
Self::Aborted { elapsed, .. }
| Self::AttemptsExceeded { elapsed, .. }
| Self::MaxElapsedExceeded { elapsed, .. } => *elapsed,
}
}
#[inline]
pub fn last_failure(&self) -> Option<&AttemptFailure<E>> {
match self {
Self::Aborted { failure, .. } => Some(failure),
Self::AttemptsExceeded { last_failure, .. } => Some(last_failure),
Self::MaxElapsedExceeded { last_failure, .. } => last_failure.as_ref(),
}
}
#[inline]
pub fn last_error(&self) -> Option<&E> {
self.last_failure().and_then(AttemptFailure::as_error)
}
#[inline]
pub fn into_last_error(self) -> Option<E> {
match self {
Self::Aborted { failure, .. } => failure.into_error(),
Self::AttemptsExceeded { last_failure, .. } => last_failure.into_error(),
Self::MaxElapsedExceeded { last_failure, .. } => {
last_failure.and_then(AttemptFailure::into_error)
}
}
}
}
impl<E> fmt::Display for RetryError<E>
where
E: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Aborted {
attempts,
failure,
..
} => write!(
f,
"retry aborted after {attempts} attempt(s); failure: {failure}"
),
Self::AttemptsExceeded {
attempts,
max_attempts,
last_failure,
..
} => write!(
f,
"retry attempts exceeded: {attempts} attempt(s), max {max_attempts}; last failure: {last_failure}"
),
Self::MaxElapsedExceeded {
attempts,
max_elapsed,
last_failure,
..
} => {
if let Some(failure) = last_failure {
write!(
f,
"retry max elapsed exceeded after {attempts} attempt(s); max {max_elapsed:?}; last failure: {failure}"
)
} else {
write!(
f,
"retry max elapsed exceeded after {attempts} attempt(s); max {max_elapsed:?}"
)
}
}
}
}
}
impl<E> Error for RetryError<E>
where
E: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.last_error()
.map(|error| error as &(dyn Error + 'static))
}
}