throttle_timer/
lib.rs

1//! Throttle events and record event stats with a simple library
2//!
3//! throttle_timer has no dependencies
4//!
5//! `ThrottleTimer` struct is created with a max frequency and label
6//!
7//! ```ThrottleTimer::new(Duration::from_secs(1_u64), &"Once every second");```
8//!
9//! Calling ```run()``` will check the last call time. If max frequency time has not passed the fn will return false.
10//! If max_frequency duration has passed since the last call then the fn will return true
11//!
12//!
13//! # Example
14//! ```
15//! use std::time::Duration;
16//! use throttle_timer::ThrottleTimer;
17//!
18//! let mut break_timer = ThrottleTimer::new(Duration::from_secs(10_u64), &"Break");
19//! let mut val = 0_u8;
20//!
21//! // timers always run when no previous runs
22//! break_timer.run(&mut || val += 1);
23//! for _ in 0..100 {
24//!     // timer will not run as 10 secs has not passed
25//!     // do run will return false
26//!     break_timer.run(&mut || val += 1);
27//! }
28//!
29//! break_timer.print_stats();
30//! // Break called 0/sec, total calls 1, has been running for 10us
31//!
32//! assert_eq!(break_timer.total_calls(), &1);
33//! assert_eq!(val, 1_u8);
34//!
35//!
36//! ```
37
38use std::thread;
39use std::time::Duration;
40use std::time::Instant;
41use std::time::SystemTime;
42
43#[derive(Debug)]
44pub struct ThrottleTimer {
45    maybe_last_called_time: Option<Instant>,
46    total_calls: usize,
47    created_date: SystemTime,
48    max_frequency: Duration,
49    event_name: &'static str,
50}
51
52///
53/// # Example
54/// ```
55/// use std::time::Duration;
56/// use throttle_timer::ThrottleTimer;
57///
58/// let mut break_timer: ThrottleTimer = ThrottleTimer::new(Duration::from_secs(1_u64), &"Break");
59/// let do_break_flag = break_timer.run(&mut || {});
60///
61/// // Timers always run when no previous runs
62/// assert!(do_break_flag == true);
63/// if do_break_flag {
64///     println!("timer do run flag is set to true")
65/// }
66///
67/// // Run flag false as no time has passed
68/// assert!(break_timer.run(&mut || {}) == false);
69/// ```
70impl ThrottleTimer {
71    pub fn new(max_frequency: std::time::Duration, event_name: &'static str) -> Self {
72        Self {
73            maybe_last_called_time: None,
74            max_frequency,
75            event_name,
76            total_calls: 0,
77            created_date: SystemTime::now(),
78        }
79    }
80    pub const fn event_name(&self) -> &str {
81        self.event_name
82    }
83    pub const fn total_calls(&self) -> &usize {
84        &self.total_calls
85    }
86    pub const fn max_frequency(&self) -> &Duration {
87        &self.max_frequency
88    }
89    pub const fn created_date(&self) -> SystemTime {
90        self.created_date
91    }
92    pub fn wait_time(&self) -> Duration {
93        match self.maybe_last_called_time {
94            None => Duration::from_secs(0),
95            Some(last_time) => {
96                (self.max_frequency
97                    - Instant::now()
98                        .duration_since(last_time)
99                        .min(self.max_frequency))
100            }
101        }
102    }
103
104    /// Prints total calls and calls/sec
105    pub fn print_stats(&self) {
106        match self.created_date.elapsed() {
107            Ok(created_time_elapsed) => {
108                println!(
109                    "{} called {}/sec, total calls {}, has been running for {:?}",
110                    self.event_name,
111                    created_time_elapsed.as_secs() / self.total_calls as u64,
112                    self.total_calls,
113                    created_time_elapsed,
114                );
115            }
116            Err(e) => eprintln!("{:?}", e),
117        }
118    }
119
120    /// Calling ```run()``` will check the last call time. If max frequency time has not passed the fn will return false.
121    /// If max_frequency duration has passed since the last call then the fn will return true
122    pub fn can_run(&mut self) -> bool {
123        match self.maybe_last_called_time {
124            None => true,
125            Some(last_time) => Instant::now().duration_since(last_time) >= self.max_frequency,
126        }
127    }
128
129    pub fn run_throttle_cb(&mut self, success: &mut FnMut(), throttled: &mut FnMut()) -> bool {
130        let run_flag: bool = self.can_run();
131
132        if run_flag {
133            self.maybe_last_called_time = Some(Instant::now());
134            self.total_calls += 1;
135            success();
136        } else {
137            throttled()
138        }
139        run_flag
140    }
141
142    /// Calling ```run()``` will check the last call time. If max frequency time has not passed the fn will return false.
143    /// If max_frequency duration has passed since the last call then the fn will return true
144    pub fn run(&mut self, success: &mut FnMut()) -> bool {
145        self.run_throttle_cb(success, &mut || {})
146    }
147
148    /// Calling ```run()``` will check the last call time. If max frequency time has not passed the fn will return false.
149    /// If max_frequency duration has passed since the last call then the fn will return true
150    pub fn run_wait(&mut self, success: &mut FnMut()) {
151        thread::sleep(self.wait_time());
152        self.run_throttle_cb(success, &mut || {});
153    }
154
155    // Same as run but will print a message if throttled
156    pub fn run_with_msg(&mut self, success: &mut FnMut()) -> bool {
157        let did_run = self.run(success);
158        if !did_run {
159            println!(
160                "{} throttled, last time {:?}",
161                self.event_name(),
162                Instant::now().duration_since(self.maybe_last_called_time.unwrap())
163            );
164        }
165        did_run
166    }
167}
168
169#[cfg(test)]
170mod test {
171    use super::ThrottleTimer;
172    use std::{thread, time::Duration};
173
174    #[test]
175    fn test_run() {
176        let mut break_timer: ThrottleTimer =
177            ThrottleTimer::new(Duration::from_secs(45_000_u64), &"Break");
178        let run_flag = break_timer.run(&mut || {});
179
180        // timers always run when no previous runs
181        assert!(run_flag);
182        if run_flag {
183            println!("timer do run flag is set to true")
184        }
185        break_timer.event_name();
186        break_timer.total_calls();
187        break_timer.max_frequency();
188        break_timer.created_date();
189    }
190
191    #[test]
192    fn test_run_with_msg() {
193        let mut break_timer: ThrottleTimer =
194            ThrottleTimer::new(Duration::from_secs(45_000_u64), &"Break");
195        let run_flag = break_timer.run_with_msg(&mut || {});
196
197        // timers always run when no previous runs
198        assert!(run_flag);
199    }
200
201    #[test]
202    fn test_call_count() {
203        let mut break_timer: ThrottleTimer =
204            ThrottleTimer::new(Duration::from_nanos(1_u64), &"Break");
205
206        for _ in 0..100 {
207            assert_eq!(break_timer.run(&mut || {}), true);
208            thread::sleep(Duration::from_nanos(100_u64));
209        }
210
211        // timers always run when no previous runs
212        assert_eq!(break_timer.total_calls, 100);
213        break_timer.print_stats();
214    }
215
216    #[test]
217    fn test_can_run() {
218        let mut break_timer: ThrottleTimer =
219            ThrottleTimer::new(Duration::from_secs(1_u64), &"Break");
220
221        assert!(break_timer.run(&mut || {}));
222        for _ in 0..100 {
223            assert!(!break_timer.can_run());
224            assert!(!break_timer.run(&mut || {}));
225        }
226
227        // timers always run when no previous runs
228        assert_eq!(break_timer.total_calls, 1);
229        break_timer.print_stats();
230    }
231
232    #[test]
233    fn test_print_debug() {
234        println!(
235            "{:?}",
236            ThrottleTimer::new(Duration::from_nanos(1_u64), &"Break")
237        );
238    }
239
240    #[test]
241    fn test_in_loop() {
242        let mut break_timer = ThrottleTimer::new(Duration::from_secs(10_u64), &"Break");
243
244        // timers always run when no previous runs
245        assert!(break_timer.run(&mut || {}));
246        for _ in 0..100 {
247            // timer will not run as 10 secs has not passed
248            // do run will return false
249            assert!(!break_timer.run(&mut || {}));
250        }
251        assert_eq!(break_timer.total_calls(), &1);
252    }
253
254    #[test]
255    fn test_run_wait() {
256        let mut break_timer = ThrottleTimer::new(Duration::from_nanos(10_u64), &"Break");
257
258        break_timer.run_wait(&mut || {});
259        break_timer.run_wait(&mut || {});
260        break_timer.run_wait(&mut || {});
261        assert_eq!(break_timer.total_calls(), &3);
262    }
263
264    #[test]
265    fn test_with_delay() {
266        let mut snack_timer: ThrottleTimer =
267            ThrottleTimer::new(Duration::from_secs(1_u64), &"Snack");
268        let run_flag = snack_timer.run(&mut || {});
269
270        // timers always run when no previous runs
271        assert!(run_flag);
272
273        let run_flag2 = snack_timer.run_with_msg(&mut || {});
274
275        // run flag false as no time has passed
276        assert_eq!(run_flag2, false);
277
278        thread::sleep(snack_timer.max_frequency);
279        assert!(snack_timer.run(&mut || {}));
280        thread::sleep(Duration::from_millis(100_u64));
281        assert!(!snack_timer.run(&mut || {}));
282        thread::sleep(Duration::from_secs(1_u64));
283        assert!(snack_timer.run(&mut || {}));
284    }
285}