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 inc_hits(&self) -> u64 {
59 self.hits.fetch_add(1, Ordering::Relaxed) + 1
60 }
61
62 #[must_use]
64 pub fn inc_misses(&self) -> u64 {
65 self.misses.fetch_add(1, Ordering::Relaxed) + 1
66 }
67
68 #[must_use]
70 pub fn inc_sets(&self) -> u64 {
71 self.sets.fetch_add(1, Ordering::Relaxed) + 1
72 }
73
74 #[must_use]
76 pub fn total_requests(&self) -> u64 {
77 self.hits() + self.misses()
78 }
79
80 #[must_use]
82 #[allow(clippy::cast_precision_loss)]
83 pub fn hit_rate(&self) -> f64 {
84 let total = self.total_requests();
85 if total == 0 {
86 return 0.0;
87 }
88 self.hits() as f64 / total as f64
89 }
90
91 #[must_use]
93 pub fn as_tuple(&self) -> (u64, u64, u64) {
94 (self.hits(), self.misses(), self.sets())
95 }
96
97 pub fn reset(&self) {
99 self.hits.store(0, Ordering::Relaxed);
100 self.misses.store(0, Ordering::Relaxed);
101 self.sets.store(0, Ordering::Relaxed);
102 }
103}
104
105impl Clone for CacheStats {
106 fn clone(&self) -> Self {
107 Self {
108 hits: AtomicU64::new(self.hits.load(Ordering::Relaxed)),
109 misses: AtomicU64::new(self.misses.load(Ordering::Relaxed)),
110 sets: AtomicU64::new(self.sets.load(Ordering::Relaxed)),
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_cache_stats_new() {
121 let stats = CacheStats::new();
122 assert_eq!(stats.hits(), 0);
123 assert_eq!(stats.misses(), 0);
124 assert_eq!(stats.sets(), 0);
125 }
126
127 #[test]
128 fn test_cache_stats_record() {
129 let stats = CacheStats::new();
130
131 stats.record_hit();
132 stats.record_hit();
133 stats.record_miss();
134 stats.record_set();
135 stats.record_set();
136 stats.record_set();
137
138 assert_eq!(stats.hits(), 2);
139 assert_eq!(stats.misses(), 1);
140 assert_eq!(stats.sets(), 3);
141 }
142
143 #[test]
144 fn test_cache_stats_hit_rate() {
145 let stats = CacheStats::new();
146
147 assert!((stats.hit_rate() - 0.0).abs() < f64::EPSILON);
148
149 stats.record_hit();
150 stats.record_hit();
151 stats.record_miss();
152
153 let rate = stats.hit_rate();
154 assert!((rate - 0.666_666_666_666_666_6).abs() < f64::EPSILON);
155 }
156
157 #[test]
158 fn test_cache_stats_total_requests() {
159 let stats = CacheStats::new();
160
161 stats.record_hit();
162 stats.record_hit();
163 stats.record_miss();
164 stats.record_miss();
165
166 assert_eq!(stats.total_requests(), 4);
167 }
168
169 #[test]
170 fn test_cache_stats_reset() {
171 let stats = CacheStats::new();
172
173 stats.record_hit();
174 stats.record_miss();
175 stats.record_set();
176
177 stats.reset();
178
179 assert_eq!(stats.hits(), 0);
180 assert_eq!(stats.misses(), 0);
181 assert_eq!(stats.sets(), 0);
182 }
183
184 #[test]
185 fn test_cache_stats_clone() {
186 let stats = CacheStats::new();
187 stats.record_hit();
188 stats.record_miss();
189
190 let cloned = stats.clone();
191
192 assert_eq!(cloned.hits(), 1);
193 assert_eq!(cloned.misses(), 1);
194 }
195}