Skip to main content

pull_timer/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::collections::VecDeque;
4
5#[derive(Debug, Clone)]
6pub struct PullTimer<T>(VecDeque<(u32, T)>);
7
8impl<T> PullTimer<T> {
9    pub fn new() -> PullTimer<T> {
10        PullTimer(VecDeque::new())
11    }
12
13    pub fn next_in(&self) -> Option<u32> {
14        self.0.front().map(|&(deadline, _)| deadline)
15    }
16
17    pub fn tick(&mut self, elapsed: u32) {
18        let mut remaining = elapsed;
19        for (delta, _) in &mut self.0 {
20            let temp = *delta;
21            *delta = delta.saturating_sub(elapsed);
22            remaining = remaining.saturating_sub(temp);
23
24            if remaining == 0 {
25                break;
26            }
27        }
28    }
29
30    pub fn add(&mut self, deadline: u32, event: T) {
31        let mut sum = 0;
32        let mut insertion_point = 0;
33
34        for (index, &(delta, _)) in self.0.iter().enumerate() {
35            if sum + delta > deadline {
36                break;
37            }
38            insertion_point = index + 1;
39            sum += delta;
40        }
41
42        let insertion_delta = deadline - sum;
43
44        if let Some((delta, _)) = &mut self.0.get_mut(insertion_point) {
45            *delta = delta.saturating_sub(insertion_delta);
46        }
47
48        self.0.insert(insertion_point, (insertion_delta, event));
49    }
50
51    pub fn remove(&mut self, event: T) -> Option<u32>
52    where
53        T: PartialEq,
54    {
55        let mut sum = 0;
56        let mut target = None;
57
58        for (index, &(delta, ref element)) in self.0.iter().enumerate() {
59            sum += delta;
60            if *element == event {
61                target = Some(index);
62                break;
63            }
64        }
65
66        let index = target?;
67        let (delta, _) = self.0.remove(index)?;
68
69        if let Some((next_delta, _)) = self.0.get_mut(index) {
70            *next_delta += delta;
71        }
72
73        Some(sum)
74    }
75
76    pub fn poll(&mut self) -> Option<T> {
77        let &(delta, _) = self.0.front()?;
78
79        if delta == 0 {
80            self.0.pop_front().map(|(_, event)| event)
81        } else {
82            None
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn timer_preserves_fifo_order() {
93        let mut timer = PullTimer::new();
94
95        timer.add(0, "testing");
96        timer.add(0, "one two three");
97
98        assert_eq!(timer.poll(), Some("testing"));
99        assert_eq!(timer.poll(), Some("one two three"));
100        assert_eq!(timer.poll(), None);
101    }
102
103    #[test]
104    fn timer_fires_in_order() {
105        let mut timer = PullTimer::new();
106
107        timer.add(4, "test");
108        timer.add(3, "a");
109        timer.add(2, "is");
110        timer.add(1, "this");
111
112        timer.tick(4);
113
114        assert_eq!(timer.poll(), Some("this"));
115        assert_eq!(timer.poll(), Some("is"));
116        assert_eq!(timer.poll(), Some("a"));
117        assert_eq!(timer.poll(), Some("test"));
118        assert_eq!(timer.poll(), None);
119    }
120
121    #[test]
122    fn timer_fires_in_time() {
123        let mut timer = PullTimer::new();
124
125        timer.add(40, 40);
126        timer.add(20, 20);
127        timer.add(0, 0);
128        timer.add(30, 30);
129        timer.add(10, 10);
130
131        for i in 0..=41 {
132            if let Some(value) = timer.poll() {
133                assert_eq!(value, i);
134            }
135            timer.tick(1);
136        }
137    }
138
139    #[test]
140    fn timer_next_in() {
141        let mut timer = PullTimer::new();
142
143        timer.add(0, "hi");
144        timer.add(20, "!");
145        timer.add(10, "there");
146
147        assert_eq!(timer.next_in(), Some(0));
148        assert_eq!(timer.poll(), Some("hi"));
149
150        assert_eq!(timer.next_in(), Some(10));
151
152        timer.tick(10);
153        assert_eq!(timer.next_in(), Some(0));
154        assert_eq!(timer.poll(), Some("there"));
155
156        timer.tick(3);
157        assert_eq!(timer.next_in(), Some(7));
158    }
159
160    #[test]
161    fn timer_remove() {
162        let mut timer = PullTimer::new();
163
164        timer.add(100, "boom!");
165        timer.tick(50);
166        assert_eq!(timer.remove("boom!"), Some(50));
167        assert_eq!(timer.next_in(), None);
168    }
169
170    #[test]
171    fn timer_fires_after_remove() {
172        let mut timer = PullTimer::new();
173
174        timer.add(30, 30);
175        timer.add(20, 20);
176        timer.add(40, 40);
177        timer.add(10, 10);
178        timer.add(50, 50);
179
180        assert_eq!(timer.remove(50), Some(50));
181        assert_eq!(timer.remove(10), Some(10));
182
183        for i in 0..=41 {
184            if let Some(value) = timer.poll() {
185                assert_eq!(value, i);
186            }
187            timer.tick(1);
188        }
189    }
190}