1use std::{
2 cmp::Ordering,
3 time::{Duration, Instant},
4};
5
6pub const INFINITE_DURATION: Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
8
9#[derive(Clone, Copy, PartialEq, Eq)]
10pub enum TimerDuration {
18 Elapsed,
19 Real(Duration),
20 Infinite,
21}
22impl TimerDuration {
23 pub fn from_difference(a: TimerDuration, b: Duration) -> TimerDuration {
28 if a == TimerDuration::Infinite {
29 return a;
30 }
31 let a = a.to_real();
32 if a < b {
33 TimerDuration::Elapsed
34 } else {
35 TimerDuration::Real(a - b)
36 }
37 }
38
39 pub fn to_real(&self) -> Duration {
42 match *self {
43 Self::Real(d) if d > Duration::ZERO => d,
44 Self::Infinite => INFINITE_DURATION,
45 _ => Duration::new(0, 0),
46 }
47 }
48
49 pub const fn is_elapsed(&self) -> bool {
50 matches!(self, TimerDuration::Elapsed)
51 }
52
53 pub const fn is_real(&self) -> bool {
54 matches!(self, TimerDuration::Real(_))
55 }
56
57 pub const fn is_infinite(&self) -> bool {
58 matches!(self, TimerDuration::Infinite)
59 }
60}
61impl From<Duration> for TimerDuration {
62 fn from(value: Duration) -> Self {
63 if value == Duration::ZERO {
64 return Self::Elapsed;
65 }
66 if value == INFINITE_DURATION {
67 return Self::Infinite;
68 }
69 Self::Real(value)
70 }
71}
72impl From<u64> for TimerDuration {
73 fn from(value: u64) -> Self {
74 if value == 0 {
75 return Self::Elapsed;
76 }
77 if value == u64::MAX {
78 return Self::Infinite;
79 }
80 Self::Real(Duration::from_millis(value))
81 }
82}
83impl PartialOrd for TimerDuration {
84 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
85 Some(self.cmp(other))
86 }
87}
88impl Ord for TimerDuration {
89 fn cmp(&self, other: &Self) -> Ordering {
90 match (self, other) {
91 (TimerDuration::Elapsed, TimerDuration::Elapsed) => Ordering::Equal,
92 (TimerDuration::Elapsed, _) => Ordering::Less,
93 (TimerDuration::Real(_), TimerDuration::Elapsed) => Ordering::Greater,
94 (TimerDuration::Real(a), TimerDuration::Real(b)) => a.cmp(b),
95 (TimerDuration::Real(_), TimerDuration::Infinite) => Ordering::Less,
96 (TimerDuration::Infinite, TimerDuration::Infinite) => Ordering::Equal,
97 (TimerDuration::Infinite, _) => Ordering::Greater,
98 }
99 }
100}
101
102#[derive(Clone, Copy)]
103pub struct Timer(Option<Instant>, TimerDuration);
106impl Timer {
107 pub fn new(d: TimerDuration) -> Self {
108 Self(Some(Instant::now()), d)
109 }
110
111 pub const fn new_const(d: TimerDuration) -> Self {
112 if d.is_real() {
113 panic!("TimerDuration is only const-compatible when Elapsed or Infinite.");
114 }
115 Self(None, d)
116 }
117
118 pub fn time_elapsed(&self) -> Duration {
119 self.0.unwrap_or_else(Instant::now).elapsed()
120 }
121
122 pub fn restart(&mut self) {
123 self.0 = Some(Instant::now());
124 }
125
126}
127
128pub trait Timed {
130 fn has_elapsed(&self) -> bool;
132 fn time_left(&self) -> TimerDuration;
135}
136impl Timed for Timer {
137 fn has_elapsed(&self) -> bool {
138 if self.1.is_elapsed() {
139 return true;
140 }
141 if self.1.is_infinite() {
142 return false;
143 }
144 self.0.unwrap().elapsed() >= self.1.to_real()
145 }
146 fn time_left(&self) -> TimerDuration {
147 if !self.1.is_real() {
148 return self.1;
149 }
150 TimerDuration::from_difference(self.1, self.0.unwrap().elapsed())
151 }
152}