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}