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
104#[derive(Debug, Clone, Copy, PartialEq, Default)]
106pub struct Delay(StdDuration);
107
108impl Delay {
109 pub const ZERO: Self = Self(StdDuration::ZERO);
111
112 #[must_use]
114 pub fn from_millis(millis: f64) -> Self {
115 Self(std_duration_from_secs(millis / 1000.0))
116 }
117
118 #[must_use]
120 pub fn from_secs(seconds: f64) -> Self {
121 Self(std_duration_from_secs(seconds))
122 }
123
124 #[must_use]
126 pub const fn as_millis(self) -> f64 {
127 self.0.as_secs_f64() * 1000.0
128 }
129}
130
131impl From<StdDuration> for Delay {
132 fn from(value: StdDuration) -> Self {
133 Self(value)
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::{Delay, Duration};
140 use float_cmp::assert_approx_eq;
141
142 #[test]
143 fn duration_constructors_sanitize_negative_and_nan_values() {
144 assert!(Duration::from_millis(-10.0).is_zero());
145 assert!(Duration::from_secs(f64::NAN).is_zero());
146 assert!(Delay::from_millis(-10.0).as_millis().abs() <= f64::EPSILON);
147 }
148
149 #[test]
150 fn duration_reports_seconds_and_milliseconds() {
151 let duration = Duration::from_millis(1_250.0);
152
153 assert_approx_eq!(f64, duration.as_secs(), 1.25);
154 assert_approx_eq!(f64, duration.as_millis(), 1_250.0);
155 }
156
157 #[test]
158 fn duration_arithmetic_is_saturating_where_expected() {
159 let short = Duration::from_millis(25.0);
160 let long = Duration::from_millis(100.0);
161
162 assert_eq!(short.saturating_sub(long), Duration::ZERO);
163 assert_eq!(short.min(long), short);
164 assert_eq!(short.max(long), long);
165 assert_approx_eq!(f64, short.checked_mul(2).unwrap().as_millis(), 50.0);
166 assert_approx_eq!(
167 f64,
168 short
169 .checked_add_delay(Delay::from_millis(25.0))
170 .unwrap()
171 .as_millis(),
172 50.0
173 );
174 assert_eq!(
175 long.checked_sub_delay(Delay::from_millis(25.0)),
176 Some(Duration::from_millis(75.0))
177 );
178 assert_eq!(short.checked_sub_delay(Delay::from_millis(50.0)), None);
179 assert_eq!(long.divided_by(2.0), Duration::from_millis(50.0));
180 assert_eq!(long.divided_by(0.5), Duration::from_millis(200.0));
181 assert_eq!(long.divided_by(0.0), long);
182 assert_eq!(long.divided_by(f64::NAN), long);
183 assert_eq!(
184 long.divided_by(f64::MIN_POSITIVE),
185 Duration(std::time::Duration::MAX)
186 );
187 }
188}