open_sesame/util/
timeout.rs1use std::time::{Duration, Instant};
6
7#[derive(Debug, Clone)]
9pub struct TimeoutTracker {
10 started_at: Option<Instant>,
12 duration: Duration,
14}
15
16impl TimeoutTracker {
17 pub fn new(duration_ms: u64) -> Self {
19 Self {
20 started_at: None,
21 duration: Duration::from_millis(duration_ms),
22 }
23 }
24
25 pub fn start(&mut self) {
27 self.started_at = Some(Instant::now());
28 }
29
30 pub fn reset(&mut self) {
32 self.start();
33 }
34
35 pub fn cancel(&mut self) {
37 self.started_at = None;
38 }
39
40 pub fn is_active(&self) -> bool {
42 self.started_at.is_some() && !self.has_elapsed()
43 }
44
45 pub fn has_elapsed(&self) -> bool {
47 self.started_at
48 .map(|start| start.elapsed() >= self.duration)
49 .unwrap_or(false)
50 }
51
52 pub fn remaining(&self) -> Option<Duration> {
54 self.started_at.and_then(|start| {
55 let elapsed = start.elapsed();
56 if elapsed >= self.duration {
57 None
58 } else {
59 Some(self.duration - elapsed)
60 }
61 })
62 }
63
64 pub fn elapsed(&self) -> Option<Duration> {
66 self.started_at.map(|start| start.elapsed())
67 }
68
69 pub fn deadline(&self) -> Option<Instant> {
71 self.started_at.map(|start| start + self.duration)
72 }
73
74 pub fn set_duration(&mut self, duration_ms: u64) {
76 self.duration = Duration::from_millis(duration_ms);
77 }
78}
79
80impl Default for TimeoutTracker {
81 fn default() -> Self {
82 Self::new(200) }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use std::thread::sleep;
90
91 #[test]
92 fn test_timeout_not_started() {
93 let tracker = TimeoutTracker::new(100);
94 assert!(!tracker.is_active());
95 assert!(!tracker.has_elapsed());
96 assert!(tracker.remaining().is_none());
97 }
98
99 #[test]
100 fn test_timeout_started() {
101 let mut tracker = TimeoutTracker::new(1000);
102 tracker.start();
103 assert!(tracker.is_active());
104 assert!(!tracker.has_elapsed());
105 assert!(tracker.remaining().is_some());
106 }
107
108 #[test]
109 fn test_timeout_elapsed() {
110 let mut tracker = TimeoutTracker::new(10);
111 tracker.start();
112 sleep(Duration::from_millis(20));
113 assert!(tracker.has_elapsed());
114 assert!(!tracker.is_active());
115 }
116
117 #[test]
118 fn test_timeout_cancel() {
119 let mut tracker = TimeoutTracker::new(1000);
120 tracker.start();
121 assert!(tracker.is_active());
122 tracker.cancel();
123 assert!(!tracker.is_active());
124 }
125}