origin_mcp_http/
activity.rs1use std::sync::{Arc, Mutex};
9use std::time::{Duration, Instant};
10
11#[derive(Debug, Clone, Default)]
13pub struct Activity {
14 last: Arc<Mutex<Option<Instant>>>,
15}
16
17impl Activity {
18 pub fn touch(&self) {
20 *self.last.lock().expect("activity poisoned") = Some(Instant::now());
21 }
22
23 pub fn has_seen_activity(&self) -> bool {
25 self.last.lock().expect("activity poisoned").is_some()
26 }
27
28 pub fn is_idle_for(&self, idle: Duration) -> bool {
32 match *self.last.lock().expect("activity poisoned") {
33 None => true,
34 Some(at) => at.elapsed() >= idle,
35 }
36 }
37
38 pub fn is_active(&self, idle: Duration) -> bool {
40 self.has_seen_activity() && !self.is_idle_for(idle)
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn a_fresh_activity_has_seen_nothing_and_is_idle() {
50 let activity = Activity::default();
51
52 assert!(!activity.has_seen_activity());
53 assert!(activity.is_idle_for(Duration::from_secs(1)));
54 assert!(!activity.is_active(Duration::from_secs(60)));
55 }
56
57 #[test]
58 fn touching_makes_it_active_then_idle_again() {
59 let activity = Activity::default();
60 activity.touch();
61
62 assert!(activity.has_seen_activity());
63 assert!(activity.is_active(Duration::from_secs(60)));
64 assert!(!activity.is_idle_for(Duration::from_secs(60)));
65
66 std::thread::sleep(Duration::from_millis(20));
68 assert!(activity.is_idle_for(Duration::from_millis(10)));
69 assert!(!activity.is_active(Duration::from_millis(10)));
70 }
71}