use std::time::Duration;
use qubit_common::BoxError;
use qubit_function::{ArcBiFunction, BiFunction, BiPredicate};
use crate::event::RetryListeners;
use crate::{
RetryAbortContext, RetryAbortListener, RetryAttemptContext, RetryAttemptFailure,
RetryConfigError, RetryContext, RetryDecision, RetryDelay, RetryFailureContext,
RetryFailureListener, RetryJitter, RetryListener, RetryOptions, RetrySuccessContext,
RetrySuccessListener,
};
use crate::error::RetryErrorClassifier;
use crate::retry_executor::RetryExecutor;
pub struct RetryExecutorBuilder<E = BoxError> {
options: RetryOptions,
classifier: Option<RetryErrorClassifier<E>>,
listeners: RetryListeners<E>,
max_attempts_error: Option<RetryConfigError>,
}
impl<E> RetryExecutorBuilder<E> {
#[inline]
pub fn new() -> Self {
Self {
options: RetryOptions::default(),
classifier: None,
listeners: RetryListeners::default(),
max_attempts_error: None,
}
}
#[inline]
pub fn options(mut self, options: RetryOptions) -> Self {
self.options = options;
self.max_attempts_error = None;
self
}
#[inline]
pub fn max_attempts(mut self, max_attempts: u32) -> Self {
if let Some(max_attempts) = std::num::NonZeroU32::new(max_attempts) {
self.options.max_attempts = max_attempts;
self.max_attempts_error = None;
} else {
self.max_attempts_error = Some(RetryConfigError::invalid_value(
RetryOptions::KEY_MAX_ATTEMPTS,
"max_attempts must be greater than zero",
));
}
self
}
#[inline]
pub fn max_elapsed(mut self, max_elapsed: Option<Duration>) -> Self {
self.options.max_elapsed = max_elapsed;
self
}
#[inline]
pub fn delay(mut self, delay: RetryDelay) -> Self {
self.options.delay = delay;
self
}
#[inline]
pub fn jitter(mut self, jitter: RetryJitter) -> Self {
self.options.jitter = jitter;
self
}
#[inline]
pub fn jitter_factor(self, factor: f64) -> Self {
self.jitter(RetryJitter::Factor(factor))
}
pub fn retry_if<P>(mut self, retry_tester: P) -> Self
where
P: BiPredicate<E, RetryAttemptContext> + Send + Sync + 'static,
{
self.classifier = Some(ArcBiFunction::new(move |error, context| {
if retry_tester.test(error, context) {
RetryDecision::Retry
} else {
RetryDecision::Abort
}
}));
self
}
pub fn retry_decide<B>(mut self, decider: B) -> Self
where
B: BiFunction<E, RetryAttemptContext, RetryDecision> + Send + Sync + 'static,
{
self.classifier = Some(decider.into_arc());
self
}
pub fn on_retry<F>(mut self, listener: F) -> Self
where
F: Fn(&RetryContext, &RetryAttemptFailure<E>) + Send + Sync + 'static,
{
self.listeners.retry = Some(RetryListener::new(listener));
self
}
pub fn on_success<F>(mut self, listener: F) -> Self
where
F: Fn(&RetrySuccessContext) + Send + Sync + 'static,
{
self.listeners.success = Some(RetrySuccessListener::new(listener));
self
}
pub fn on_failure<F>(mut self, listener: F) -> Self
where
F: Fn(&RetryFailureContext, &Option<RetryAttemptFailure<E>>) + Send + Sync + 'static,
{
self.listeners.failure = Some(RetryFailureListener::new(listener));
self
}
pub fn on_abort<F>(mut self, listener: F) -> Self
where
F: Fn(&RetryAbortContext, &RetryAttemptFailure<E>) + Send + Sync + 'static,
{
self.listeners.abort = Some(RetryAbortListener::new(listener));
self
}
pub fn build(self) -> Result<RetryExecutor<E>, RetryConfigError> {
if let Some(error) = self.max_attempts_error {
return Err(error);
}
self.options.validate()?;
let classifier = self
.classifier
.unwrap_or_else(|| ArcBiFunction::constant(RetryDecision::Retry));
Ok(RetryExecutor::new(self.options, classifier, self.listeners))
}
}
impl<E> Default for RetryExecutorBuilder<E> {
#[inline]
fn default() -> Self {
Self::new()
}
}