use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ApplicationIntent {
#[default]
ReadWrite,
ReadOnly,
}
#[derive(Debug, Clone)]
pub struct RedirectConfig {
pub max_redirects: u8,
pub follow_redirects: bool,
}
impl Default for RedirectConfig {
fn default() -> Self {
Self {
max_redirects: 2,
follow_redirects: true,
}
}
}
impl RedirectConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn max_redirects(mut self, max: u8) -> Self {
self.max_redirects = max;
self
}
#[must_use]
pub fn follow_redirects(mut self, follow: bool) -> Self {
self.follow_redirects = follow;
self
}
#[must_use]
pub fn no_follow() -> Self {
Self {
max_redirects: 0,
follow_redirects: false,
}
}
}
#[derive(Debug, Clone)]
pub struct TimeoutConfig {
pub connect_timeout: Duration,
pub tls_timeout: Duration,
pub login_timeout: Duration,
pub command_timeout: Duration,
pub idle_timeout: Duration,
pub keepalive_interval: Option<Duration>,
}
impl Default for TimeoutConfig {
fn default() -> Self {
Self {
connect_timeout: Duration::from_secs(15),
tls_timeout: Duration::from_secs(10),
login_timeout: Duration::from_secs(30),
command_timeout: Duration::from_secs(30),
idle_timeout: Duration::from_secs(300),
keepalive_interval: Some(Duration::from_secs(30)),
}
}
}
impl TimeoutConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
#[must_use]
pub fn tls_timeout(mut self, timeout: Duration) -> Self {
self.tls_timeout = timeout;
self
}
#[must_use]
pub fn login_timeout(mut self, timeout: Duration) -> Self {
self.login_timeout = timeout;
self
}
#[must_use]
pub fn command_timeout(mut self, timeout: Duration) -> Self {
self.command_timeout = timeout;
self
}
#[must_use]
pub fn idle_timeout(mut self, timeout: Duration) -> Self {
self.idle_timeout = timeout;
self
}
#[must_use]
pub fn keepalive_interval(mut self, interval: Option<Duration>) -> Self {
self.keepalive_interval = interval;
self
}
#[must_use]
pub fn no_keepalive(mut self) -> Self {
self.keepalive_interval = None;
self
}
#[must_use]
pub fn total_connect_timeout(&self) -> Duration {
self.connect_timeout + self.tls_timeout + self.login_timeout
}
}
#[derive(Debug, Clone)]
pub struct RetryPolicy {
pub max_retries: u32,
pub initial_backoff: Duration,
pub max_backoff: Duration,
pub backoff_multiplier: f64,
pub jitter: bool,
}
impl Default for RetryPolicy {
fn default() -> Self {
Self {
max_retries: 3,
initial_backoff: Duration::from_millis(100),
max_backoff: Duration::from_secs(30),
backoff_multiplier: 2.0,
jitter: true,
}
}
}
impl RetryPolicy {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn max_retries(mut self, max: u32) -> Self {
self.max_retries = max;
self
}
#[must_use]
pub fn initial_backoff(mut self, backoff: Duration) -> Self {
self.initial_backoff = backoff;
self
}
#[must_use]
pub fn max_backoff(mut self, backoff: Duration) -> Self {
self.max_backoff = backoff;
self
}
#[must_use]
pub fn backoff_multiplier(mut self, multiplier: f64) -> Self {
self.backoff_multiplier = multiplier;
self
}
#[must_use]
pub fn jitter(mut self, enabled: bool) -> Self {
self.jitter = enabled;
self
}
#[must_use]
pub fn no_retry() -> Self {
Self {
max_retries: 0,
..Self::default()
}
}
#[must_use]
pub fn backoff_for_attempt(&self, attempt: u32) -> Duration {
if attempt == 0 {
return Duration::ZERO;
}
let base = self.initial_backoff.as_millis() as f64
* self
.backoff_multiplier
.powi(attempt.saturating_sub(1) as i32);
let capped = base.min(self.max_backoff.as_millis() as f64);
if self.jitter {
Duration::from_millis(capped as u64)
} else {
Duration::from_millis(capped as u64)
}
}
#[must_use]
pub fn should_retry(&self, attempt: u32) -> bool {
attempt < self.max_retries
}
}