use serde::{Deserialize, Serialize};
use std::error::Error;
use std::fmt;
use crate::{AttemptFailure, RetryContext, RetryErrorReason};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(
serialize = "E: serde::Serialize",
deserialize = "E: serde::de::DeserializeOwned"
))]
pub struct RetryError<E> {
reason: RetryErrorReason,
last_failure: Option<AttemptFailure<E>>,
context: RetryContext,
}
impl<E> RetryError<E> {
#[inline]
pub(crate) fn new(
reason: RetryErrorReason,
last_failure: Option<AttemptFailure<E>>,
context: RetryContext,
) -> Self {
Self {
reason,
last_failure,
context,
}
}
#[inline]
pub fn reason(&self) -> RetryErrorReason {
self.reason
}
#[inline]
pub fn context(&self) -> &RetryContext {
&self.context
}
#[inline]
pub fn attempts(&self) -> u32 {
self.context.attempt()
}
#[inline]
pub fn last_failure(&self) -> Option<&AttemptFailure<E>> {
self.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> {
self.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 {
let attempts = self.attempts();
match self.reason {
RetryErrorReason::Aborted => write!(f, "retry aborted after {attempts} attempt(s)")?,
RetryErrorReason::AttemptsExceeded => write!(
f,
"retry attempts exceeded after {attempts} attempt(s), max {}",
self.context.max_attempts()
)?,
RetryErrorReason::MaxElapsedExceeded => {
write!(f, "retry max elapsed exceeded after {attempts} attempt(s)")?
}
}
if let Some(failure) = &self.last_failure {
write!(f, "; last failure: {failure}")?;
}
Ok(())
}
}
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))
}
}