1mod coord_cache;
7mod entry;
8
9use thiserror::Error;
10
11pub use coord_cache::{CoordCache, DEFAULT_COORD_CACHE_SIZE, DEFAULT_COORD_CACHE_TTL_MS};
12pub use entry::CacheEntry;
13
14#[derive(Debug, Error)]
16pub enum CacheError {
17 #[error("cache full: max {max} entries")]
18 CacheFull { max: usize },
19
20 #[error("entry not found")]
21 NotFound,
22
23 #[error("entry expired")]
24 Expired,
25}
26
27#[derive(Clone, Debug)]
29pub struct CacheStats {
30 pub entries: usize,
32 pub max_entries: usize,
34 pub expired: usize,
36 pub avg_age_ms: u64,
38}
39
40impl CacheStats {
41 pub fn fill_ratio(&self) -> f64 {
43 if self.max_entries == 0 {
44 0.0
45 } else {
46 self.entries as f64 / self.max_entries as f64
47 }
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn test_cache_stats_fill_ratio() {
57 let stats = CacheStats {
58 entries: 50,
59 max_entries: 100,
60 expired: 0,
61 avg_age_ms: 0,
62 };
63 assert!((stats.fill_ratio() - 0.5).abs() < f64::EPSILON);
64 }
65
66 #[test]
67 fn test_cache_stats_fill_ratio_zero_max() {
68 let stats = CacheStats {
69 entries: 0,
70 max_entries: 0,
71 expired: 0,
72 avg_age_ms: 0,
73 };
74 assert!((stats.fill_ratio() - 0.0).abs() < f64::EPSILON);
75 }
76
77 #[test]
78 fn test_cache_stats_fill_ratio_full() {
79 let stats = CacheStats {
80 entries: 100,
81 max_entries: 100,
82 expired: 10,
83 avg_age_ms: 500,
84 };
85 assert!((stats.fill_ratio() - 1.0).abs() < f64::EPSILON);
86 }
87
88 #[test]
89 fn test_cache_error_display() {
90 let full = CacheError::CacheFull { max: 1000 };
91 assert_eq!(full.to_string(), "cache full: max 1000 entries");
92
93 let not_found = CacheError::NotFound;
94 assert_eq!(not_found.to_string(), "entry not found");
95
96 let expired = CacheError::Expired;
97 assert_eq!(expired.to_string(), "entry expired");
98 }
99}