1use std::time;
6
7pub type Duration = time::Duration;
9
10pub type Instant = time::Instant;
12
13#[derive(Debug, Clone)]
17pub struct Time {
18 elapsed: Duration,
20 delta: Duration,
22 fixed_timestep: Duration,
24 accumulator: Duration,
26 start_instant: Instant,
28 last_instant: Instant,
30 frame_count: u64,
32 time_scale: f64,
34}
35
36impl Default for Time {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl Time {
43 #[must_use]
45 pub fn new() -> Self {
46 let now = Instant::now();
47 Self {
48 elapsed: Duration::ZERO,
49 delta: Duration::ZERO,
50 fixed_timestep: Duration::from_secs_f64(1.0 / 60.0), accumulator: Duration::ZERO,
52 start_instant: now,
53 last_instant: now,
54 frame_count: 0,
55 time_scale: 1.0,
56 }
57 }
58
59 #[must_use]
61 pub fn with_fixed_timestep(fixed_hz: f64) -> Self {
62 let mut time = Self::new();
63 time.fixed_timestep = Duration::from_secs_f64(1.0 / fixed_hz);
64 time
65 }
66
67 pub fn update(&mut self) {
69 let now = Instant::now();
70 let raw_delta = now.duration_since(self.last_instant);
71
72 let scaled_delta = Duration::from_secs_f64(raw_delta.as_secs_f64() * self.time_scale);
74
75 self.delta = scaled_delta;
76 self.elapsed += scaled_delta;
77 self.accumulator += scaled_delta;
78 self.last_instant = now;
79 self.frame_count += 1;
80 }
81
82 pub fn update_with_delta(&mut self, delta: Duration) {
84 let scaled_delta = Duration::from_secs_f64(delta.as_secs_f64() * self.time_scale);
85
86 self.delta = scaled_delta;
87 self.elapsed += scaled_delta;
88 self.accumulator += scaled_delta;
89 self.frame_count += 1;
90 }
91
92 #[inline]
94 #[must_use]
95 pub fn elapsed(&self) -> Duration {
96 self.elapsed
97 }
98
99 #[inline]
101 #[must_use]
102 pub fn elapsed_secs(&self) -> f32 {
103 self.elapsed.as_secs_f32()
104 }
105
106 #[inline]
108 #[must_use]
109 pub fn elapsed_secs_f64(&self) -> f64 {
110 self.elapsed.as_secs_f64()
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn delta(&self) -> Duration {
117 self.delta
118 }
119
120 #[inline]
122 #[must_use]
123 pub fn delta_secs(&self) -> f32 {
124 self.delta.as_secs_f32()
125 }
126
127 #[inline]
129 #[must_use]
130 pub fn delta_secs_f64(&self) -> f64 {
131 self.delta.as_secs_f64()
132 }
133
134 #[inline]
136 #[must_use]
137 pub fn fixed_timestep(&self) -> Duration {
138 self.fixed_timestep
139 }
140
141 #[inline]
143 #[must_use]
144 pub fn fixed_timestep_secs(&self) -> f32 {
145 self.fixed_timestep.as_secs_f32()
146 }
147
148 #[inline]
150 pub fn set_fixed_timestep(&mut self, timestep: Duration) {
151 self.fixed_timestep = timestep;
152 }
153
154 #[inline]
156 pub fn set_fixed_timestep_hz(&mut self, hz: f64) {
157 self.fixed_timestep = Duration::from_secs_f64(1.0 / hz);
158 }
159
160 #[inline]
170 pub fn should_do_fixed_update(&mut self) -> bool {
171 if self.accumulator >= self.fixed_timestep {
172 self.accumulator -= self.fixed_timestep;
173 true
174 } else {
175 false
176 }
177 }
178
179 #[inline]
183 #[must_use]
184 pub fn interpolation_factor(&self) -> f32 {
185 (self.accumulator.as_secs_f64() / self.fixed_timestep.as_secs_f64()) as f32
186 }
187
188 #[inline]
190 #[must_use]
191 pub fn frame_count(&self) -> u64 {
192 self.frame_count
193 }
194
195 #[inline]
197 #[must_use]
198 pub fn fps(&self) -> f64 {
199 if self.elapsed.is_zero() {
200 0.0
201 } else {
202 self.frame_count as f64 / self.elapsed.as_secs_f64()
203 }
204 }
205
206 #[inline]
208 #[must_use]
209 pub fn time_scale(&self) -> f64 {
210 self.time_scale
211 }
212
213 #[inline]
220 pub fn set_time_scale(&mut self, scale: f64) {
221 self.time_scale = scale.max(0.0);
222 }
223
224 #[inline]
226 pub fn pause(&mut self) {
227 self.time_scale = 0.0;
228 }
229
230 #[inline]
232 pub fn resume(&mut self) {
233 self.time_scale = 1.0;
234 }
235
236 #[inline]
238 #[must_use]
239 pub fn is_paused(&self) -> bool {
240 self.time_scale == 0.0
241 }
242
243 #[inline]
245 #[must_use]
246 pub fn start_instant(&self) -> Instant {
247 self.start_instant
248 }
249
250 pub fn reset(&mut self) {
252 let now = Instant::now();
253 self.elapsed = Duration::ZERO;
254 self.delta = Duration::ZERO;
255 self.accumulator = Duration::ZERO;
256 self.start_instant = now;
257 self.last_instant = now;
258 self.frame_count = 0;
259 }
261}
262
263#[derive(Debug, Clone)]
265pub struct Stopwatch {
266 start: Option<Instant>,
267 elapsed: Duration,
268 running: bool,
269}
270
271impl Default for Stopwatch {
272 fn default() -> Self {
273 Self::new()
274 }
275}
276
277impl Stopwatch {
278 #[must_use]
280 pub fn new() -> Self {
281 Self {
282 start: None,
283 elapsed: Duration::ZERO,
284 running: false,
285 }
286 }
287
288 #[must_use]
290 pub fn started() -> Self {
291 let mut sw = Self::new();
292 sw.start();
293 sw
294 }
295
296 pub fn start(&mut self) {
298 if !self.running {
299 self.start = Some(Instant::now());
300 self.running = true;
301 }
302 }
303
304 pub fn stop(&mut self) {
306 if self.running {
307 if let Some(start) = self.start.take() {
308 self.elapsed += start.elapsed();
309 }
310 self.running = false;
311 }
312 }
313
314 pub fn reset(&mut self) {
316 self.start = None;
317 self.elapsed = Duration::ZERO;
318 self.running = false;
319 }
320
321 pub fn restart(&mut self) {
323 self.reset();
324 self.start();
325 }
326
327 #[must_use]
329 pub fn elapsed(&self) -> Duration {
330 let mut total = self.elapsed;
331 if let Some(start) = self.start {
332 total += start.elapsed();
333 }
334 total
335 }
336
337 #[must_use]
339 pub fn elapsed_secs(&self) -> f32 {
340 self.elapsed().as_secs_f32()
341 }
342
343 #[must_use]
345 pub fn is_running(&self) -> bool {
346 self.running
347 }
348}
349
350#[cfg(feature = "async")]
356pub mod async_time {
357 use super::Duration;
358
359 pub async fn sleep(duration: Duration) {
374 tokio::time::sleep(duration).await;
375 }
376
377 pub async fn sleep_secs(secs: f64) {
379 tokio::time::sleep(Duration::from_secs_f64(secs)).await;
380 }
381
382 pub async fn sleep_millis(millis: u64) {
384 tokio::time::sleep(Duration::from_millis(millis)).await;
385 }
386
387 pub struct Interval {
405 inner: tokio::time::Interval,
406 }
407
408 impl Interval {
409 #[must_use]
411 pub fn new(period: Duration) -> Self {
412 Self {
413 inner: tokio::time::interval(period),
414 }
415 }
416
417 #[must_use]
419 pub fn from_hz(hz: f64) -> Self {
420 Self::new(Duration::from_secs_f64(1.0 / hz))
421 }
422
423 pub async fn tick(&mut self) -> tokio::time::Instant {
425 self.inner.tick().await
426 }
427
428 #[must_use]
430 pub fn period(&self) -> Duration {
431 self.inner.period()
432 }
433 }
434
435 pub async fn timeout<F, T>(duration: Duration, future: F) -> Result<T, TimeoutError>
455 where
456 F: std::future::Future<Output = T>,
457 {
458 tokio::time::timeout(duration, future)
459 .await
460 .map_err(|_| TimeoutError)
461 }
462
463 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
465 pub struct TimeoutError;
466
467 impl std::fmt::Display for TimeoutError {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 write!(f, "operation timed out")
470 }
471 }
472
473 impl std::error::Error for TimeoutError {}
474
475 pub struct Debouncer {
479 last_triggered: Option<tokio::time::Instant>,
480 interval: Duration,
481 }
482
483 impl Debouncer {
484 #[must_use]
486 pub fn new(interval: Duration) -> Self {
487 Self {
488 last_triggered: None,
489 interval,
490 }
491 }
492
493 pub fn should_trigger(&mut self) -> bool {
497 let now = tokio::time::Instant::now();
498 match self.last_triggered {
499 Some(last) if now.duration_since(last) < self.interval => false,
500 _ => {
501 self.last_triggered = Some(now);
502 true
503 }
504 }
505 }
506
507 #[must_use]
509 pub fn time_until_ready(&self) -> Duration {
510 match self.last_triggered {
511 Some(last) => {
512 let elapsed = tokio::time::Instant::now().duration_since(last);
513 self.interval.saturating_sub(elapsed)
514 }
515 None => Duration::ZERO,
516 }
517 }
518
519 pub async fn wait_until_ready(&mut self) {
521 let wait_time = self.time_until_ready();
522 if !wait_time.is_zero() {
523 tokio::time::sleep(wait_time).await;
524 }
525 self.last_triggered = Some(tokio::time::Instant::now());
526 }
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533 use std::thread::sleep;
534
535 #[test]
536 fn test_time_new() {
537 let time = Time::new();
538 assert_eq!(time.frame_count(), 0);
539 assert_eq!(time.elapsed(), Duration::ZERO);
540 assert_eq!(time.delta(), Duration::ZERO);
541 assert!((time.time_scale() - 1.0).abs() < f64::EPSILON);
542 }
543
544 #[test]
545 fn test_time_update_with_delta() {
546 let mut time = Time::new();
547 let delta = Duration::from_millis(16);
548
549 time.update_with_delta(delta);
550
551 assert_eq!(time.frame_count(), 1);
552 assert_eq!(time.delta(), delta);
553 assert_eq!(time.elapsed(), delta);
554 }
555
556 #[test]
557 fn test_time_multiple_updates() {
558 let mut time = Time::new();
559 let delta = Duration::from_millis(16);
560
561 time.update_with_delta(delta);
562 time.update_with_delta(delta);
563 time.update_with_delta(delta);
564
565 assert_eq!(time.frame_count(), 3);
566 assert_eq!(time.elapsed(), delta * 3);
567 }
568
569 #[test]
570 fn test_time_scale() {
571 let mut time = Time::new();
572 time.set_time_scale(0.5);
573
574 let delta = Duration::from_millis(100);
575 time.update_with_delta(delta);
576
577 assert_eq!(time.delta(), Duration::from_millis(50));
579 }
580
581 #[test]
582 fn test_time_pause_resume() {
583 let mut time = Time::new();
584
585 time.pause();
586 assert!(time.is_paused());
587
588 let delta = Duration::from_millis(100);
589 time.update_with_delta(delta);
590
591 assert_eq!(time.elapsed(), Duration::ZERO);
593
594 time.resume();
595 assert!(!time.is_paused());
596
597 time.update_with_delta(delta);
598 assert_eq!(time.elapsed(), delta);
599 }
600
601 #[test]
602 fn test_fixed_timestep() {
603 let mut time = Time::with_fixed_timestep(60.0);
604
605 time.update_with_delta(Duration::from_millis(32));
607
608 let mut count = 0;
609 while time.should_do_fixed_update() {
610 count += 1;
611 }
612
613 assert_eq!(count, 1);
615
616 time.update_with_delta(Duration::from_millis(32));
618
619 let mut count = 0;
620 while time.should_do_fixed_update() {
621 count += 1;
622 }
623
624 assert!(count >= 1);
626 }
627
628 #[test]
629 fn test_interpolation_factor() {
630 let mut time = Time::with_fixed_timestep(60.0);
631
632 time.update_with_delta(time.fixed_timestep());
634 time.should_do_fixed_update();
635
636 assert!(time.interpolation_factor() < 0.1);
638 }
639
640 #[test]
641 fn test_time_reset() {
642 let mut time = Time::new();
643 time.update_with_delta(Duration::from_millis(100));
644 time.update_with_delta(Duration::from_millis(100));
645
646 time.reset();
647
648 assert_eq!(time.frame_count(), 0);
649 assert_eq!(time.elapsed(), Duration::ZERO);
650 }
651
652 #[test]
653 fn test_stopwatch_new() {
654 let sw = Stopwatch::new();
655 assert!(!sw.is_running());
656 assert_eq!(sw.elapsed(), Duration::ZERO);
657 }
658
659 #[test]
660 fn test_stopwatch_started() {
661 let sw = Stopwatch::started();
662 assert!(sw.is_running());
663 }
664
665 #[test]
666 fn test_stopwatch_start_stop() {
667 let mut sw = Stopwatch::new();
668
669 sw.start();
670 assert!(sw.is_running());
671
672 sleep(Duration::from_millis(10));
673
674 sw.stop();
675 assert!(!sw.is_running());
676 assert!(sw.elapsed() >= Duration::from_millis(10));
677 }
678
679 #[test]
680 fn test_stopwatch_accumulates() {
681 let mut sw = Stopwatch::new();
682
683 sw.start();
684 sleep(Duration::from_millis(10));
685 sw.stop();
686
687 let first = sw.elapsed();
688
689 sw.start();
690 sleep(Duration::from_millis(10));
691 sw.stop();
692
693 assert!(sw.elapsed() >= first + Duration::from_millis(10));
695 }
696
697 #[test]
698 fn test_stopwatch_reset() {
699 let mut sw = Stopwatch::started();
700 sleep(Duration::from_millis(10));
701
702 sw.reset();
703
704 assert!(!sw.is_running());
705 assert_eq!(sw.elapsed(), Duration::ZERO);
706 }
707
708 #[test]
709 fn test_stopwatch_restart() {
710 let mut sw = Stopwatch::started();
711 sleep(Duration::from_millis(10));
712
713 sw.restart();
714
715 assert!(sw.is_running());
716 assert!(sw.elapsed() < Duration::from_millis(5));
718 }
719
720 #[test]
721 fn test_fps() {
722 let mut time = Time::new();
723
724 for _ in 0..60 {
726 time.update_with_delta(Duration::from_micros(16667));
727 }
728
729 let fps = time.fps();
731 assert!(fps > 59.0 && fps < 61.0, "FPS was {fps}");
732 }
733}