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
224trait SystemTimeExt {
226 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError>;
228}
229
230impl SystemTimeExt for std::time::SystemTime {
231 fn duration_since_epoch_secs(&self) -> Result<u64, std::time::SystemTimeError> {
232 self.duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs())
233 }
234}
235
236impl std::fmt::Debug for ConnectionHandle {
237 fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238 let created_age = self
239 .created_at
240 .duration_since(std::time::UNIX_EPOCH)
241 .map(|d| d.as_secs())
242 .unwrap_or(0);
243 let last_used_age = self
244 .last_used
245 .duration_since(std::time::UNIX_EPOCH)
246 .map(|d| d.as_secs())
247 .unwrap_or(0);
248
249 f.debug_struct("ConnectionHandle")
250 .field("id", &self.id)
251 .field("created_at_age_seconds", &created_age)
252 .field("last_used_age_seconds", &last_used_age)
253 .field("health_score", &self.health_score)
254 .field("error_count", &self.error_count)
255 .field("status", &self.status())
256 .finish()
257 }
258}
259
260#[derive(Debug, Clone, Default)]
275pub struct ConnectionStats {
276 pub total_connections:usize,
278
279 pub healthy_connections:usize,
281
282 pub max_connections:usize,
284
285 pub available_permits:usize,
287
288 pub connection_timeout:std::time::Duration,
290}
291
292impl ConnectionStats {
293 pub fn utilization(&self) -> f64 {
298 if self.max_connections == 0 {
299 return 0.0;
300 }
301
302 let used = self.max_connections - self.available_permits;
303 (used as f64 / self.max_connections as f64) * 100.0
304 }
305
306 pub fn health_percentage(&self) -> f64 {
311 if self.total_connections == 0 {
312 return 100.0;
313 }
314
315 (self.healthy_connections as f64 / self.total_connections as f64) * 100.0
316 }
317
318 pub fn is_under_stress(&self) -> bool { self.utilization() > 80.0 || self.health_percentage() < 70.0 }
328
329 pub fn summary(&self) -> String {
331 format!(
332 "Connections: {}/{} ({}%), Healthy: {}%, Utilization: {}%",
333 self.total_connections,
334 self.max_connections,
335 self.health_percentage(),
336 self.health_percentage(),
337 self.utilization()
338 )
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_connection_status_from_bool() {
348 assert!(matches!(ConnectionStatus::from(true), ConnectionStatus::Connected));
349 assert!(matches!(ConnectionStatus::from(false), ConnectionStatus::Disconnected));
350 }
351
352 #[test]
353 fn test_connection_status_description() {
354 assert_eq!(ConnectionStatus::Connected.description(), "Connected and healthy");
355 assert_eq!(ConnectionStatus::Disconnected.description(), "Disconnected");
356 assert_eq!(ConnectionStatus::Degraded.description(), "Degraded - experiencing issues");
357 assert_eq!(ConnectionStatus::Failed.description(), "Failed - connection lost");
358 }
359
360 #[test]
361 fn test_connection_status_level() {
362 assert_eq!(ConnectionStatus::Failed.level(), 0);
363 assert_eq!(ConnectionStatus::Degraded.level(), 1);
364 assert_eq!(ConnectionStatus::Disconnected.level(), 2);
365 assert_eq!(ConnectionStatus::Connected.level(), 3);
366 }
367
368 #[test]
369 fn test_connection_handle_creation() {
370 let handle = ConnectionHandle::new();
371 assert!(!handle.id.is_empty());
372 assert_eq!(handle.health_score, 100.0);
373 assert_eq!(handle.error_count, 0);
374 assert!(handle.is_healthy());
375 }
376
377 #[test]
378 fn test_connection_handle_health_update_success() {
379 let mut handle = ConnectionHandle::new();
380
381 assert_eq!(handle.health_score, 100.0);
383 assert!(handle.is_healthy());
384
385 handle.update_health(true);
387 assert_eq!(handle.health_score, 100.0);
388 assert_eq!(handle.error_count, 0);
389
390 handle.update_health(false);
392 assert_eq!(handle.health_score, 75.0);
393 assert_eq!(handle.error_count, 1);
394 assert!(handle.is_healthy());
395
396 handle.update_health(false);
398 assert_eq!(handle.health_score, 50.0);
399 assert_eq!(handle.error_count, 2);
400 assert!(!handle.is_healthy()); handle.update_health(true);
404 assert_eq!(handle.health_score, 60.0);
405 assert_eq!(handle.error_count, 0);
406 assert!(handle.is_healthy());
407 }
408
409 #[test]
410 fn test_connection_handle_health_boundaries() {
411 let mut handle = ConnectionHandle::new();
412
413 for _ in 0..20 {
415 handle.update_health(true);
416 }
417 assert_eq!(handle.health_score, 100.0);
418
419 handle.health_score = 50.0;
421
422 for _ in 0..10 {
424 handle.update_health(false);
425 }
426 assert_eq!(handle.health_score, 0.0);
427 }
428
429 #[test]
430 fn test_connection_handle_is_healthy() {
431 let mut handle = ConnectionHandle::new();
432
433 assert!(handle.is_healthy());
434
435 handle.health_score = 50.0;
437 handle.error_count = 0;
438 assert!(!handle.is_healthy()); handle.health_score = 60.0;
442 handle.error_count = 5;
443 assert!(!handle.is_healthy()); }
445
446 #[test]
447 fn test_connection_handle_status() {
448 let mut handle = ConnectionHandle::new();
449 assert!(matches!(handle.status(), ConnectionStatus::Connected));
450
451 handle.health_score = 75.0;
452 assert!(matches!(handle.status(), ConnectionStatus::Connected));
453
454 handle.health_score = 50.0;
455 assert!(matches!(handle.status(), ConnectionStatus::Degraded));
456
457 handle.health_score = 25.0;
458 assert!(matches!(handle.status(), ConnectionStatus::Failed));
459 }
460
461 #[test]
462 fn test_connection_handle_reset() {
463 let mut handle = ConnectionHandle::new();
464
465 for _ in 0..3 {
467 handle.update_health(false);
468 }
469 assert!(handle.health_score < 100.0);
470
471 handle.reset_health();
473 assert_eq!(handle.health_score, 100.0);
474 assert_eq!(handle.error_count, 0);
475 }
476
477 #[test]
478 fn test_connection_stats_utilization() {
479 let stats = ConnectionStats {
480 total_connections:50,
481 healthy_connections:45,
482 max_connections:100,
483 available_permits:50,
484 connection_timeout:std::time::Duration::from_secs(30),
485 };
486
487 assert_eq!(stats.utilization(), 50.0);
489 }
490
491 #[test]
492 fn test_connection_stats_health_percentage() {
493 let stats = ConnectionStats {
494 total_connections:50,
495 healthy_connections:45,
496 max_connections:100,
497 available_permits:50,
498 connection_timeout:std::time::Duration::from_secs(30),
499 };
500
501 assert_eq!(stats.health_percentage(), 90.0);
503 }
504
505 #[test]
506 fn test_connection_stats_is_under_stress() {
507 let mut stats = ConnectionStats {
508 total_connections:50,
509 healthy_connections:45,
510 max_connections:100,
511 available_permits:50,
512 connection_timeout:std::time::Duration::from_secs(30),
513 };
514
515 assert!(!stats.is_under_stress());
517
518 stats.available_permits = 10;
520 assert!(stats.is_under_stress());
521
522 stats.available_permits = 50;
524 stats.healthy_connections = 30; assert!(stats.is_under_stress());
526 }
527
528 #[test]
529 fn test_connection_stats_empty_pool() {
530 let stats = ConnectionStats {
531 total_connections:0,
532 healthy_connections:0,
533 max_connections:100,
534 available_permits:100,
535 connection_timeout:std::time::Duration::from_secs(30),
536 };
537
538 assert_eq!(stats.utilization(), 0.0);
539 assert_eq!(stats.health_percentage(), 100.0); assert!(!stats.is_under_stress());
541 }
542}