aura_anim_core/timing/
duration.rs1use std::{
2 ops::{Add, AddAssign},
3 time::Duration as StdDuration,
4};
5
6use crate::timing::utils::std_duration_from_secs;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
10pub struct Duration(StdDuration);
11
12impl Duration {
13 pub const ZERO: Self = Self(StdDuration::ZERO);
15
16 #[must_use]
18 pub fn is_zero(&self) -> bool {
19 self.0.is_zero()
20 }
21
22 #[must_use]
24 pub fn from_millis(millis: f64) -> Self {
25 Self(std_duration_from_secs(millis / 1000.0))
26 }
27
28 #[must_use]
30 pub fn from_secs(seconds: f64) -> Self {
31 Self(std_duration_from_secs(seconds))
32 }
33
34 #[must_use]
36 pub const fn as_millis(self) -> f64 {
37 self.0.as_secs_f64() * 1000.0
38 }
39
40 #[must_use]
42 pub const fn as_secs(self) -> f64 {
43 self.0.as_secs_f64()
44 }
45
46 pub(crate) fn checked_mul(self, rhs: u32) -> Option<Self> {
47 self.0.checked_mul(rhs).map(Self)
48 }
49
50 pub(crate) fn checked_add_delay(self, rhs: Delay) -> Option<Self> {
51 self.0.checked_add(rhs.0).map(Self)
52 }
53
54 pub(crate) fn checked_sub_delay(self, rhs: Delay) -> Option<Self> {
55 self.0.checked_sub(rhs.0).map(Self)
56 }
57
58 pub(crate) fn divided_by(self, divisor: f64) -> Self {
59 if divisor.is_finite() && divisor > 0.0 {
60 let seconds = self.0.as_secs_f64() / divisor;
61 if seconds.is_infinite() {
62 Self(StdDuration::MAX)
63 } else {
64 Self(std_duration_from_secs(seconds))
65 }
66 } else {
67 self
68 }
69 }
70
71 pub(crate) fn saturating_sub(self, rhs: Self) -> Self {
72 Self(self.0.saturating_sub(rhs.0))
73 }
74
75 pub(crate) fn min(self, rhs: Self) -> Self {
76 Self(self.0.min(rhs.0))
77 }
78
79 pub(crate) fn max(self, rhs: Self) -> Self {
80 Self(self.0.max(rhs.0))
81 }
82}
83
84impl AddAssign for Duration {
85 fn add_assign(&mut self, rhs: Self) {
86 self.0 += rhs.0;
87 }
88}
89
90impl Add for Duration {
91 type Output = Self;
92
93 fn add(self, rhs: Self) -> Self::Output {
94 Self(self.0 + rhs.0)
95 }
96}
97
98impl From<StdDuration> for Duration {
99 fn from(value: StdDuration) -> Self {
100 Self(value)
101 }
102}
103
104impl From<f32> for Duration {
105 fn from(value: f32) -> Self {
106 Duration::from_millis(f64::from(value))
107 }
108}
109
110impl From<f64> for Duration {
111 fn from(value: f64) -> Self {
112 Duration::from_millis(value)
113 }
114}
115
116impl From<i32> for Duration {
117 fn from(value: i32) -> Self {
118 Duration::from_millis(f64::from(value))
119 }
120}
121
122impl From<u32> for Duration {
123 fn from(value: u32) -> Self {
124 Self(StdDuration::from_millis(u64::from(value)))
125 }
126}
127
128impl From<u64> for Duration {
129 fn from(value: u64) -> Self {
130 Self(StdDuration::from_millis(value))
131 }
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Default)]
136pub struct Delay(StdDuration);
137
138impl Delay {
139 pub const ZERO: Self = Self(StdDuration::ZERO);
141
142 #[must_use]
144 pub fn from_millis(millis: f64) -> Self {
145 Self(std_duration_from_secs(millis / 1000.0))
146 }
147
148 #[must_use]
150 pub fn from_secs(seconds: f64) -> Self {
151 Self(std_duration_from_secs(seconds))
152 }
153
154 #[must_use]
156 pub const fn as_millis(self) -> f64 {
157 self.0.as_secs_f64() * 1000.0
158 }
159}
160
161impl From<StdDuration> for Delay {
162 fn from(value: StdDuration) -> Self {
163 Self(value)
164 }
165}
166
167impl From<f32> for Delay {
168 fn from(value: f32) -> Self {
169 Delay::from_millis(f64::from(value))
170 }
171}
172
173impl From<f64> for Delay {
174 fn from(value: f64) -> Self {
175 Delay::from_millis(value)
176 }
177}
178
179impl From<i32> for Delay {
180 fn from(value: i32) -> Self {
181 Delay::from_millis(f64::from(value))
182 }
183}
184
185impl From<u32> for Delay {
186 fn from(value: u32) -> Self {
187 Self(StdDuration::from_millis(u64::from(value)))
188 }
189}
190
191impl From<u64> for Delay {
192 fn from(value: u64) -> Self {
193 Self(StdDuration::from_millis(value))
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::{Delay, Duration};
200 use float_cmp::assert_approx_eq;
201
202 #[test]
203 fn duration_constructors_sanitize_negative_and_nan_values() {
204 assert!(Duration::from_millis(-10.0).is_zero());
205 assert!(Duration::from_secs(f64::NAN).is_zero());
206 assert!(Delay::from_millis(-10.0).as_millis().abs() <= f64::EPSILON);
207 }
208
209 #[test]
210 fn duration_reports_seconds_and_milliseconds() {
211 let duration = Duration::from_millis(1_250.0);
212
213 assert_approx_eq!(f64, duration.as_secs(), 1.25);
214 assert_approx_eq!(f64, duration.as_millis(), 1_250.0);
215 }
216
217 #[test]
218 fn duration_arithmetic_is_saturating_where_expected() {
219 let short = Duration::from_millis(25.0);
220 let long = Duration::from_millis(100.0);
221
222 assert_eq!(short.saturating_sub(long), Duration::ZERO);
223 assert_eq!(short.min(long), short);
224 assert_eq!(short.max(long), long);
225 assert_approx_eq!(f64, short.checked_mul(2).unwrap().as_millis(), 50.0);
226 assert_approx_eq!(
227 f64,
228 short
229 .checked_add_delay(Delay::from_millis(25.0))
230 .unwrap()
231 .as_millis(),
232 50.0
233 );
234 assert_eq!(
235 long.checked_sub_delay(Delay::from_millis(25.0)),
236 Some(Duration::from_millis(75.0))
237 );
238 assert_eq!(short.checked_sub_delay(Delay::from_millis(50.0)), None);
239 assert_eq!(long.divided_by(2.0), Duration::from_millis(50.0));
240 assert_eq!(long.divided_by(0.5), Duration::from_millis(200.0));
241 assert_eq!(long.divided_by(0.0), long);
242 assert_eq!(long.divided_by(f64::NAN), long);
243 assert_eq!(
244 long.divided_by(f64::MIN_POSITIVE),
245 Duration(std::time::Duration::MAX)
246 );
247 }
248}