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
240trait SystemTimeExt {
242 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError>;
244}
245
246impl SystemTimeExt for std::time::SystemTime {
247 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError> {
248 self.duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs())
249 }
250}
251
252impl std::fmt::Debug for ConnectionHandle {
253 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 let created_age = self
255 .created_at
256 .duration_since(std::time::UNIX_EPOCH)
257 .map(|d| d.as_secs())
258 .unwrap_or(0);
259
260 let last_used_age = self
261 .last_used
262 .duration_since(std::time::UNIX_EPOCH)
263 .map(|d| d.as_secs())
264 .unwrap_or(0);
265
266 f.debug_struct("ConnectionHandle")
267 .field("id", &self.id)
268 .field("created_at_age_seconds", &created_age)
269 .field("last_used_age_seconds", &last_used_age)
270 .field("health_score", &self.health_score)
271 .field("error_count", &self.error_count)
272 .field("status", &self.status())
273 .finish()
274 }
275}
276
277#[derive(Debug, Clone, Default)]
292pub struct ConnectionStats {
293 pub total_connections:usize,
295
296 pub healthy_connections:usize,
298
299 pub max_connections:usize,
301
302 pub available_permits:usize,
304
305 pub connection_timeout:std::time::Duration,
307}
308
309impl ConnectionStats {
310 pub fn utilization(&self) -> f64 {
315 if self.max_connections == 0 {
316 return 0.0;
317 }
318
319 let used = self.max_connections - self.available_permits;
320
321 (used as f64 / self.max_connections as f64) * 100.0
322 }
323
324 pub fn health_percentage(&self) -> f64 {
329 if self.total_connections == 0 {
330 return 100.0;
331 }
332
333 (self.healthy_connections as f64 / self.total_connections as f64) * 100.0
334 }
335
336 pub fn is_under_stress(&self) -> bool { self.utilization() > 80.0 || self.health_percentage() < 70.0 }
346
347 pub fn summary(&self) -> String {
349 format!(
350 "Connections: {}/{} ({}%), Healthy: {}%, Utilization: {}%",
351 self.total_connections,
352 self.max_connections,
353 self.health_percentage(),
354 self.health_percentage(),
355 self.utilization()
356 )
357 }
358}
359
360#[cfg(test)]
361mod tests {
362
363 use super::*;
364
365 #[test]
366 fn test_connection_status_from_bool() {
367 assert!(matches!(ConnectionStatus::from(true), ConnectionStatus::Connected));
368
369 assert!(matches!(ConnectionStatus::from(false), ConnectionStatus::Disconnected));
370 }
371
372 #[test]
373 fn test_connection_status_description() {
374 assert_eq!(ConnectionStatus::Connected.description(), "Connected and healthy");
375
376 assert_eq!(ConnectionStatus::Disconnected.description(), "Disconnected");
377
378 assert_eq!(ConnectionStatus::Degraded.description(), "Degraded - experiencing issues");
379
380 assert_eq!(ConnectionStatus::Failed.description(), "Failed - connection lost");
381 }
382
383 #[test]
384 fn test_connection_status_level() {
385 assert_eq!(ConnectionStatus::Failed.level(), 0);
386
387 assert_eq!(ConnectionStatus::Degraded.level(), 1);
388
389 assert_eq!(ConnectionStatus::Disconnected.level(), 2);
390
391 assert_eq!(ConnectionStatus::Connected.level(), 3);
392 }
393
394 #[test]
395 fn test_connection_handle_creation() {
396 let handle = ConnectionHandle::new();
397
398 assert!(!handle.id.is_empty());
399
400 assert_eq!(handle.health_score, 100.0);
401
402 assert_eq!(handle.error_count, 0);
403
404 assert!(handle.is_healthy());
405 }
406
407 #[test]
408 fn test_connection_handle_health_update_success() {
409 let mut handle = ConnectionHandle::new();
410
411 assert_eq!(handle.health_score, 100.0);
413
414 assert!(handle.is_healthy());
415
416 handle.update_health(true);
418
419 assert_eq!(handle.health_score, 100.0);
420
421 assert_eq!(handle.error_count, 0);
422
423 handle.update_health(false);
425
426 assert_eq!(handle.health_score, 75.0);
427
428 assert_eq!(handle.error_count, 1);
429
430 assert!(handle.is_healthy());
431
432 handle.update_health(false);
434
435 assert_eq!(handle.health_score, 50.0);
436
437 assert_eq!(handle.error_count, 2);
438
439 assert!(!handle.is_healthy()); handle.update_health(true);
443
444 assert_eq!(handle.health_score, 60.0);
445
446 assert_eq!(handle.error_count, 0);
447
448 assert!(handle.is_healthy());
449 }
450
451 #[test]
452 fn test_connection_handle_health_boundaries() {
453 let mut handle = ConnectionHandle::new();
454
455 for _ in 0..20 {
457 handle.update_health(true);
458 }
459
460 assert_eq!(handle.health_score, 100.0);
461
462 handle.health_score = 50.0;
464
465 for _ in 0..10 {
467 handle.update_health(false);
468 }
469
470 assert_eq!(handle.health_score, 0.0);
471 }
472
473 #[test]
474 fn test_connection_handle_is_healthy() {
475 let mut handle = ConnectionHandle::new();
476
477 assert!(handle.is_healthy());
478
479 handle.health_score = 50.0;
481
482 handle.error_count = 0;
483
484 assert!(!handle.is_healthy()); handle.health_score = 60.0;
488
489 handle.error_count = 5;
490
491 assert!(!handle.is_healthy()); }
493
494 #[test]
495 fn test_connection_handle_status() {
496 let mut handle = ConnectionHandle::new();
497
498 assert!(matches!(handle.status(), ConnectionStatus::Connected));
499
500 handle.health_score = 75.0;
501
502 assert!(matches!(handle.status(), ConnectionStatus::Connected));
503
504 handle.health_score = 50.0;
505
506 assert!(matches!(handle.status(), ConnectionStatus::Degraded));
507
508 handle.health_score = 25.0;
509
510 assert!(matches!(handle.status(), ConnectionStatus::Failed));
511 }
512
513 #[test]
514 fn test_connection_handle_reset() {
515 let mut handle = ConnectionHandle::new();
516
517 for _ in 0..3 {
519 handle.update_health(false);
520 }
521
522 assert!(handle.health_score < 100.0);
523
524 handle.reset_health();
526
527 assert_eq!(handle.health_score, 100.0);
528
529 assert_eq!(handle.error_count, 0);
530 }
531
532 #[test]
533 fn test_connection_stats_utilization() {
534 let stats = ConnectionStats {
535 total_connections:50,
536
537 healthy_connections:45,
538
539 max_connections:100,
540
541 available_permits:50,
542
543 connection_timeout:std::time::Duration::from_secs(30),
544 };
545
546 assert_eq!(stats.utilization(), 50.0);
548 }
549
550 #[test]
551 fn test_connection_stats_health_percentage() {
552 let stats = ConnectionStats {
553 total_connections:50,
554
555 healthy_connections:45,
556
557 max_connections:100,
558
559 available_permits:50,
560
561 connection_timeout:std::time::Duration::from_secs(30),
562 };
563
564 assert_eq!(stats.health_percentage(), 90.0);
566 }
567
568 #[test]
569 fn test_connection_stats_is_under_stress() {
570 let mut stats = ConnectionStats {
571 total_connections:50,
572
573 healthy_connections:45,
574
575 max_connections:100,
576
577 available_permits:50,
578
579 connection_timeout:std::time::Duration::from_secs(30),
580 };
581
582 assert!(!stats.is_under_stress());
584
585 stats.available_permits = 10;
587
588 assert!(stats.is_under_stress());
589
590 stats.available_permits = 50;
592
593 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());
617 }
618}