1use crate::types::{
6 CacheCapacity, HostName, MaxConnections, MaxErrors, Port, ServerName, duration_serde,
7 option_duration_serde,
8};
9use serde::{Deserialize, Serialize};
10use std::time::Duration;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum RoutingMode {
16 Standard,
18 PerCommand,
20 Hybrid,
22}
23
24impl Default for RoutingMode {
25 fn default() -> Self {
29 Self::Hybrid
30 }
31}
32
33impl RoutingMode {
34 #[must_use]
36 pub fn supports_per_command_routing(&self) -> bool {
37 matches!(self, Self::PerCommand | Self::Hybrid)
38 }
39
40 #[must_use]
42 pub fn supports_stateful_commands(&self) -> bool {
43 matches!(self, Self::Standard | Self::Hybrid)
44 }
45}
46
47#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
49pub struct Config {
50 #[serde(default)]
52 pub servers: Vec<ServerConfig>,
53 #[serde(default)]
55 pub health_check: HealthCheckConfig,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub cache: Option<CacheConfig>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
63pub struct CacheConfig {
64 #[serde(default = "super::defaults::cache_max_capacity")]
66 pub max_capacity: CacheCapacity,
67 #[serde(with = "duration_serde", default = "super::defaults::cache_ttl")]
69 pub ttl: Duration,
70}
71
72impl Default for CacheConfig {
73 fn default() -> Self {
74 Self {
75 max_capacity: super::defaults::cache_max_capacity(),
76 ttl: super::defaults::cache_ttl(),
77 }
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
83pub struct HealthCheckConfig {
84 #[serde(
86 with = "duration_serde",
87 default = "super::defaults::health_check_interval"
88 )]
89 pub interval: Duration,
90 #[serde(
92 with = "duration_serde",
93 default = "super::defaults::health_check_timeout"
94 )]
95 pub timeout: Duration,
96 #[serde(default = "super::defaults::unhealthy_threshold")]
98 pub unhealthy_threshold: MaxErrors,
99}
100
101impl Default for HealthCheckConfig {
102 fn default() -> Self {
103 Self {
104 interval: super::defaults::health_check_interval(),
105 timeout: super::defaults::health_check_timeout(),
106 unhealthy_threshold: super::defaults::unhealthy_threshold(),
107 }
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
113pub struct ServerConfig {
114 pub host: HostName,
115 pub port: Port,
116 pub name: ServerName,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub username: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub password: Option<String>,
121 #[serde(default = "super::defaults::max_connections")]
123 pub max_connections: MaxConnections,
124
125 #[serde(default)]
127 pub use_tls: bool,
128 #[serde(default = "super::defaults::tls_verify_cert")]
130 pub tls_verify_cert: bool,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub tls_cert_path: Option<String>,
134 #[serde(
137 with = "option_duration_serde",
138 default,
139 skip_serializing_if = "Option::is_none"
140 )]
141 pub connection_keepalive: Option<Duration>,
142 #[serde(default = "super::defaults::health_check_max_per_cycle")]
145 pub health_check_max_per_cycle: usize,
146 #[serde(
149 with = "duration_serde",
150 default = "super::defaults::health_check_pool_timeout"
151 )]
152 pub health_check_pool_timeout: Duration,
153}
154
155pub struct ServerConfigBuilder {
181 host: String,
182 port: u16,
183 name: Option<String>,
184 username: Option<String>,
185 password: Option<String>,
186 max_connections: Option<usize>,
187 use_tls: bool,
188 tls_verify_cert: bool,
189 tls_cert_path: Option<String>,
190 connection_keepalive: Option<Duration>,
191 health_check_max_per_cycle: Option<usize>,
192 health_check_pool_timeout: Option<Duration>,
193}
194
195impl ServerConfigBuilder {
196 #[must_use]
202 pub fn new(host: impl Into<String>, port: u16) -> Self {
203 Self {
204 host: host.into(),
205 port,
206 name: None,
207 username: None,
208 password: None,
209 max_connections: None,
210 use_tls: false,
211 tls_verify_cert: true, tls_cert_path: None,
213 connection_keepalive: None,
214 health_check_max_per_cycle: None,
215 health_check_pool_timeout: None,
216 }
217 }
218
219 #[must_use]
221 pub fn name(mut self, name: impl Into<String>) -> Self {
222 self.name = Some(name.into());
223 self
224 }
225
226 #[must_use]
228 pub fn username(mut self, username: impl Into<String>) -> Self {
229 self.username = Some(username.into());
230 self
231 }
232
233 #[must_use]
235 pub fn password(mut self, password: impl Into<String>) -> Self {
236 self.password = Some(password.into());
237 self
238 }
239
240 #[must_use]
242 pub fn max_connections(mut self, max: usize) -> Self {
243 self.max_connections = Some(max);
244 self
245 }
246
247 #[must_use]
249 pub fn use_tls(mut self, enabled: bool) -> Self {
250 self.use_tls = enabled;
251 self
252 }
253
254 #[must_use]
256 pub fn tls_verify_cert(mut self, verify: bool) -> Self {
257 self.tls_verify_cert = verify;
258 self
259 }
260
261 #[must_use]
263 pub fn tls_cert_path(mut self, path: impl Into<String>) -> Self {
264 self.tls_cert_path = Some(path.into());
265 self
266 }
267
268 #[must_use]
270 pub fn connection_keepalive(mut self, interval: Duration) -> Self {
271 self.connection_keepalive = Some(interval);
272 self
273 }
274
275 #[must_use]
277 pub fn health_check_max_per_cycle(mut self, max: usize) -> Self {
278 self.health_check_max_per_cycle = Some(max);
279 self
280 }
281
282 #[must_use]
284 pub fn health_check_pool_timeout(mut self, timeout: Duration) -> Self {
285 self.health_check_pool_timeout = Some(timeout);
286 self
287 }
288
289 pub fn build(self) -> Result<ServerConfig, anyhow::Error> {
299 use crate::types::{HostName, MaxConnections, Port, ServerName};
300
301 let host = HostName::new(self.host.clone())?;
302
303 let port = Port::new(self.port)
304 .ok_or_else(|| anyhow::anyhow!("Invalid port: {} (must be 1-65535)", self.port))?;
305
306 let name_str = self
307 .name
308 .unwrap_or_else(|| format!("{}:{}", self.host, self.port));
309 let name = ServerName::new(name_str)?;
310
311 let max_connections = if let Some(max) = self.max_connections {
312 MaxConnections::new(max)
313 .ok_or_else(|| anyhow::anyhow!("Invalid max_connections: {} (must be > 0)", max))?
314 } else {
315 super::defaults::max_connections()
316 };
317
318 let health_check_max_per_cycle = self
319 .health_check_max_per_cycle
320 .unwrap_or_else(super::defaults::health_check_max_per_cycle);
321
322 let health_check_pool_timeout = self
323 .health_check_pool_timeout
324 .unwrap_or_else(super::defaults::health_check_pool_timeout);
325
326 Ok(ServerConfig {
327 host,
328 port,
329 name,
330 username: self.username,
331 password: self.password,
332 max_connections,
333 use_tls: self.use_tls,
334 tls_verify_cert: self.tls_verify_cert,
335 tls_cert_path: self.tls_cert_path,
336 connection_keepalive: self.connection_keepalive,
337 health_check_max_per_cycle,
338 health_check_pool_timeout,
339 })
340 }
341}
342
343impl ServerConfig {
344 #[must_use]
358 pub fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
359 ServerConfigBuilder::new(host, port)
360 }
361}