#[derive(Debug, Clone, Default)]
pub struct CacheStats {
pub hits: u64,
pub misses: u64,
pub ttl_evictions: u64,
pub stale_evictions: u64,
pub lru_evictions: u64,
pub current_entries: usize,
pub max_entries: usize,
}
impl CacheStats {
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
0.0
} else {
self.hits as f64 / total as f64
}
}
pub fn miss_rate(&self) -> f64 {
1.0 - self.hit_rate()
}
pub fn total_evictions(&self) -> u64 {
self.ttl_evictions + self.stale_evictions + self.lru_evictions
}
pub fn total_operations(&self) -> u64 {
self.hits + self.misses
}
pub fn is_full(&self) -> bool {
self.current_entries >= self.max_entries
}
pub fn utilization(&self) -> f64 {
if self.max_entries == 0 {
0.0
} else {
(self.current_entries as f64 / self.max_entries as f64) * 100.0
}
}
pub fn reset(&mut self) {
self.hits = 0;
self.misses = 0;
self.ttl_evictions = 0;
self.stale_evictions = 0;
self.lru_evictions = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_stats_rates() {
let mut stats = CacheStats {
hits: 75,
misses: 25,
..Default::default()
};
assert_eq!(stats.hit_rate(), 0.75);
assert_eq!(stats.miss_rate(), 0.25);
assert_eq!(stats.total_operations(), 100);
stats.reset();
assert_eq!(stats.hit_rate(), 0.0);
assert_eq!(stats.miss_rate(), 1.0);
}
#[test]
fn test_cache_stats_evictions() {
let stats = CacheStats {
ttl_evictions: 10,
stale_evictions: 5,
lru_evictions: 3,
..Default::default()
};
assert_eq!(stats.total_evictions(), 18);
}
#[test]
fn test_cache_stats_utilization() {
let stats = CacheStats {
current_entries: 50,
max_entries: 100,
..Default::default()
};
assert_eq!(stats.utilization(), 50.0);
assert!(!stats.is_full());
let full_stats = CacheStats {
current_entries: 100,
max_entries: 100,
..Default::default()
};
assert!(full_stats.is_full());
assert_eq!(full_stats.utilization(), 100.0);
}
}