use std::collections::VecDeque;
use std::iter::Sum;
use std::ops::{AddAssign, SubAssign};
use std::time::{Duration, Instant};
#[derive(Debug)]
pub(crate) struct ValueHistory<T> {
value: T,
history: VecDeque<(Instant, T)>,
max_time: Duration,
}
const DEFAULT_VALUE_HISTORY_DURATION: Duration = Duration::from_secs(1);
impl<T: Default> Default for ValueHistory<T> {
fn default() -> Self {
Self {
value: Default::default(),
history: Default::default(),
max_time: DEFAULT_VALUE_HISTORY_DURATION,
}
}
}
impl<T> ValueHistory<T>
where
T: Copy + AddAssign + SubAssign + Sum,
{
pub fn push(&mut self, t: Instant, v: T) {
self.value += v;
self.history.push_back((t, v));
}
pub fn sum(&self) -> T {
self.value
}
pub fn purge_old(&mut self, now: Instant) {
while {
let Some(front_t) = self.history.front().map(|v| v.0) else {
return;
};
now.duration_since(front_t) > self.max_time
} {
if let Some((_, v)) = self.history.pop_front() {
self.value -= v;
}
}
}
}
#[allow(clippy::unchecked_duration_subtraction)]
#[cfg(test)]
mod test {
use std::time::{Duration, Instant};
use super::ValueHistory;
#[test]
fn with_value_test() {
let now = Instant::now();
let mut h = ValueHistory {
value: 11,
max_time: Duration::from_secs(1),
..Default::default()
};
assert_eq!(h.sum(), 11);
h.purge_old(now);
assert_eq!(h.sum(), 11);
h.push(now - Duration::from_millis(1500), 22);
h.push(now - Duration::from_millis(500), 22);
assert_eq!(h.sum(), 11 + 22 + 22);
h.purge_old(now);
assert_eq!(h.sum(), 11 + 22);
h.push(now, 0);
assert_eq!(h.sum(), 11 + 22);
}
#[test]
fn test() {
let now = Instant::now();
let mut h = ValueHistory::default();
assert_eq!(h.sum(), 0);
h.push(now - Duration::from_millis(1500), 22);
assert_eq!(h.sum(), 22);
h.purge_old(now);
assert_eq!(h.sum(), 0);
h.push(now - Duration::from_millis(700), 22);
h.push(now - Duration::from_millis(500), 33);
assert_eq!(h.sum(), 22 + 33);
h.purge_old(now);
assert_eq!(h.sum(), 22 + 33);
h.purge_old(now + Duration::from_millis(400));
assert_eq!(h.sum(), 33);
h.purge_old(now + Duration::from_millis(600));
assert_eq!(h.sum(), 0);
}
}