use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolVersion {
Resp2,
Resp3,
}
impl Default for ProtocolVersion {
fn default() -> Self {
Self::Resp2
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PoolStrategy {
Multiplexed,
Pool,
}
#[derive(Debug, Clone)]
pub struct PoolConfig {
pub strategy: PoolStrategy,
pub max_size: usize,
pub min_idle: usize,
pub connection_timeout: Duration,
}
impl Default for PoolConfig {
fn default() -> Self {
Self {
strategy: PoolStrategy::Multiplexed,
max_size: 10,
min_idle: 2,
connection_timeout: Duration::from_secs(5),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TopologyMode {
Auto,
Standalone,
Cluster,
}
#[derive(Debug, Clone)]
pub struct ConnectionConfig {
pub connection_string: String,
pub password: Option<String>,
pub database: u8,
pub connect_timeout: Duration,
pub operation_timeout: Duration,
pub tcp_keepalive: Option<Duration>,
pub topology_mode: TopologyMode,
pub pool: PoolConfig,
pub max_redirects: usize,
pub protocol_version: ProtocolVersion,
pub sentinel: Option<crate::sentinel::SentinelConfig>,
pub reconnect: ReconnectConfig,
}
#[derive(Debug, Clone)]
pub struct ReconnectConfig {
pub enabled: bool,
pub initial_delay: Duration,
pub max_delay: Duration,
pub backoff_multiplier: f64,
pub max_attempts: Option<usize>,
}
impl Default for ReconnectConfig {
fn default() -> Self {
Self {
enabled: true,
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
backoff_multiplier: 2.0,
max_attempts: None,
}
}
}
impl Default for ConnectionConfig {
fn default() -> Self {
Self {
connection_string: "redis://localhost:6379".to_string(),
password: None,
database: 0,
connect_timeout: Duration::from_secs(5),
operation_timeout: Duration::from_secs(30),
tcp_keepalive: Some(Duration::from_secs(60)),
topology_mode: TopologyMode::Auto,
pool: PoolConfig::default(),
max_redirects: 3,
protocol_version: ProtocolVersion::default(),
sentinel: None,
reconnect: ReconnectConfig::default(),
}
}
}
impl ConnectionConfig {
pub fn new(connection_string: impl Into<String>) -> Self {
Self {
connection_string: connection_string.into(),
..Default::default()
}
}
#[must_use]
pub fn with_password(mut self, password: impl Into<String>) -> Self {
self.password = Some(password.into());
self
}
#[must_use]
pub const fn with_database(mut self, database: u8) -> Self {
self.database = database;
self
}
#[must_use]
pub const fn with_connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
#[must_use]
pub const fn with_operation_timeout(mut self, timeout: Duration) -> Self {
self.operation_timeout = timeout;
self
}
#[must_use]
pub const fn with_topology_mode(mut self, mode: TopologyMode) -> Self {
self.topology_mode = mode;
self
}
#[must_use]
pub const fn with_pool_config(mut self, pool: PoolConfig) -> Self {
self.pool = pool;
self
}
#[must_use]
pub const fn with_max_redirects(mut self, max: usize) -> Self {
self.max_redirects = max;
self
}
#[must_use]
pub const fn with_protocol_version(mut self, version: ProtocolVersion) -> Self {
self.protocol_version = version;
self
}
#[must_use]
pub fn parse_endpoints(&self) -> Vec<(String, u16)> {
let conn_str = self.connection_string.trim();
let addr_part = conn_str
.strip_prefix("redis://")
.unwrap_or(conn_str)
.strip_prefix("rediss://")
.unwrap_or_else(|| conn_str.strip_prefix("redis://").unwrap_or(conn_str));
addr_part
.split(',')
.filter_map(|endpoint| {
let endpoint = endpoint.trim();
if endpoint.is_empty() {
return None;
}
if let Some((host, port_str)) = endpoint.rsplit_once(':') {
if let Ok(port) = port_str.parse::<u16>() {
return Some((host.to_string(), port));
}
}
Some((endpoint.to_string(), 6379))
})
.collect()
}
}