redis_oxide/core/
config.rs

1//! Configuration types for Redis connections
2
3use std::time::Duration;
4
5/// Protocol version preference
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ProtocolVersion {
8    /// RESP2 (Redis Serialization Protocol version 2) - Default
9    Resp2,
10    /// RESP3 (Redis Serialization Protocol version 3) - Redis 6.0+
11    Resp3,
12}
13
14impl Default for ProtocolVersion {
15    fn default() -> Self {
16        Self::Resp2
17    }
18}
19
20/// Strategy for connection pooling
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum PoolStrategy {
23    /// Single multiplexed connection shared across tasks
24    Multiplexed,
25    /// Connection pool with multiple connections
26    Pool,
27}
28
29/// Configuration for connection pooling
30#[derive(Debug, Clone)]
31pub struct PoolConfig {
32    /// Pooling strategy to use
33    pub strategy: PoolStrategy,
34    /// Maximum number of connections in pool (only for Pool strategy)
35    pub max_size: usize,
36    /// Minimum number of connections to maintain (only for Pool strategy)
37    pub min_idle: usize,
38    /// Timeout for acquiring a connection from pool
39    pub connection_timeout: Duration,
40}
41
42impl Default for PoolConfig {
43    fn default() -> Self {
44        Self {
45            strategy: PoolStrategy::Multiplexed,
46            max_size: 10,
47            min_idle: 2,
48            connection_timeout: Duration::from_secs(5),
49        }
50    }
51}
52
53/// Topology detection mode
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum TopologyMode {
56    /// Automatically detect topology (Standalone or Cluster)
57    Auto,
58    /// Force standalone mode
59    Standalone,
60    /// Force cluster mode
61    Cluster,
62}
63
64/// Configuration for Redis connection
65#[derive(Debug, Clone)]
66pub struct ConnectionConfig {
67    /// Connection string (e.g., `<redis://localhost:6379>` or `<redis://host1:6379,host2:6379>`)
68    pub connection_string: String,
69
70    /// Optional password for authentication
71    pub password: Option<String>,
72
73    /// Database number (only for standalone mode)
74    pub database: u8,
75
76    /// Connection timeout
77    pub connect_timeout: Duration,
78
79    /// Read/write operation timeout
80    pub operation_timeout: Duration,
81
82    /// Enable TCP keepalive
83    pub tcp_keepalive: Option<Duration>,
84
85    /// Topology detection mode
86    pub topology_mode: TopologyMode,
87
88    /// Pool configuration
89    pub pool: PoolConfig,
90
91    /// Maximum number of retries for cluster redirects
92    pub max_redirects: usize,
93
94    /// Preferred protocol version
95    pub protocol_version: ProtocolVersion,
96
97    /// Sentinel configuration for high availability
98    pub sentinel: Option<crate::sentinel::SentinelConfig>,
99
100    /// Reconnection settings
101    pub reconnect: ReconnectConfig,
102}
103
104/// Configuration for reconnection behavior
105#[derive(Debug, Clone)]
106pub struct ReconnectConfig {
107    /// Enable automatic reconnection
108    pub enabled: bool,
109
110    /// Initial delay before first reconnect attempt
111    pub initial_delay: Duration,
112
113    /// Maximum delay between reconnect attempts
114    pub max_delay: Duration,
115
116    /// Backoff multiplier
117    pub backoff_multiplier: f64,
118
119    /// Maximum number of reconnect attempts (None = infinite)
120    pub max_attempts: Option<usize>,
121}
122
123impl Default for ReconnectConfig {
124    fn default() -> Self {
125        Self {
126            enabled: true,
127            initial_delay: Duration::from_millis(100),
128            max_delay: Duration::from_secs(30),
129            backoff_multiplier: 2.0,
130            max_attempts: None,
131        }
132    }
133}
134
135impl Default for ConnectionConfig {
136    fn default() -> Self {
137        Self {
138            connection_string: "redis://localhost:6379".to_string(),
139            password: None,
140            database: 0,
141            connect_timeout: Duration::from_secs(5),
142            operation_timeout: Duration::from_secs(30),
143            tcp_keepalive: Some(Duration::from_secs(60)),
144            topology_mode: TopologyMode::Auto,
145            pool: PoolConfig::default(),
146            max_redirects: 3,
147            protocol_version: ProtocolVersion::default(),
148            sentinel: None,
149            reconnect: ReconnectConfig::default(),
150        }
151    }
152}
153
154impl ConnectionConfig {
155    /// Create a new configuration with the given connection string
156    pub fn new(connection_string: impl Into<String>) -> Self {
157        Self {
158            connection_string: connection_string.into(),
159            ..Default::default()
160        }
161    }
162
163    /// Set the password for authentication
164    #[must_use]
165    pub fn with_password(mut self, password: impl Into<String>) -> Self {
166        self.password = Some(password.into());
167        self
168    }
169
170    /// Set the database number
171    #[must_use]
172    pub const fn with_database(mut self, database: u8) -> Self {
173        self.database = database;
174        self
175    }
176
177    /// Set the connection timeout
178    #[must_use]
179    pub const fn with_connect_timeout(mut self, timeout: Duration) -> Self {
180        self.connect_timeout = timeout;
181        self
182    }
183
184    /// Set the operation timeout
185    #[must_use]
186    pub const fn with_operation_timeout(mut self, timeout: Duration) -> Self {
187        self.operation_timeout = timeout;
188        self
189    }
190
191    /// Set the topology mode
192    #[must_use]
193    pub const fn with_topology_mode(mut self, mode: TopologyMode) -> Self {
194        self.topology_mode = mode;
195        self
196    }
197
198    /// Set the pool configuration
199    #[must_use]
200    pub const fn with_pool_config(mut self, pool: PoolConfig) -> Self {
201        self.pool = pool;
202        self
203    }
204
205    /// Set the maximum number of redirects
206    #[must_use]
207    pub const fn with_max_redirects(mut self, max: usize) -> Self {
208        self.max_redirects = max;
209        self
210    }
211
212    /// Set the preferred protocol version
213    #[must_use]
214    pub const fn with_protocol_version(mut self, version: ProtocolVersion) -> Self {
215        self.protocol_version = version;
216        self
217    }
218
219    /// Parse connection endpoints from connection string
220    #[must_use]
221    pub fn parse_endpoints(&self) -> Vec<(String, u16)> {
222        let conn_str = self.connection_string.trim();
223
224        // Strip redis:// prefix if present
225        let addr_part = conn_str
226            .strip_prefix("redis://")
227            .unwrap_or(conn_str)
228            .strip_prefix("rediss://")
229            .unwrap_or_else(|| conn_str.strip_prefix("redis://").unwrap_or(conn_str));
230
231        // Split by comma for multiple endpoints
232        addr_part
233            .split(',')
234            .filter_map(|endpoint| {
235                let endpoint = endpoint.trim();
236                if endpoint.is_empty() {
237                    return None;
238                }
239
240                // Parse host:port
241                if let Some((host, port_str)) = endpoint.rsplit_once(':') {
242                    if let Ok(port) = port_str.parse::<u16>() {
243                        return Some((host.to_string(), port));
244                    }
245                }
246
247                // Default port 6379 if not specified
248                Some((endpoint.to_string(), 6379))
249            })
250            .collect()
251    }
252}