1use serde::{Deserialize, Serialize};
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
42pub enum ConnectionStatus {
43 Connected,
45
46 Disconnected,
48
49 Degraded,
51
52 Failed,
54}
55
56impl ConnectionStatus {
57 pub fn is_connected(&self) -> bool { matches!(self, ConnectionStatus::Connected) }
59
60 pub fn has_issues(&self) -> bool { matches!(self, ConnectionStatus::Degraded | ConnectionStatus::Failed) }
62
63 pub fn description(&self) -> &'static str {
65 match self {
66 ConnectionStatus::Connected => "Connected and healthy",
67 ConnectionStatus::Disconnected => "Disconnected",
68 ConnectionStatus::Degraded => "Degraded - experiencing issues",
69 ConnectionStatus::Failed => "Failed - connection lost",
70 }
71 }
72
73 pub fn level(&self) -> u8 {
75 match self {
76 ConnectionStatus::Failed => 0,
77 ConnectionStatus::Degraded => 1,
78 ConnectionStatus::Disconnected => 2,
79 ConnectionStatus::Connected => 3,
80 }
81 }
82}
83
84impl From<bool> for ConnectionStatus {
85 fn from(connected:bool) -> Self {
86 if connected {
87 ConnectionStatus::Connected
88 } else {
89 ConnectionStatus::Disconnected
90 }
91 }
92}
93
94#[derive(Clone, Serialize, Deserialize)]
126pub struct ConnectionHandle {
127 pub id:String,
129
130 pub created_at:std::time::SystemTime,
132
133 pub last_used:std::time::SystemTime,
135
136 pub health_score:f64,
138
139 pub error_count:usize,
141}
142
143impl ConnectionHandle {
144 pub fn new() -> Self {
146 let now = std::time::SystemTime::now();
147 Self {
148 id:uuid::Uuid::new_v4().to_string(),
149 created_at:now,
150 last_used:now,
151 health_score:100.0,
152 error_count:0,
153 }
154 }
155
156 pub fn update_health(&mut self, success:bool) {
165 if success {
166 self.health_score = (self.health_score + 10.0).min(100.0);
167 self.error_count = 0;
168 } else {
169 self.health_score = (self.health_score - 25.0).max(0.0);
170 self.error_count += 1;
171 }
172 self.last_used = std::time::SystemTime::now();
173 }
174
175 pub fn is_healthy(&self) -> bool { self.health_score > 50.0 && self.error_count < 5 }
185
186 pub fn age_seconds(&self) -> u64 {
188 self.created_at
189 .duration_since(std::time::UNIX_EPOCH)
190 .map(|d| d.as_secs())
191 .unwrap_or(0)
192 }
193
194 pub fn idle_seconds(&self) -> u64 {
196 self.last_used
197 .duration_since(std::time::UNIX_EPOCH)
198 .map(|d| d.as_secs())
199 .unwrap_or(0)
200 }
201
202 pub fn status(&self) -> ConnectionStatus {
204 if self.is_healthy() {
205 ConnectionStatus::Connected
206 } else if self.health_score > 25.0 {
207 ConnectionStatus::Degraded
208 } else {
209 ConnectionStatus::Failed
210 }
211 }
212
213 pub fn touch(&mut self) { self.last_used = std::time::SystemTime::now(); }
215
216 pub fn reset_health(&mut self) {
218 self.health_score = 100.0;
219 self.error_count = 0;
220 self.last_used = std::time::SystemTime::now();
221 }
222}
223
224#[allow(dead_code)]
226trait SystemTimeExt {
227 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError>;
229}
230
231impl SystemTimeExt for std::time::SystemTime {
232 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError> {
233 self.duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs())
234 }
235}
236
237impl std::fmt::Debug for ConnectionHandle {
238 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 let created_age = self
240 .created_at
241 .duration_since(std::time::UNIX_EPOCH)
242 .map(|d| d.as_secs())
243 .unwrap_or(0);
244 let last_used_age = self
245 .last_used
246 .duration_since(std::time::UNIX_EPOCH)
247 .map(|d| d.as_secs())
248 .unwrap_or(0);
249
250 f.debug_struct("ConnectionHandle")
251 .field("id", &self.id)
252 .field("created_at_age_seconds", &created_age)
253 .field("last_used_age_seconds", &last_used_age)
254 .field("health_score", &self.health_score)
255 .field("error_count", &self.error_count)
256 .field("status", &self.status())
257 .finish()
258 }
259}
260
261#[derive(Debug, Clone, Default)]
276pub struct ConnectionStats {
277 pub total_connections:usize,
279
280 pub healthy_connections:usize,
282
283 pub max_connections:usize,
285
286 pub available_permits:usize,
288
289 pub connection_timeout:std::time::Duration,
291}
292
293impl ConnectionStats {
294 pub fn utilization(&self) -> f64 {
299 if self.max_connections == 0 {
300 return 0.0;
301 }
302
303 let used = self.max_connections - self.available_permits;
304 (used as f64 / self.max_connections as f64) * 100.0
305 }
306
307 pub fn health_percentage(&self) -> f64 {
312 if self.total_connections == 0 {
313 return 100.0;
314 }
315
316 (self.healthy_connections as f64 / self.total_connections as f64) * 100.0
317 }
318
319 pub fn is_under_stress(&self) -> bool { self.utilization() > 80.0 || self.health_percentage() < 70.0 }
329
330 pub fn summary(&self) -> String {
332 format!(
333 "Connections: {}/{} ({}%), Healthy: {}%, Utilization: {}%",
334 self.total_connections,
335 self.max_connections,
336 self.health_percentage(),
337 self.health_percentage(),
338 self.utilization()
339 )
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_connection_status_from_bool() {
349 assert!(matches!(ConnectionStatus::from(true), ConnectionStatus::Connected));
350 assert!(matches!(ConnectionStatus::from(false), ConnectionStatus::Disconnected));
351 }
352
353 #[test]
354 fn test_connection_status_description() {
355 assert_eq!(ConnectionStatus::Connected.description(), "Connected and healthy");
356 assert_eq!(ConnectionStatus::Disconnected.description(), "Disconnected");
357 assert_eq!(ConnectionStatus::Degraded.description(), "Degraded - experiencing issues");
358 assert_eq!(ConnectionStatus::Failed.description(), "Failed - connection lost");
359 }
360
361 #[test]
362 fn test_connection_status_level() {
363 assert_eq!(ConnectionStatus::Failed.level(), 0);
364 assert_eq!(ConnectionStatus::Degraded.level(), 1);
365 assert_eq!(ConnectionStatus::Disconnected.level(), 2);
366 assert_eq!(ConnectionStatus::Connected.level(), 3);
367 }
368
369 #[test]
370 fn test_connection_handle_creation() {
371 let handle = ConnectionHandle::new();
372 assert!(!handle.id.is_empty());
373 assert_eq!(handle.health_score, 100.0);
374 assert_eq!(handle.error_count, 0);
375 assert!(handle.is_healthy());
376 }
377
378 #[test]
379 fn test_connection_handle_health_update_success() {
380 let mut handle = ConnectionHandle::new();
381
382 assert_eq!(handle.health_score, 100.0);
384 assert!(handle.is_healthy());
385
386 handle.update_health(true);
388 assert_eq!(handle.health_score, 100.0);
389 assert_eq!(handle.error_count, 0);
390
391 handle.update_health(false);
393 assert_eq!(handle.health_score, 75.0);
394 assert_eq!(handle.error_count, 1);
395 assert!(handle.is_healthy());
396
397 handle.update_health(false);
399 assert_eq!(handle.health_score, 50.0);
400 assert_eq!(handle.error_count, 2);
401 assert!(!handle.is_healthy()); handle.update_health(true);
405 assert_eq!(handle.health_score, 60.0);
406 assert_eq!(handle.error_count, 0);
407 assert!(handle.is_healthy());
408 }
409
410 #[test]
411 fn test_connection_handle_health_boundaries() {
412 let mut handle = ConnectionHandle::new();
413
414 for _ in 0..20 {
416 handle.update_health(true);
417 }
418 assert_eq!(handle.health_score, 100.0);
419
420 handle.health_score = 50.0;
422
423 for _ in 0..10 {
425 handle.update_health(false);
426 }
427 assert_eq!(handle.health_score, 0.0);
428 }
429
430 #[test]
431 fn test_connection_handle_is_healthy() {
432 let mut handle = ConnectionHandle::new();
433
434 assert!(handle.is_healthy());
435
436 handle.health_score = 50.0;
438 handle.error_count = 0;
439 assert!(!handle.is_healthy()); handle.health_score = 60.0;
443 handle.error_count = 5;
444 assert!(!handle.is_healthy()); }
446
447 #[test]
448 fn test_connection_handle_status() {
449 let mut handle = ConnectionHandle::new();
450 assert!(matches!(handle.status(), ConnectionStatus::Connected));
451
452 handle.health_score = 75.0;
453 assert!(matches!(handle.status(), ConnectionStatus::Connected));
454
455 handle.health_score = 50.0;
456 assert!(matches!(handle.status(), ConnectionStatus::Degraded));
457
458 handle.health_score = 25.0;
459 assert!(matches!(handle.status(), ConnectionStatus::Failed));
460 }
461
462 #[test]
463 fn test_connection_handle_reset() {
464 let mut handle = ConnectionHandle::new();
465
466 for _ in 0..3 {
468 handle.update_health(false);
469 }
470 assert!(handle.health_score < 100.0);
471
472 handle.reset_health();
474 assert_eq!(handle.health_score, 100.0);
475 assert_eq!(handle.error_count, 0);
476 }
477
478 #[test]
479 fn test_connection_stats_utilization() {
480 let stats = ConnectionStats {
481 total_connections:50,
482 healthy_connections:45,
483 max_connections:100,
484 available_permits:50,
485 connection_timeout:std::time::Duration::from_secs(30),
486 };
487
488 assert_eq!(stats.utilization(), 50.0);
490 }
491
492 #[test]
493 fn test_connection_stats_health_percentage() {
494 let stats = ConnectionStats {
495 total_connections:50,
496 healthy_connections:45,
497 max_connections:100,
498 available_permits:50,
499 connection_timeout:std::time::Duration::from_secs(30),
500 };
501
502 assert_eq!(stats.health_percentage(), 90.0);
504 }
505
506 #[test]
507 fn test_connection_stats_is_under_stress() {
508 let mut stats = ConnectionStats {
509 total_connections:50,
510 healthy_connections:45,
511 max_connections:100,
512 available_permits:50,
513 connection_timeout:std::time::Duration::from_secs(30),
514 };
515
516 assert!(!stats.is_under_stress());
518
519 stats.available_permits = 10;
521 assert!(stats.is_under_stress());
522
523 stats.available_permits = 50;
525 stats.healthy_connections = 30; assert!(stats.is_under_stress());
527 }
528
529 #[test]
530 fn test_connection_stats_empty_pool() {
531 let stats = ConnectionStats {
532 total_connections:0,
533 healthy_connections:0,
534 max_connections:100,
535 available_permits:100,
536 connection_timeout:std::time::Duration::from_secs(30),
537 };
538
539 assert_eq!(stats.utilization(), 0.0);
540 assert_eq!(stats.health_percentage(), 100.0); assert!(!stats.is_under_stress());
542 }
543}