use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RetryInfo {
pub attempts: u32,
pub max_attempts: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry_after_ms: Option<u64>,
}
impl RetryInfo {
#[must_use]
pub const fn new(max_attempts: u32) -> Self {
Self {
attempts: 0,
max_attempts,
retry_after_ms: None,
}
}
#[must_use]
pub const fn with_delay(max_attempts: u32, retry_after_ms: u64) -> Self {
Self {
attempts: 0,
max_attempts,
retry_after_ms: Some(retry_after_ms),
}
}
#[must_use]
pub const fn exhausted(&self) -> bool {
self.attempts >= self.max_attempts
}
pub fn increment(&mut self) {
self.attempts = self.attempts.saturating_add(1);
}
#[must_use]
pub const fn remaining(&self) -> u32 {
self.max_attempts.saturating_sub(self.attempts)
}
}
impl Default for RetryInfo {
fn default() -> Self {
Self::new(3) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_retry_info_creation() {
let retry_info = RetryInfo::new(5);
assert_eq!(retry_info.attempts, 0);
assert_eq!(retry_info.max_attempts, 5);
assert_eq!(retry_info.retry_after_ms, None);
}
#[test]
fn test_retry_info_with_delay() {
let retry_info = RetryInfo::with_delay(3, 1000);
assert_eq!(retry_info.attempts, 0);
assert_eq!(retry_info.max_attempts, 3);
assert_eq!(retry_info.retry_after_ms, Some(1000));
}
#[test]
fn test_retry_exhausted() {
let mut retry_info = RetryInfo::new(2);
assert!(!retry_info.exhausted());
retry_info.attempts = 1;
assert!(!retry_info.exhausted());
retry_info.attempts = 2;
assert!(retry_info.exhausted());
retry_info.attempts = 3;
assert!(retry_info.exhausted());
}
#[test]
fn test_retry_increment() {
let mut retry_info = RetryInfo::new(5);
assert_eq!(retry_info.attempts, 0);
retry_info.increment();
assert_eq!(retry_info.attempts, 1);
retry_info.increment();
assert_eq!(retry_info.attempts, 2);
}
#[test]
fn test_retry_remaining() {
let mut retry_info = RetryInfo::new(5);
assert_eq!(retry_info.remaining(), 5);
retry_info.attempts = 2;
assert_eq!(retry_info.remaining(), 3);
retry_info.attempts = 5;
assert_eq!(retry_info.remaining(), 0);
}
#[test]
fn test_retry_default() {
let retry_info = RetryInfo::default();
assert_eq!(retry_info.max_attempts, 3);
assert_eq!(retry_info.attempts, 0);
}
#[test]
fn test_retry_serialization() {
let retry_info = RetryInfo {
attempts: 2,
max_attempts: 5,
retry_after_ms: Some(1000),
};
let json = serde_json::to_string(&retry_info).unwrap();
let deserialized: RetryInfo = serde_json::from_str(&json).unwrap();
assert_eq!(retry_info, deserialized);
}
#[test]
fn test_retry_serialization_no_delay() {
let retry_info = RetryInfo::new(3);
let json = serde_json::to_string(&retry_info).unwrap();
assert!(!json.contains("retry_after_ms"));
let deserialized: RetryInfo = serde_json::from_str(&json).unwrap();
assert_eq!(retry_info, deserialized);
}
}