1use tokio::time::{self, Duration, Instant, Interval};
2
3pub struct EasedInterval {
4 started: Option<Instant>,
5 delay: Duration,
6 interval: Interval,
7}
8
9impl EasedInterval {
10 pub fn new(delay: Duration, interval: Duration) -> Self {
11 Self {
12 started: Some(Instant::now()),
13 delay,
14 interval: time::interval(interval),
15 }
16 }
17
18 pub async fn tick(&mut self) {
19 if let Some(started) = &self.started {
20 let elapsed = Instant::now().duration_since(*started);
21 if let Some(remaining) = self.delay.checked_sub(elapsed) {
22 time::sleep(remaining).await;
23 }
24 self.started = None;
25 self.interval.reset();
26 } else {
27 self.interval.tick().await;
28 }
29 }
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[tokio::test]
37 async fn test_eased_interval() {
38 let mut interval =
39 EasedInterval::new(Duration::from_millis(100), Duration::from_millis(50));
40 tokio::select! {
41 _ = interval.tick() => panic!("interval is not expected to tick yet"),
42 _ = time::sleep(Duration::from_millis(50)) => (),
43 };
44 tokio::select! {
45 _ = interval.tick() => (),
46 _ = time::sleep(Duration::from_millis(75)) => panic!("interval was expected to tick"),
47 };
48 tokio::select! {
49 _ = interval.tick() => panic!("interval is not expected to tick yet"),
50 _ = time::sleep(Duration::from_millis(35)) => (),
51 };
52 tokio::select! {
53 _ = interval.tick() => (),
54 _ = time::sleep(Duration::from_millis(25)) => panic!("interval was expected to tick"),
55 };
56 tokio::select! {
57 _ = interval.tick() => (),
58 _ = time::sleep(Duration::from_millis(60)) => panic!("interval was expected to tick"),
59 };
60 }
61}