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
68 ConnectionStatus::Disconnected => "Disconnected",
69
70 ConnectionStatus::Degraded => "Degraded - experiencing issues",
71
72 ConnectionStatus::Failed => "Failed - connection lost",
73 }
74 }
75
76 pub fn level(&self) -> u8 {
78 match self {
79 ConnectionStatus::Failed => 0,
80
81 ConnectionStatus::Degraded => 1,
82
83 ConnectionStatus::Disconnected => 2,
84
85 ConnectionStatus::Connected => 3,
86 }
87 }
88}
89
90impl From<bool> for ConnectionStatus {
91 fn from(connected:bool) -> Self {
92 if connected {
93 ConnectionStatus::Connected
94 } else {
95 ConnectionStatus::Disconnected
96 }
97 }
98}
99
100#[derive(Clone, Serialize, Deserialize)]
132pub struct ConnectionHandle {
133 pub id:String,
135
136 pub created_at:std::time::SystemTime,
138
139 pub last_used:std::time::SystemTime,
141
142 pub health_score:f64,
144
145 pub error_count:usize,
147}
148
149impl ConnectionHandle {
150 pub fn new() -> Self {
152 let now = std::time::SystemTime::now();
153
154 Self {
155 id:uuid::Uuid::new_v4().to_string(),
156
157 created_at:now,
158
159 last_used:now,
160
161 health_score:100.0,
162
163 error_count:0,
164 }
165 }
166
167 pub fn update_health(&mut self, success:bool) {
176 if success {
177 self.health_score = (self.health_score + 10.0).min(100.0);
178
179 self.error_count = 0;
180 } else {
181 self.health_score = (self.health_score - 25.0).max(0.0);
182
183 self.error_count += 1;
184 }
185
186 self.last_used = std::time::SystemTime::now();
187 }
188
189 pub fn is_healthy(&self) -> bool { self.health_score > 50.0 && self.error_count < 5 }
199
200 pub fn age_seconds(&self) -> u64 {
202 self.created_at
203 .duration_since(std::time::UNIX_EPOCH)
204 .map(|d| d.as_secs())
205 .unwrap_or(0)
206 }
207
208 pub fn idle_seconds(&self) -> u64 {
210 self.last_used
211 .duration_since(std::time::UNIX_EPOCH)
212 .map(|d| d.as_secs())
213 .unwrap_or(0)
214 }
215
216 pub fn status(&self) -> ConnectionStatus {
218 if self.is_healthy() {
219 ConnectionStatus::Connected
220 } else if self.health_score > 25.0 {
221 ConnectionStatus::Degraded
222 } else {
223 ConnectionStatus::Failed
224 }
225 }
226
227 pub fn touch(&mut self) { self.last_used = std::time::SystemTime::now(); }
229
230 pub fn reset_health(&mut self) {
232 self.health_score = 100.0;
233
234 self.error_count = 0;
235
236 self.last_used = std::time::SystemTime::now();
237 }
238}
239
240#[allow(dead_code)]
242trait SystemTimeExt {
243 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError>;
245}
246
247impl SystemTimeExt for std::time::SystemTime {
248 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError> {
249 self.duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs())
250 }
251}
252
253impl std::fmt::Debug for ConnectionHandle {
254 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 let created_age = self
256 .created_at
257 .duration_since(std::time::UNIX_EPOCH)
258 .map(|d| d.as_secs())
259 .unwrap_or(0);
260
261 let last_used_age = self
262 .last_used
263 .duration_since(std::time::UNIX_EPOCH)
264 .map(|d| d.as_secs())
265 .unwrap_or(0);
266
267 f.debug_struct("ConnectionHandle")
268 .field("id", &self.id)
269 .field("created_at_age_seconds", &created_age)
270 .field("last_used_age_seconds", &last_used_age)
271 .field("health_score", &self.health_score)
272 .field("error_count", &self.error_count)
273 .field("status", &self.status())
274 .finish()
275 }
276}
277
278#[derive(Debug, Clone, Default)]
293pub struct ConnectionStats {
294 pub total_connections:usize,
296
297 pub healthy_connections:usize,
299
300 pub max_connections:usize,
302
303 pub available_permits:usize,
305
306 pub connection_timeout:std::time::Duration,
308}
309
310impl ConnectionStats {
311 pub fn utilization(&self) -> f64 {
316 if self.max_connections == 0 {
317 return 0.0;
318 }
319
320 let used = self.max_connections - self.available_permits;
321
322 (used as f64 / self.max_connections as f64) * 100.0
323 }
324
325 pub fn health_percentage(&self) -> f64 {
330 if self.total_connections == 0 {
331 return 100.0;
332 }
333
334 (self.healthy_connections as f64 / self.total_connections as f64) * 100.0
335 }
336
337 pub fn is_under_stress(&self) -> bool { self.utilization() > 80.0 || self.health_percentage() < 70.0 }
347
348 pub fn summary(&self) -> String {
350 format!(
351 "Connections: {}/{} ({}%), Healthy: {}%, Utilization: {}%",
352 self.total_connections,
353 self.max_connections,
354 self.health_percentage(),
355 self.health_percentage(),
356 self.utilization()
357 )
358 }
359}
360
361#[cfg(test)]
362mod tests {
363
364 use super::*;
365
366 #[test]
367 fn test_connection_status_from_bool() {
368 assert!(matches!(ConnectionStatus::from(true), ConnectionStatus::Connected));
369
370 assert!(matches!(ConnectionStatus::from(false), ConnectionStatus::Disconnected));
371 }
372
373 #[test]
374 fn test_connection_status_description() {
375 assert_eq!(ConnectionStatus::Connected.description(), "Connected and healthy");
376
377 assert_eq!(ConnectionStatus::Disconnected.description(), "Disconnected");
378
379 assert_eq!(ConnectionStatus::Degraded.description(), "Degraded - experiencing issues");
380
381 assert_eq!(ConnectionStatus::Failed.description(), "Failed - connection lost");
382 }
383
384 #[test]
385 fn test_connection_status_level() {
386 assert_eq!(ConnectionStatus::Failed.level(), 0);
387
388 assert_eq!(ConnectionStatus::Degraded.level(), 1);
389
390 assert_eq!(ConnectionStatus::Disconnected.level(), 2);
391
392 assert_eq!(ConnectionStatus::Connected.level(), 3);
393 }
394
395 #[test]
396 fn test_connection_handle_creation() {
397 let handle = ConnectionHandle::new();
398
399 assert!(!handle.id.is_empty());
400
401 assert_eq!(handle.health_score, 100.0);
402
403 assert_eq!(handle.error_count, 0);
404
405 assert!(handle.is_healthy());
406 }
407
408 #[test]
409 fn test_connection_handle_health_update_success() {
410 let mut handle = ConnectionHandle::new();
411
412 assert_eq!(handle.health_score, 100.0);
414
415 assert!(handle.is_healthy());
416
417 handle.update_health(true);
419
420 assert_eq!(handle.health_score, 100.0);
421
422 assert_eq!(handle.error_count, 0);
423
424 handle.update_health(false);
426
427 assert_eq!(handle.health_score, 75.0);
428
429 assert_eq!(handle.error_count, 1);
430
431 assert!(handle.is_healthy());
432
433 handle.update_health(false);
435
436 assert_eq!(handle.health_score, 50.0);
437
438 assert_eq!(handle.error_count, 2);
439
440 assert!(!handle.is_healthy()); handle.update_health(true);
444
445 assert_eq!(handle.health_score, 60.0);
446
447 assert_eq!(handle.error_count, 0);
448
449 assert!(handle.is_healthy());
450 }
451
452 #[test]
453 fn test_connection_handle_health_boundaries() {
454 let mut handle = ConnectionHandle::new();
455
456 for _ in 0..20 {
458 handle.update_health(true);
459 }
460
461 assert_eq!(handle.health_score, 100.0);
462
463 handle.health_score = 50.0;
465
466 for _ in 0..10 {
468 handle.update_health(false);
469 }
470
471 assert_eq!(handle.health_score, 0.0);
472 }
473
474 #[test]
475 fn test_connection_handle_is_healthy() {
476 let mut handle = ConnectionHandle::new();
477
478 assert!(handle.is_healthy());
479
480 handle.health_score = 50.0;
482
483 handle.error_count = 0;
484
485 assert!(!handle.is_healthy()); handle.health_score = 60.0;
489
490 handle.error_count = 5;
491
492 assert!(!handle.is_healthy()); }
494
495 #[test]
496 fn test_connection_handle_status() {
497 let mut handle = ConnectionHandle::new();
498
499 assert!(matches!(handle.status(), ConnectionStatus::Connected));
500
501 handle.health_score = 75.0;
502
503 assert!(matches!(handle.status(), ConnectionStatus::Connected));
504
505 handle.health_score = 50.0;
506
507 assert!(matches!(handle.status(), ConnectionStatus::Degraded));
508
509 handle.health_score = 25.0;
510
511 assert!(matches!(handle.status(), ConnectionStatus::Failed));
512 }
513
514 #[test]
515 fn test_connection_handle_reset() {
516 let mut handle = ConnectionHandle::new();
517
518 for _ in 0..3 {
520 handle.update_health(false);
521 }
522
523 assert!(handle.health_score < 100.0);
524
525 handle.reset_health();
527
528 assert_eq!(handle.health_score, 100.0);
529
530 assert_eq!(handle.error_count, 0);
531 }
532
533 #[test]
534 fn test_connection_stats_utilization() {
535 let stats = ConnectionStats {
536 total_connections:50,
537
538 healthy_connections:45,
539
540 max_connections:100,
541
542 available_permits:50,
543
544 connection_timeout:std::time::Duration::from_secs(30),
545 };
546
547 assert_eq!(stats.utilization(), 50.0);
549 }
550
551 #[test]
552 fn test_connection_stats_health_percentage() {
553 let stats = ConnectionStats {
554 total_connections:50,
555
556 healthy_connections:45,
557
558 max_connections:100,
559
560 available_permits:50,
561
562 connection_timeout:std::time::Duration::from_secs(30),
563 };
564
565 assert_eq!(stats.health_percentage(), 90.0);
567 }
568
569 #[test]
570 fn test_connection_stats_is_under_stress() {
571 let mut stats = ConnectionStats {
572 total_connections:50,
573
574 healthy_connections:45,
575
576 max_connections:100,
577
578 available_permits:50,
579
580 connection_timeout:std::time::Duration::from_secs(30),
581 };
582
583 assert!(!stats.is_under_stress());
585
586 stats.available_permits = 10;
588
589 assert!(stats.is_under_stress());
590
591 stats.available_permits = 50;
593
594 stats.healthy_connections = 30; assert!(stats.is_under_stress());
596 }
597
598 #[test]
599 fn test_connection_stats_empty_pool() {
600 let stats = ConnectionStats {
601 total_connections:0,
602
603 healthy_connections:0,
604
605 max_connections:100,
606
607 available_permits:100,
608
609 connection_timeout:std::time::Duration::from_secs(30),
610 };
611
612 assert_eq!(stats.utilization(), 0.0);
613
614 assert_eq!(stats.health_percentage(), 100.0); assert!(!stats.is_under_stress());
616 }
617}