crates_docs/tools/docs/cache/
stats.rs1use std::sync::atomic::{AtomicU64, Ordering};
4
5#[derive(Debug, Default)]
7pub struct CacheStats {
8 hits: AtomicU64,
10 misses: AtomicU64,
12 sets: AtomicU64,
14}
15
16impl CacheStats {
17 #[must_use]
19 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn record_hit(&self) {
25 self.hits.fetch_add(1, Ordering::Relaxed);
26 }
27
28 pub fn record_miss(&self) {
30 self.misses.fetch_add(1, Ordering::Relaxed);
31 }
32
33 pub fn record_set(&self) {
35 self.sets.fetch_add(1, Ordering::Relaxed);
36 }
37
38 #[must_use]
40 pub fn hits(&self) -> u64 {
41 self.hits.load(Ordering::Relaxed)
42 }
43
44 #[must_use]
46 pub fn misses(&self) -> u64 {
47 self.misses.load(Ordering::Relaxed)
48 }
49
50 #[must_use]
52 pub fn sets(&self) -> u64 {
53 self.sets.load(Ordering::Relaxed)
54 }
55
56 #[must_use]
58 pub fn total_requests(&self) -> u64 {
59 self.hits() + self.misses()
60 }
61
62 #[must_use]
64 #[allow(clippy::cast_precision_loss)]
65 pub fn hit_rate(&self) -> f64 {
66 let total = self.total_requests();
67 if total == 0 {
68 return 0.0;
69 }
70 self.hits() as f64 / total as f64
71 }
72
73 pub fn reset(&self) {
75 self.hits.store(0, Ordering::Relaxed);
76 self.misses.store(0, Ordering::Relaxed);
77 self.sets.store(0, Ordering::Relaxed);
78 }
79}
80
81impl Clone for CacheStats {
82 fn clone(&self) -> Self {
83 Self {
84 hits: AtomicU64::new(self.hits.load(Ordering::Relaxed)),
85 misses: AtomicU64::new(self.misses.load(Ordering::Relaxed)),
86 sets: AtomicU64::new(self.sets.load(Ordering::Relaxed)),
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_cache_stats_new() {
97 let stats = CacheStats::new();
98 assert_eq!(stats.hits(), 0);
99 assert_eq!(stats.misses(), 0);
100 assert_eq!(stats.sets(), 0);
101 }
102
103 #[test]
104 fn test_cache_stats_record() {
105 let stats = CacheStats::new();
106
107 stats.record_hit();
108 stats.record_hit();
109 stats.record_miss();
110 stats.record_set();
111 stats.record_set();
112 stats.record_set();
113
114 assert_eq!(stats.hits(), 2);
115 assert_eq!(stats.misses(), 1);
116 assert_eq!(stats.sets(), 3);
117 }
118
119 #[test]
120 fn test_cache_stats_hit_rate() {
121 let stats = CacheStats::new();
122
123 assert!((stats.hit_rate() - 0.0).abs() < f64::EPSILON);
124
125 stats.record_hit();
126 stats.record_hit();
127 stats.record_miss();
128
129 let rate = stats.hit_rate();
130 assert!((rate - 0.666_666_666_666_666_6).abs() < f64::EPSILON);
131 }
132
133 #[test]
134 fn test_cache_stats_total_requests() {
135 let stats = CacheStats::new();
136
137 stats.record_hit();
138 stats.record_hit();
139 stats.record_miss();
140 stats.record_miss();
141
142 assert_eq!(stats.total_requests(), 4);
143 }
144
145 #[test]
146 fn test_cache_stats_reset() {
147 let stats = CacheStats::new();
148
149 stats.record_hit();
150 stats.record_miss();
151 stats.record_set();
152
153 stats.reset();
154
155 assert_eq!(stats.hits(), 0);
156 assert_eq!(stats.misses(), 0);
157 assert_eq!(stats.sets(), 0);
158 }
159
160 #[test]
161 fn test_cache_stats_clone() {
162 let stats = CacheStats::new();
163 stats.record_hit();
164 stats.record_miss();
165
166 let cloned = stats.clone();
167
168 assert_eq!(cloned.hits(), 1);
169 assert_eq!(cloned.misses(), 1);
170 }
171}