minitimer/task/
frequency.rs1use std::{
2 iter::{Peekable, StepBy},
3 ops::RangeFrom,
4};
5
6use crate::utils::timestamp;
7
8pub(crate) type SecondsState = Peekable<StepBy<RangeFrom<u64>>>;
9const ONE_MINUTE: u64 = 60;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum FrequencySeconds {
14 Once(u64),
16 Repeated(u64),
18 CountDown(u64, u64),
20}
21
22impl FrequencySeconds {
23 pub(crate) fn interval(&self) -> u64 {
28 match self {
29 Self::Once(seconds) => *seconds,
30 Self::Repeated(seconds) => *seconds,
31 Self::CountDown(_, seconds) => *seconds,
32 }
33 }
34}
35
36impl Default for FrequencySeconds {
37 fn default() -> FrequencySeconds {
38 FrequencySeconds::Once(ONE_MINUTE)
39 }
40}
41
42#[derive(Clone)]
46#[allow(dead_code)]
47pub(crate) enum FrequencyState {
48 SecondsRepeated(SecondsState),
50 SecondsCountDown(u64, SecondsState),
52}
53
54impl From<FrequencySeconds> for FrequencyState {
55 fn from(frequency: FrequencySeconds) -> Self {
56 match frequency {
57 FrequencySeconds::Once(seconds) => {
58 assert!(seconds > 0, "once frequency must be greater than 0");
59 let state: SecondsState = ((timestamp() + seconds)..)
60 .step_by(seconds as usize)
61 .peekable();
62 FrequencyState::SecondsRepeated(state)
63 }
64 FrequencySeconds::Repeated(seconds) => {
65 assert!(seconds > 0, "repeated frequency must be greater than 0");
66 let state: SecondsState = ((timestamp() + seconds)..)
67 .step_by(seconds as usize)
68 .peekable();
69 FrequencyState::SecondsRepeated(state)
70 }
71 FrequencySeconds::CountDown(count_down, seconds) => {
72 assert!(seconds > 0, "countdown initial must be greater than 0");
73 let state: SecondsState = (timestamp() + seconds..)
74 .step_by(count_down as usize)
75 .peekable();
76 FrequencyState::SecondsCountDown(count_down, state)
77 }
78 }
79 }
80}
81
82impl FrequencyState {
83 #[allow(dead_code)]
84 pub(crate) fn peek_alarm_timestamp(&mut self) -> Option<u64> {
89 match self {
90 Self::SecondsRepeated(state) => state.peek().copied(),
91 Self::SecondsCountDown(_, state) => state.peek().copied(),
92 }
93 }
94
95 pub(crate) fn next_alarm_timestamp(&mut self) -> Option<u64> {
100 match self {
101 Self::SecondsRepeated(state) => state.next(),
102 Self::SecondsCountDown(_, state) => state.next(),
103 }
104 }
105
106 #[allow(dead_code)]
107 pub(crate) fn down_count(&mut self) {
109 if let Self::SecondsCountDown(count, _) = self {
110 *count = count.saturating_sub(1);
111 }
112 }
113
114 pub(crate) fn reset_from_timestamp(&mut self, base_timestamp: u64, interval: u64) {
123 let new_state: SecondsState = ((base_timestamp + interval)..)
124 .step_by(interval as usize)
125 .peekable();
126
127 match self {
128 Self::SecondsRepeated(_) => {
129 *self = Self::SecondsRepeated(new_state);
130 }
131 Self::SecondsCountDown(count, _) => {
132 *self = Self::SecondsCountDown(*count, new_state);
133 }
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_frequency_state_from_once() {
144 let freq = FrequencySeconds::Once(10);
145 let mut state = FrequencyState::from(freq);
146
147 let now = crate::utils::timestamp();
149 let alarm = state.peek_alarm_timestamp().unwrap();
150 assert!(alarm >= now + 10);
151
152 let alarm2 = state.peek_alarm_timestamp().unwrap();
154 assert_eq!(alarm, alarm2);
155
156 let alarm3 = state.next_alarm_timestamp().unwrap();
158 assert_eq!(alarm, alarm3);
159 }
160
161 #[test]
162 fn test_frequency_state_from_repeated() {
163 let freq = FrequencySeconds::Repeated(5);
164 let mut state = FrequencyState::from(freq);
165
166 let now = crate::utils::timestamp();
167 let alarm1 = state.next_alarm_timestamp().unwrap();
169 assert_eq!(alarm1, now + 5);
170
171 let alarm2 = state.next_alarm_timestamp().unwrap();
172 assert_eq!(alarm2, now + 10);
173
174 let alarm3 = state.next_alarm_timestamp().unwrap();
175 assert_eq!(alarm3, now + 15);
176 }
177
178 #[test]
179 fn test_frequency_state_from_countdown() {
180 let freq = FrequencySeconds::CountDown(2, 5); let state = FrequencyState::from(freq);
184
185 match state {
187 FrequencyState::SecondsCountDown(count, _) => assert_eq!(count, 2),
188 _ => panic!("Expected SecondsCountDown variant"),
189 }
190 }
191
192 #[test]
193 fn test_peek_alarm_timestamp() {
194 let freq = FrequencySeconds::Repeated(10);
195 let mut state = FrequencyState::from(freq);
196
197 let peek1 = state.peek_alarm_timestamp().unwrap();
199 let peek2 = state.peek_alarm_timestamp().unwrap();
200 assert_eq!(peek1, peek2);
201
202 let next1 = state.next_alarm_timestamp().unwrap();
204 assert_eq!(peek1, next1);
205
206 let peek3 = state.peek_alarm_timestamp().unwrap();
207 assert_ne!(peek1, peek3);
208 }
209
210 #[test]
211 fn test_reset_from_timestamp_repeated() {
212 let freq = FrequencySeconds::Repeated(10);
213 let mut state = FrequencyState::from(freq);
214
215 let _ = state.next_alarm_timestamp().unwrap();
217 let _ = state.next_alarm_timestamp().unwrap();
218
219 let reset_base = 1000;
221 state.reset_from_timestamp(reset_base, 10);
222
223 let next = state.peek_alarm_timestamp().unwrap();
225 assert_eq!(next, reset_base + 10);
226
227 let next2 = state.next_alarm_timestamp().unwrap();
229 assert_eq!(next2, reset_base + 10);
230
231 let next3 = state.next_alarm_timestamp().unwrap();
232 assert_eq!(next3, reset_base + 20);
233 }
234
235 #[test]
236 fn test_reset_from_timestamp_countdown() {
237 let freq = FrequencySeconds::CountDown(5, 10);
238 let mut state = FrequencyState::from(freq);
239
240 let reset_base = 2000;
242 state.reset_from_timestamp(reset_base, 10);
243
244 let next = state.peek_alarm_timestamp().unwrap();
246 assert_eq!(next, reset_base + 10);
247
248 match state {
250 FrequencyState::SecondsCountDown(count, _) => assert_eq!(count, 5),
251 _ => panic!("Expected SecondsCountDown variant"),
252 }
253 }
254
255 #[test]
256 fn test_frequency_seconds_interval() {
257 assert_eq!(FrequencySeconds::Once(30).interval(), 30);
258 assert_eq!(FrequencySeconds::Repeated(60).interval(), 60);
259 assert_eq!(FrequencySeconds::CountDown(3, 15).interval(), 15);
260 }
261}