Skip to main content

fips_core/cache/
mod.rs

1//! Caching Entities
2//!
3//! Coordinate caching for FIPS routing. The CoordCache stores
4//! address-to-coordinate mappings populated by session setup and discovery.
5
6mod 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/// Errors related to cache operations.
15#[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/// Cache statistics.
28#[derive(Clone, Debug)]
29pub struct CacheStats {
30    /// Current number of entries.
31    pub entries: usize,
32    /// Maximum capacity.
33    pub max_entries: usize,
34    /// Number of expired entries.
35    pub expired: usize,
36    /// Average entry age in milliseconds.
37    pub avg_age_ms: u64,
38}
39
40impl CacheStats {
41    /// Fill ratio (entries / max_entries).
42    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}