use std::time::{
Duration,
Instant,
};
use crate::event::RetryContextParts;
use crate::options::EffectiveAttemptTimeout;
use crate::{
AttemptFailure,
RetryContext,
RetryError,
RetryOptions,
};
pub(in crate::executor) struct RetryFlowState<E> {
started_at: Instant,
operation_elapsed: Duration,
attempts: u32,
last_failure: Option<AttemptFailure<E>>,
}
impl<E> RetryFlowState<E> {
pub(in crate::executor) fn new() -> Self {
Self {
started_at: Instant::now(),
operation_elapsed: Duration::ZERO,
attempts: 0,
last_failure: None,
}
}
#[inline]
pub(in crate::executor) fn total_elapsed(&self) -> Duration {
self.started_at.elapsed()
}
#[inline]
pub(in crate::executor) fn operation_elapsed(&self) -> Duration {
self.operation_elapsed
}
#[inline]
pub(in crate::executor) fn attempts(&self) -> u32 {
self.attempts
}
#[inline]
pub(in crate::executor) fn start_next_attempt(&mut self) -> u32 {
self.attempts += 1;
self.attempts
}
#[inline]
pub(in crate::executor) fn add_operation_elapsed(&mut self, attempt_elapsed: Duration) {
self.operation_elapsed = self.operation_elapsed.saturating_add(attempt_elapsed);
}
pub(in crate::executor) fn context(
&self,
options: &RetryOptions,
attempt_elapsed: Duration,
attempt_timeout: EffectiveAttemptTimeout,
) -> RetryContext {
RetryContext::from_parts(RetryContextParts {
attempt: self.attempts,
max_attempts: options.max_attempts(),
max_operation_elapsed: options.max_operation_elapsed(),
max_total_elapsed: options.max_total_elapsed(),
operation_elapsed: self.operation_elapsed,
total_elapsed: self.total_elapsed(),
attempt_elapsed,
attempt_timeout: attempt_timeout.duration(),
})
.with_attempt_timeout_source(attempt_timeout.source())
}
pub(in crate::executor) fn take_elapsed_error(
&mut self,
options: &RetryOptions,
attempt_timeout: EffectiveAttemptTimeout,
) -> Option<RetryError<E>> {
let total_elapsed = self.total_elapsed();
let reason = options.elapsed_error_reason(self.operation_elapsed, total_elapsed)?;
Some(RetryError::new(
reason,
self.take_last_failure(),
RetryContext::from_parts(RetryContextParts {
attempt: self.attempts,
max_attempts: options.max_attempts(),
max_operation_elapsed: options.max_operation_elapsed(),
max_total_elapsed: options.max_total_elapsed(),
operation_elapsed: self.operation_elapsed,
total_elapsed,
attempt_elapsed: Duration::ZERO,
attempt_timeout: attempt_timeout.duration(),
})
.with_attempt_timeout_source(attempt_timeout.source()),
))
}
#[inline]
pub(in crate::executor) fn record_last_failure(&mut self, failure: AttemptFailure<E>) {
self.last_failure = Some(failure);
}
#[inline]
pub(in crate::executor) fn take_last_failure(&mut self) -> Option<AttemptFailure<E>> {
self.last_failure.take()
}
}