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
77impl<T> Iterator for PullTimer<T> {
78    type Item = T;
79
80    fn next(&mut self) -> Option<T> {
81        let &(delta, _) = self.0.front()?;
82
83        if delta == 0 {
84            self.0.pop_front().map(|(_, event)| event)
85        } else {
86            None
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn timer_preserves_fifo_order() {
97        let mut timer = PullTimer::new();
98
99        timer.add(0, "testing");
100        timer.add(0, "one two three");
101
102        assert_eq!(timer.next(), Some("testing"));
103        assert_eq!(timer.next(), Some("one two three"));
104        assert_eq!(timer.next(), None);
105    }
106
107    #[test]
108    fn timer_fires_in_order() {
109        let mut timer = PullTimer::new();
110
111        timer.add(4, "test");
112        timer.add(3, "a");
113        timer.add(2, "is");
114        timer.add(1, "this");
115
116        timer.tick(4);
117
118        assert_eq!(timer.next(), Some("this"));
119        assert_eq!(timer.next(), Some("is"));
120        assert_eq!(timer.next(), Some("a"));
121        assert_eq!(timer.next(), Some("test"));
122        assert_eq!(timer.next(), None);
123    }
124
125    #[test]
126    fn timer_fires_in_time() {
127        let mut timer = PullTimer::new();
128
129        timer.add(40, 40);
130        timer.add(20, 20);
131        timer.add(0, 0);
132        timer.add(30, 30);
133        timer.add(10, 10);
134
135        for i in 0..=41 {
136            if let Some(value) = timer.next() {
137                assert_eq!(value, i);
138            }
139            timer.tick(1);
140        }
141    }
142
143    #[test]
144    fn timer_next_in() {
145        let mut timer = PullTimer::new();
146
147        timer.add(0, "hi");
148        timer.add(20, "!");
149        timer.add(10, "there");
150
151        assert_eq!(timer.next_in(), Some(0));
152        assert_eq!(timer.next(), Some("hi"));
153
154        assert_eq!(timer.next_in(), Some(10));
155
156        timer.tick(10);
157        assert_eq!(timer.next_in(), Some(0));
158        assert_eq!(timer.next(), Some("there"));
159
160        timer.tick(3);
161        assert_eq!(timer.next_in(), Some(7));
162    }
163
164    #[test]
165    fn timer_remove() {
166        let mut timer = PullTimer::new();
167
168        timer.add(100, "boom!");
169        timer.tick(50);
170        assert_eq!(timer.remove("boom!"), Some(50));
171        assert_eq!(timer.next_in(), None);
172    }
173
174    #[test]
175    fn timer_fires_after_remove() {
176        let mut timer = PullTimer::new();
177
178        timer.add(30, 30);
179        timer.add(20, 20);
180        timer.add(40, 40);
181        timer.add(10, 10);
182        timer.add(50, 50);
183
184        assert_eq!(timer.remove(50), Some(50));
185        assert_eq!(timer.remove(10), Some(10));
186
187        for i in 0..=41 {
188            if let Some(value) = timer.next() {
189                assert_eq!(value, i);
190            }
191            timer.tick(1);
192        }
193    }
194}