use std::{collections::VecDeque, fmt::Debug, time::Duration};
pub trait RetryStrategy: Debug + Send + Sync {
fn max_retries(&self) -> usize;
fn next_backoff(&mut self) -> Option<Duration>;
}
#[derive(Debug, Clone)]
pub enum SupervisionStrategy {
Stop,
Retry(Strategy),
}
#[derive(Debug, Clone)]
pub enum Strategy {
NoInterval(NoIntervalStrategy),
FixedInterval(FixedIntervalStrategy),
CustomIntervalStrategy(CustomIntervalStrategy),
}
impl RetryStrategy for Strategy {
fn max_retries(&self) -> usize {
match self {
Self::NoInterval(strategy) => strategy.max_retries(),
Self::FixedInterval(strategy) => strategy.max_retries(),
Self::CustomIntervalStrategy(strategy) => strategy.max_retries(),
}
}
fn next_backoff(&mut self) -> Option<Duration> {
match self {
Self::NoInterval(strategy) => strategy.next_backoff(),
Self::FixedInterval(strategy) => strategy.next_backoff(),
Self::CustomIntervalStrategy(strategy) => strategy.next_backoff(),
}
}
}
impl Default for Strategy {
fn default() -> Self {
Self::NoInterval(NoIntervalStrategy::default())
}
}
#[derive(Debug, Default, Clone)]
pub struct NoIntervalStrategy {
max_retries: usize,
}
impl NoIntervalStrategy {
pub const fn new(max_retries: usize) -> Self {
Self { max_retries }
}
}
impl RetryStrategy for NoIntervalStrategy {
fn max_retries(&self) -> usize {
self.max_retries
}
fn next_backoff(&mut self) -> Option<Duration> {
None
}
}
#[derive(Debug, Default, Clone)]
pub struct FixedIntervalStrategy {
max_retries: usize,
duration: Duration,
}
impl FixedIntervalStrategy {
pub const fn new(max_retries: usize, duration: Duration) -> Self {
Self {
max_retries,
duration,
}
}
}
impl RetryStrategy for FixedIntervalStrategy {
fn max_retries(&self) -> usize {
self.max_retries
}
fn next_backoff(&mut self) -> Option<Duration> {
Some(self.duration)
}
}
#[derive(Debug, Default, Clone)]
pub struct CustomIntervalStrategy {
durations: VecDeque<Duration>,
max_retries: usize,
}
impl CustomIntervalStrategy {
pub fn new(durations: VecDeque<Duration>) -> Self {
let max_retries = durations.len();
Self {
durations,
max_retries,
}
}
}
impl RetryStrategy for CustomIntervalStrategy {
fn max_retries(&self) -> usize {
self.max_retries
}
fn next_backoff(&mut self) -> Option<Duration> {
self.durations.pop_front()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_no_interval_strategy() {
let mut strategy = NoIntervalStrategy::new(3);
assert_eq!(strategy.max_retries(), 3);
assert_eq!(strategy.next_backoff(), None);
}
#[test]
fn test_fixed_interval_strategy() {
let mut strategy =
FixedIntervalStrategy::new(3, Duration::from_secs(1));
assert_eq!(strategy.max_retries(), 3);
assert_eq!(strategy.next_backoff(), Some(Duration::from_secs(1)));
}
#[test]
fn test_exponential_custom_strategy() {
let mut strategy = CustomIntervalStrategy::new(VecDeque::from([
Duration::from_secs(1),
Duration::from_secs(2),
Duration::from_secs(3),
]));
assert_eq!(strategy.max_retries(), 3);
assert!(strategy.next_backoff().is_some());
assert!(strategy.next_backoff().is_some());
assert!(strategy.next_backoff().is_some());
assert!(strategy.next_backoff().is_none());
}
}