use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy)]
pub struct ScanTimer {
hard_deadline: Instant,
min_runtime: Instant,
max_silence: Duration,
last_activity: Instant,
}
impl ScanTimer {
pub fn new(
max_total_duration: Duration,
min_runtime_duration: Duration,
max_silence: Duration,
) -> Self {
let now = Instant::now();
Self {
hard_deadline: now + max_total_duration,
min_runtime: now + min_runtime_duration,
max_silence,
last_activity: now,
}
}
pub fn mark_activity(&mut self) {
self.last_activity = Instant::now();
}
pub fn time_until_next_tick(&self) -> Duration {
let now = Instant::now();
let time_since_last = now.duration_since(self.last_activity);
self.max_silence
.checked_sub(time_since_last)
.unwrap_or_else(|| Duration::from_millis(100))
}
pub fn has_expired(&self) -> bool {
let now = Instant::now();
if now > self.hard_deadline {
return true;
}
let time_since_last = now.duration_since(self.last_activity);
if now > self.min_runtime && time_since_last >= self.max_silence {
return true;
}
false
}
pub fn should_break_on_timeout(&self) -> bool {
Instant::now() >= self.min_runtime
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread::sleep;
#[test]
fn test_initialization() {
let timer = ScanTimer::new(
Duration::from_secs(10),
Duration::from_secs(5),
Duration::from_secs(1),
);
assert!(!timer.has_expired());
assert!(!timer.should_break_on_timeout());
}
#[test]
fn test_mark_activity() {
let mut timer = ScanTimer::new(
Duration::from_secs(10),
Duration::from_secs(5),
Duration::from_millis(50),
);
let wait_time1 = timer.time_until_next_tick();
sleep(Duration::from_millis(10));
let wait_time2 = timer.time_until_next_tick();
assert!(wait_time2 < wait_time1);
timer.mark_activity();
let wait_time3 = timer.time_until_next_tick();
assert!(wait_time3 > wait_time2);
}
#[test]
fn test_time_until_next_tick_fallback() {
let timer = ScanTimer::new(
Duration::from_secs(10),
Duration::from_secs(5),
Duration::from_millis(10),
);
sleep(Duration::from_millis(15));
assert_eq!(timer.time_until_next_tick(), Duration::from_millis(100));
}
#[test]
fn test_hard_deadline_expiration() {
let timer = ScanTimer::new(
Duration::from_millis(10), Duration::from_millis(100), Duration::from_secs(1),
);
assert!(!timer.has_expired());
sleep(Duration::from_millis(15));
assert!(timer.has_expired());
}
#[test]
fn test_silence_expiration() {
let timer = ScanTimer::new(
Duration::from_secs(10),
Duration::from_millis(10), Duration::from_millis(10), );
assert!(!timer.has_expired());
sleep(Duration::from_millis(25)); assert!(timer.has_expired());
}
#[test]
fn test_should_break_on_timeout() {
let timer = ScanTimer::new(
Duration::from_secs(10),
Duration::from_millis(10),
Duration::from_secs(1),
);
assert!(!timer.should_break_on_timeout());
sleep(Duration::from_millis(15));
assert!(timer.should_break_on_timeout());
}
}