1use std::time::Duration;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum TimerMode {
21 Once,
23 Repeating,
25}
26
27#[derive(Clone, Debug)]
30pub struct Timer {
31 duration: Duration,
32 elapsed: Duration,
33 mode: TimerMode,
34 paused: bool,
35 finished: bool,
36 times_finished: u64,
37}
38
39impl Timer {
40 pub fn new(duration: Duration, mode: TimerMode) -> Self {
42 Self { duration, elapsed: Duration::ZERO, mode, paused: false, finished: false, times_finished: 0 }
43 }
44
45 pub fn once(seconds: f32) -> Option<Self> {
48 Duration::try_from_secs_f32(seconds).ok().map(|d| Self::new(d, TimerMode::Once))
49 }
50
51 pub fn repeating(seconds: f32) -> Option<Self> {
53 Duration::try_from_secs_f32(seconds).ok().map(|d| Self::new(d, TimerMode::Repeating))
54 }
55
56 pub fn tick(&mut self, delta: Duration) -> &mut Self {
60 self.times_finished = 0;
61 if self.mode == TimerMode::Repeating { self.finished = false; }
62 if self.paused || (self.mode == TimerMode::Once && self.finished) {
63 return self;
64 }
65 if self.duration.is_zero() {
66 self.times_finished = 1;
67 self.finished = true;
68 return self;
69 }
70 let total = self.elapsed.as_nanos() + delta.as_nanos();
71 let duration = self.duration.as_nanos();
72 if total >= duration {
73 self.finished = true;
74 match self.mode {
75 TimerMode::Once => {
76 self.elapsed = self.duration;
77 self.times_finished = 1;
78 }
79 TimerMode::Repeating => {
80 self.times_finished = (total / duration).min(u64::MAX as u128) as u64;
81 self.elapsed = from_nanos(total % duration);
82 }
83 }
84 } else {
85 self.elapsed = from_nanos(total);
86 self.finished = false;
87 }
88 self
89 }
90
91 pub fn tick_secs(&mut self, seconds: f32) -> &mut Self {
94 match Duration::try_from_secs_f32(seconds) {
95 Ok(dt) => self.tick(dt),
96 Err(_) => {
97 self.times_finished = 0;
98 if self.mode == TimerMode::Repeating { self.finished = false; }
99 self
100 }
101 }
102 }
103
104 pub fn duration(&self) -> Duration {
106 self.duration
107 }
108 pub fn elapsed(&self) -> Duration {
110 self.elapsed
111 }
112 pub fn remaining(&self) -> Duration {
114 self.duration.saturating_sub(self.elapsed)
115 }
116 pub fn fraction(&self) -> f32 {
118 if self.duration.is_zero() {
119 return if self.finished { 1.0 } else { 0.0 };
120 }
121 (self.elapsed.as_secs_f64() / self.duration.as_secs_f64()).clamp(0.0, 1.0) as f32
122 }
123 pub fn finished(&self) -> bool {
125 self.finished
126 }
127 pub fn just_finished(&self) -> bool {
129 self.times_finished != 0
130 }
131 pub fn times_finished(&self) -> u64 {
133 self.times_finished
134 }
135 pub fn mode(&self) -> TimerMode {
137 self.mode
138 }
139 pub fn is_paused(&self) -> bool {
141 self.paused
142 }
143 pub fn set_paused(&mut self, paused: bool) {
145 self.paused = paused;
146 }
147 pub fn reset(&mut self) {
149 self.elapsed = Duration::ZERO;
150 self.finished = false;
151 self.times_finished = 0;
152 }
153 pub fn set_duration(&mut self, duration: Duration) {
155 self.duration = duration;
156 self.reset();
157 }
158 pub fn set_mode(&mut self, mode: TimerMode) {
160 self.mode = mode;
161 self.reset();
162 }
163}
164
165fn from_nanos(value: u128) -> Duration {
166 Duration::new((value / 1_000_000_000) as u64, (value % 1_000_000_000) as u32)
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 #[test]
173 fn once_edges_and_pause() {
174 let mut t = Timer::once(1.0).unwrap();
175 t.tick_secs(0.5);
176 assert_eq!(t.fraction(), 0.5);
177 t.set_paused(true);
178 t.tick_secs(2.0);
179 assert!(!t.finished());
180 t.set_paused(false);
181 t.tick_secs(2.0);
182 assert!(t.just_finished());
183 assert_eq!(t.remaining(), Duration::ZERO);
184 t.tick_secs(1.0);
185 assert!(t.finished());
186 assert!(!t.just_finished());
187 t.reset();
188 assert!(!t.finished());
189 assert_eq!(t.elapsed(), Duration::ZERO);
190 }
191 #[test]
192 fn repeating_counts_catchup_exactly_and_handles_invalid_deltas() {
193 let mut t = Timer::new(Duration::from_millis(100), TimerMode::Repeating);
194 t.tick(Duration::from_millis(250));
195 assert_eq!(t.times_finished(), 2);
196 assert_eq!(t.elapsed(), Duration::from_millis(50));
197 t.tick_secs(f32::NAN);
198 assert!(!t.just_finished());
199 assert_eq!(t.elapsed(), Duration::from_millis(50));
200 t.tick(Duration::from_millis(50));
201 assert_eq!(t.times_finished(), 1);
202 assert_eq!(t.elapsed(), Duration::ZERO);
203 assert!(Timer::once(-1.0).is_none());
204 assert!(Timer::repeating(f32::INFINITY).is_none());
205 }
206 #[test]
207 fn zero_and_max_durations_are_bounded() {
208 let mut t = Timer::new(Duration::ZERO, TimerMode::Repeating);
209 t.tick(Duration::MAX);
210 assert_eq!(t.times_finished(), 1);
211 t.set_duration(Duration::from_nanos(1));
212 t.tick(Duration::MAX);
213 assert_eq!(t.times_finished(), u64::MAX);
214 t.set_duration(Duration::MAX);
215 t.tick(Duration::MAX);
216 assert_eq!(t.times_finished(), 1);
217 }
218}