cargo/util/
counter.rs
1use std::time::Instant;
2
3pub struct MetricsCounter<const N: usize> {
5 slots: [(usize, Instant); N],
7 index: usize,
10}
11
12impl<const N: usize> MetricsCounter<N> {
13 pub fn new(init: usize, init_at: Instant) -> Self {
15 assert!(N > 0, "number of slots must be greater than zero");
16 Self {
17 slots: [(init, init_at); N],
18 index: 0,
19 }
20 }
21
22 pub fn add(&mut self, data: usize, added_at: Instant) {
24 self.slots[self.index] = (data, added_at);
25 self.index = (self.index + 1) % N;
26 }
27
28 pub fn rate(&self) -> f32 {
30 let latest = self.slots[self.index.checked_sub(1).unwrap_or(N - 1)];
31 let oldest = self.slots[self.index];
32 let duration = (latest.1 - oldest.1).as_secs_f32();
33 let avg = (latest.0 - oldest.0) as f32 / duration;
34 if f32::is_nan(avg) {
35 0f32
36 } else {
37 avg
38 }
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::MetricsCounter;
45 use std::time::{Duration, Instant};
46
47 #[test]
48 fn counter() {
49 let now = Instant::now();
50 let mut counter = MetricsCounter::<3>::new(0, now);
51 assert_eq!(counter.rate(), 0f32);
52 counter.add(1, now + Duration::from_secs(1));
53 assert_eq!(counter.rate(), 1f32);
54 counter.add(4, now + Duration::from_secs(2));
55 assert_eq!(counter.rate(), 2f32);
56 counter.add(7, now + Duration::from_secs(3));
57 assert_eq!(counter.rate(), 3f32);
58 counter.add(12, now + Duration::from_secs(4));
59 assert_eq!(counter.rate(), 4f32);
60 }
61
62 #[test]
63 #[should_panic(expected = "number of slots must be greater than zero")]
64 fn counter_zero_slot() {
65 let _counter = MetricsCounter::<0>::new(0, Instant::now());
66 }
67}