mpris_server/time.rs
1use std::{
2 fmt,
3 ops::{Add, AddAssign, Sub, SubAssign},
4};
5
6use serde::{Deserialize, Serialize};
7use zbus::zvariant::{Error, Type, Value};
8
9/// A time with microsecond precision which can be negative.
10#[derive(
11 Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Type,
12)]
13#[serde(transparent)]
14#[doc(alias = "Time_In_Us")]
15pub struct Time(i64);
16
17impl fmt::Debug for Time {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 write!(f, "{}µs", self.0)
20 }
21}
22
23impl Time {
24 /// A time of zero.
25 ///
26 /// # Examples
27 /// ```
28 /// use mpris_server::Time;
29 ///
30 /// let time = Time::ZERO;
31 /// assert!(time.is_zero());
32 /// assert_eq!(time.as_micros(), 0);
33 /// ```
34 pub const ZERO: Self = Self::from_micros(0);
35
36 /// The minimum time.
37 pub const MIN: Self = Self::from_micros(i64::MIN);
38
39 /// The maximum time.
40 pub const MAX: Self = Self::from_micros(i64::MAX);
41
42 /// Creates a new `Time` from the specified number of whole seconds.
43 ///
44 /// # Panics
45 ///
46 /// This will panic if the number of seconds overflows.
47 ///
48 /// # Examples
49 /// ```
50 /// use mpris_server::Time;
51 ///
52 /// assert_eq!(Time::from_secs(5).as_nanos(), 5_000_000_000);
53 /// ```
54 #[inline]
55 pub const fn from_secs(secs: i64) -> Self {
56 match secs.checked_mul(1_000_000) {
57 Some(micros) => Self::from_micros(micros),
58 None => panic!("overflow when creating time from seconds"),
59 }
60 }
61
62 /// Creates a new `Time` from the specified number of whole milliseconds.
63 ///
64 /// # Panics
65 ///
66 /// This will panic if the number of milliseconds overflows.
67 ///
68 /// # Examples
69 /// ```
70 /// use mpris_server::Time;
71 ///
72 /// assert_eq!(Time::from_millis(5).as_nanos(), 5_000_000);
73 /// ```
74 #[inline]
75 pub const fn from_millis(millis: i64) -> Self {
76 match millis.checked_mul(1000) {
77 Some(micros) => Self::from_micros(micros),
78 None => panic!("overflow when creating time from milliseconds"),
79 }
80 }
81
82 /// Creates a new `Time` from the specified number of whole microseconds.
83 ///
84 /// # Examples
85 /// ```
86 /// use mpris_server::Time;
87 ///
88 /// assert_eq!(Time::from_micros(5).as_nanos(), 5_000);
89 /// ```
90 #[inline]
91 pub const fn from_micros(micros: i64) -> Self {
92 Self(micros)
93 }
94
95 /// Creates a new `Time` from the specified number of whole nanoseconds.
96 ///
97 /// **Note:** This will round of the nanoseconds to microseconds level of
98 /// precision.
99 ///
100 /// # Examples
101 /// ```
102 /// use mpris_server::Time;
103 ///
104 /// assert_eq!(Time::from_nanos(5).as_nanos(), 0);
105 /// assert_eq!(Time::from_nanos(5342).as_nanos(), 5000);
106 /// ```
107 #[inline]
108 pub const fn from_nanos(nanos: i64) -> Self {
109 Self::from_micros(nanos / 1000)
110 }
111
112 /// Returns the number of *whole* seconds contained by this `Time`.
113 ///
114 /// # Examples
115 /// ```
116 /// use mpris_server::Time;
117 ///
118 /// assert_eq!(Time::from_micros(5_000_000).as_secs(), 5);
119 /// assert_eq!(Time::from_micros(3).as_secs(), 0);
120 /// ```
121 #[inline]
122 pub const fn as_secs(&self) -> i64 {
123 self.as_micros() / 1_000_000
124 }
125
126 /// Returns the number of *whole* milliseconds contained by this `Time`.
127 ///
128 /// # Examples
129 /// ```
130 /// use mpris_server::Time;
131 ///
132 /// assert_eq!(Time::from_micros(5_000_000).as_millis(), 5_000);
133 /// assert_eq!(Time::from_micros(3).as_millis(), 0);
134 /// ```
135 #[inline]
136 pub const fn as_millis(&self) -> i64 {
137 self.as_micros() / 1000
138 }
139
140 /// Returns the number of *whole* microseconds contained by this `Time`.
141 ///
142 /// # Examples
143 /// ```
144 /// use mpris_server::Time;
145 ///
146 /// assert_eq!(Time::from_micros(5_000_000).as_micros(), 5_000_000);
147 /// assert_eq!(Time::from_micros(3).as_micros(), 3);
148 /// ```
149 #[inline]
150 pub const fn as_micros(&self) -> i64 {
151 self.0
152 }
153
154 /// Returns the number of *whole* nanoseconds contained by this `Time`.
155 ///
156 /// # Examples
157 /// ```
158 /// use mpris_server::Time;
159 ///
160 /// assert_eq!(Time::from_micros(5_000_000).as_nanos(), 5_000_000_000);
161 /// assert_eq!(Time::from_micros(3).as_nanos(), 3_000);
162 #[inline]
163 pub const fn as_nanos(&self) -> i128 {
164 self.as_micros() as i128 * 1000
165 }
166
167 /// Returns true if this `Time` is zero.
168 ///
169 /// # Examples
170 /// ```
171 /// use mpris_server::Time;
172 ///
173 /// assert_eq!(Time::ZERO.is_zero(), true);
174 /// assert_eq!(Time::from_micros(1).is_zero(), false);
175 /// ```
176 #[inline]
177 pub const fn is_zero(&self) -> bool {
178 self.0 == 0
179 }
180
181 /// Returns true if this `Time` is negative.
182 ///
183 /// # Examples
184 /// ```
185 /// use mpris_server::Time;
186 ///
187 /// assert_eq!(Time::ZERO.is_negative(), false);
188 /// assert_eq!(Time::from_micros(-1).is_negative(), true);
189 /// assert_eq!(Time::from_micros(1).is_negative(), false);
190 /// ```
191 #[inline]
192 pub const fn is_negative(&self) -> bool {
193 self.0 < 0
194 }
195
196 /// Returns true if this `Time` is positive.
197 ///
198 /// # Examples
199 /// ```
200 /// use mpris_server::Time;
201 ///
202 /// assert_eq!(Time::ZERO.is_positive(), false);
203 /// assert_eq!(Time::from_micros(1).is_positive(), true);
204 /// assert_eq!(Time::from_micros(-1).is_positive(), false);
205 /// ```
206 #[inline]
207 pub const fn is_positive(&self) -> bool {
208 self.0 > 0
209 }
210
211 /// Returns the time as an absolute (non-negative) value.
212 ///
213 /// # Examples
214 /// ```
215 /// use mpris_server::Time;
216 ///
217 /// assert_eq!(Time::ZERO.abs(), Time::ZERO);
218 /// assert_eq!(Time::from_micros(-1).abs(), Time::from_micros(1));
219 /// assert_eq!(Time::from_micros(1).abs(), Time::from_micros(1));
220 /// ```
221 #[inline]
222 pub const fn abs(&self) -> Self {
223 Self(self.0.abs())
224 }
225
226 /// Checked `Time` addition. Computes `self + other`, returning [`None`]
227 /// if overflow occurred.
228 ///
229 /// # Examples
230 /// ```
231 /// use mpris_server::Time;
232 ///
233 /// assert_eq!(
234 /// Time::from_micros(1).checked_add(Time::from_micros(1)),
235 /// Some(Time::from_micros(2))
236 /// );
237 /// assert_eq!(Time::MAX.checked_add(Time::from_micros(1)), None);
238 /// ```
239 #[inline]
240 pub const fn checked_add(self, other: Self) -> Option<Self> {
241 match self.0.checked_add(other.0) {
242 Some(inner) => Some(Self(inner)),
243 None => None,
244 }
245 }
246
247 /// Checked `Time` subtraction. Computes `self - other`, returning [`None`]
248 /// if overflow occurred.
249 ///
250 /// # Examples
251 /// ```
252 /// use mpris_server::Time;
253 ///
254 /// assert_eq!(
255 /// Time::from_micros(2).checked_sub(Time::from_micros(1)),
256 /// Some(Time::from_micros(1))
257 /// );
258 /// assert_eq!(Time::MIN.checked_sub(Time::from_micros(1)), None);
259 /// ```
260 #[inline]
261 pub const fn checked_sub(self, other: Self) -> Option<Self> {
262 match self.0.checked_sub(other.0) {
263 Some(inner) => Some(Self(inner)),
264 None => None,
265 }
266 }
267
268 /// Saturating `Time` addition. Computes `self + other`, returning
269 /// [`Time::MAX`] if overflow occurred.
270 ///
271 /// # Examples
272 /// ```
273 /// use mpris_server::Time;
274 ///
275 /// assert_eq!(
276 /// Time::from_micros(1).saturating_add(Time::from_micros(1)),
277 /// Time::from_micros(2)
278 /// );
279 /// assert_eq!(Time::MAX.saturating_add(Time::from_micros(1)), Time::MAX);
280 /// ```
281 #[inline]
282 pub const fn saturating_add(self, other: Self) -> Self {
283 match self.checked_add(other) {
284 Some(inner) => inner,
285 None => Self::MAX,
286 }
287 }
288
289 /// Saturating `Time` subtraction. Computes `self - other`, returning
290 /// [`Time::MIN`] if overflow occurred.
291 ///
292 /// # Examples
293 /// ```
294 /// use mpris_server::Time;
295 ///
296 /// assert_eq!(
297 /// Time::from_micros(2).saturating_sub(Time::from_micros(1)),
298 /// Time::from_micros(1)
299 /// );
300 /// assert_eq!(Time::MIN.saturating_sub(Time::from_micros(1)), Time::MIN);
301 /// ```
302 #[inline]
303 pub const fn saturating_sub(self, other: Self) -> Self {
304 match self.checked_sub(other) {
305 Some(inner) => inner,
306 None => Self::MIN,
307 }
308 }
309}
310
311impl Add for Time {
312 type Output = Self;
313
314 #[inline]
315 fn add(self, other: Self) -> Self {
316 self.checked_add(other).expect("overflow when adding times")
317 }
318}
319
320impl AddAssign for Time {
321 fn add_assign(&mut self, rhs: Self) {
322 *self = *self + rhs;
323 }
324}
325
326impl Sub for Time {
327 type Output = Self;
328
329 #[inline]
330 fn sub(self, other: Self) -> Self {
331 self.checked_sub(other)
332 .expect("overflow when subtracting times")
333 }
334}
335
336impl SubAssign for Time {
337 fn sub_assign(&mut self, rhs: Self) {
338 *self = *self - rhs;
339 }
340}
341
342impl From<Time> for Value<'_> {
343 fn from(time: Time) -> Self {
344 Value::from(time.0)
345 }
346}
347
348impl TryFrom<Value<'_>> for Time {
349 type Error = Error;
350
351 fn try_from(value: Value<'_>) -> Result<Self, Self::Error> {
352 i64::try_from(value).map(Self)
353 }
354}
355
356impl TryFrom<&Value<'_>> for Time {
357 type Error = Error;
358
359 fn try_from(value: &Value<'_>) -> Result<Self, Self::Error> {
360 i64::try_from(value).map(Self)
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn valid_signature() {
370 assert_eq!(Time::SIGNATURE, "x");
371 }
372}