phantom_newtype/
instant.rs

1// Copyright 2019 DFINITY
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::amount::Amount;
16use crate::displayer::{DisplayProxy, DisplayerOf};
17#[cfg(feature = "serde")]
18use serde::{Deserialize, Deserializer, Serialize, Serializer};
19use std::cmp::Ordering;
20use std::fmt;
21use std::hash::{Hash, Hasher};
22use std::marker::PhantomData;
23use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign};
24
25/// `Instant<Unit>` provides a type-safe way to keep absolute time of
26/// some events, expressed in `Unit`s (CPU ticks, seconds from epoch,
27/// years from birth, etc).
28///
29/// You can compare instants:
30///
31/// ```
32/// use phantom_newtype::Instant;
33///
34/// enum SecondsFromEpoch {}
35/// type UnixTime = Instant<SecondsFromEpoch, i64>;
36///
37/// assert_eq!(true, UnixTime::from(3) < UnixTime::from(5));
38/// assert_eq!(false, UnixTime::from(3) > UnixTime::from(5));
39/// assert_eq!(true, UnixTime::from(3) != UnixTime::from(5));
40/// assert_eq!(true, UnixTime::from(5) == UnixTime::from(5));
41/// assert_eq!(false, UnixTime::from(5) != UnixTime::from(5));
42///
43/// assert_eq!(vec![UnixTime::from(3), UnixTime::from(5)].iter().max().unwrap(),
44///            &UnixTime::from(5));
45/// ```
46///
47/// Instants support basic arithmetics, you can:
48/// * Subtract an instant from another instant to get amount of units between them.
49/// * Add/subtract amount of units to/from an instant to get another instant.
50///
51/// ```
52/// use phantom_newtype::{Amount, Instant};
53///
54/// enum SecondsFromEpoch {}
55///
56/// type UnixTime = Instant<SecondsFromEpoch, i64>;
57/// type TimeDiff = Amount<SecondsFromEpoch, i64>;
58///
59/// let epoch = UnixTime::from(0);
60/// let some_date = UnixTime::from(123456789);
61/// let diff = TimeDiff::from(123456789);
62///
63/// assert_eq!(some_date - epoch, diff);
64/// assert_eq!(some_date - diff, epoch);
65/// assert_eq!(epoch + diff, some_date);
66/// ```
67///
68/// Direct multiplication of instants is not supported, however, you
69/// can scale them by a scalar or divide to get a scalar back:
70///
71/// ```
72/// use phantom_newtype::Instant;
73///
74/// enum SecondsFromEpoch {}
75/// type UnixTime = Instant<SecondsFromEpoch, i64>;
76///
77/// let x = UnixTime::from(123456);
78/// assert_eq!(x * 3, UnixTime::from(3 * 123456));
79/// assert_eq!(1, x / x);
80/// assert_eq!(3, (x * 3) / x);
81/// ```
82///
83/// Note that the unit is only available at compile time, thus using
84/// `Instant` instead of `u64` doesn't incur any runtime penalty:
85///
86/// ```
87/// use phantom_newtype::Instant;
88///
89/// enum SecondsFromEpoch {}
90///
91/// let ms = Instant::<SecondsFromEpoch, u64>::from(10);
92/// assert_eq!(std::mem::size_of_val(&ms), std::mem::size_of::<u64>());
93/// ```
94///
95/// Instants can be serialized and deserialized with `serde`. Serialized
96/// forms of `Instant<Unit, Repr>` and `Repr` are identical.
97///
98/// ```
99/// #[cfg(feature = "serde")] {
100/// use phantom_newtype::Instant;
101/// use serde::{Serialize, Deserialize};
102/// use serde_json;
103///
104/// enum SecondsFromEpoch {}
105/// type UnixTime = Instant<SecondsFromEpoch, i64>;
106///
107/// let repr: u64 = 123456;
108/// let time = UnixTime::from(repr);
109/// assert_eq!(serde_json::to_string(&time).unwrap(), serde_json::to_string(&repr).unwrap());
110///
111/// let copy: UnitTime = serde_json::from_str(&serde_json::to_string(&time).unwrap()).unwrap();
112/// assert_eq!(copy, time);
113/// }
114/// ```
115///
116/// You can also declare constants of `Instant<Unit, Repr>` using `new`
117/// function:
118/// ```
119/// use phantom_newtype::Instant;
120///
121/// enum SecondsFromEpoch {}
122/// type UnixTime = Instant<SecondsFromEpoch, u64>;
123///
124/// const EPOCH: UnixTime = UnixTime::new(0);
125/// ```
126///
127/// Instants can be sent between threads if the `Repr` allows it, no
128/// matter which `Unit` is used.
129///
130/// ```
131/// use phantom_newtype::Instant;
132///
133/// type Cell = std::cell::RefCell<i64>;
134/// type CellInstant = Instant<Cell, i64>;
135/// const I: CellInstant = CellInstant::new(1234);
136///
137/// let instant_from_thread = std::thread::spawn(|| &I).join().unwrap();
138/// assert_eq!(I, *instant_from_thread);
139/// ```
140pub struct Instant<Unit, Repr>(Repr, PhantomData<std::sync::Mutex<Unit>>);
141
142impl<Unit, Repr: Copy> Instant<Unit, Repr> {
143    /// Returns the wrapped value.
144    ///
145    /// ```
146    /// use phantom_newtype::Instant;
147    ///
148    /// enum Apples {}
149    ///
150    /// let three_apples = Instant::<Apples, u64>::from(3);
151    /// assert_eq!(9, (three_apples * 3).get());
152    /// ```
153    pub fn get(&self) -> Repr {
154        self.0
155    }
156}
157
158impl<Unit, Repr> Instant<Unit, Repr> {
159    /// `new` is a synonym for `from` that can be evaluated in
160    /// compile time. The main use-case of this functions is defining
161    /// constants.
162    pub const fn new(repr: Repr) -> Instant<Unit, Repr> {
163        Instant(repr, PhantomData)
164    }
165}
166
167impl<Unit: Default, Repr: Copy> Instant<Unit, Repr> {
168    /// Provides a useful shortcut to access units of an instant if
169    /// they implement the `Default` trait:
170    ///
171    /// ```
172    /// use phantom_newtype::Instant;
173    ///
174    /// #[derive(Debug, Default)]
175    /// struct SecondsFromEpoch;
176    /// let when = Instant::<SecondsFromEpoch, i64>::from(5);
177    ///
178    /// assert_eq!("5 SecondsFromEpoch", format!("{} {:?}", when, when.unit()));
179    /// ```
180    pub fn unit(&self) -> Unit {
181        Default::default()
182    }
183}
184
185impl<Unit, Repr> Instant<Unit, Repr>
186where
187    Unit: DisplayerOf<Instant<Unit, Repr>>,
188{
189    /// `display` provides a machanism to implement a custom display
190    /// for phantom types.
191    ///
192    /// ```
193    /// use phantom_newtype::{Instant, DisplayerOf};
194    /// use std::fmt;
195    ///
196    /// struct YearUnit;
197    /// type YearAD = Instant<YearUnit, u64>;
198    ///
199    /// impl DisplayerOf<YearAD> for YearUnit {
200    ///   fn display(year: &YearAD, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201    ///     write!(f, "{} AD", year.get())
202    ///   }
203    /// }
204    ///
205    /// assert_eq!(format!("{}", YearAD::from(1221).display()), "1221 AD");
206    /// ```
207    pub fn display(&self) -> DisplayProxy<'_, Self, Unit> {
208        DisplayProxy::new(self)
209    }
210}
211
212impl<Unit, Repr: Copy> From<Repr> for Instant<Unit, Repr> {
213    fn from(repr: Repr) -> Self {
214        Self::new(repr)
215    }
216}
217
218impl<Unit, Repr: Copy> Clone for Instant<Unit, Repr> {
219    fn clone(&self) -> Self {
220        Instant(self.0, PhantomData)
221    }
222}
223
224impl<Unit, Repr: Copy> Copy for Instant<Unit, Repr> {}
225
226impl<Unit, Repr: PartialEq> PartialEq for Instant<Unit, Repr> {
227    fn eq(&self, rhs: &Self) -> bool {
228        self.0.eq(&rhs.0)
229    }
230}
231
232impl<Unit, Repr: Eq> Eq for Instant<Unit, Repr> {}
233
234impl<Unit, Repr: PartialOrd> PartialOrd for Instant<Unit, Repr> {
235    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
236        self.0.partial_cmp(&rhs.0)
237    }
238}
239
240impl<Unit, Repr: Ord> Ord for Instant<Unit, Repr> {
241    fn cmp(&self, rhs: &Self) -> Ordering {
242        self.0.cmp(&rhs.0)
243    }
244}
245
246impl<Unit, Repr: Hash> Hash for Instant<Unit, Repr> {
247    fn hash<H: Hasher>(&self, state: &mut H) {
248        self.0.hash(state)
249    }
250}
251
252impl<Unit, Repr, Repr2> Add<Amount<Unit, Repr2>> for Instant<Unit, Repr>
253where
254    Repr: AddAssign<Repr2> + Copy,
255    Repr2: Copy,
256{
257    type Output = Self;
258    fn add(mut self, rhs: Amount<Unit, Repr2>) -> Self {
259        self.add_assign(rhs);
260        self
261    }
262}
263
264impl<Unit, Repr, Repr2> AddAssign<Amount<Unit, Repr2>> for Instant<Unit, Repr>
265where
266    Repr: AddAssign<Repr2> + Copy,
267    Repr2: Copy,
268{
269    fn add_assign(&mut self, rhs: Amount<Unit, Repr2>) {
270        self.0 += rhs.get()
271    }
272}
273
274impl<Unit, Repr, Repr2> SubAssign<Amount<Unit, Repr2>> for Instant<Unit, Repr>
275where
276    Repr: SubAssign<Repr2> + Copy,
277    Repr2: Copy,
278{
279    fn sub_assign(&mut self, rhs: Amount<Unit, Repr2>) {
280        self.0 -= rhs.get()
281    }
282}
283
284impl<Unit, Repr> Sub for Instant<Unit, Repr>
285where
286    Repr: Sub + Copy,
287{
288    type Output = Amount<Unit, <Repr as Sub>::Output>;
289
290    fn sub(self, rhs: Self) -> Self::Output {
291        Amount::<Unit, <Repr as Sub>::Output>::new(self.0 - rhs.0)
292    }
293}
294
295impl<Unit, Repr, Repr2> Sub<Amount<Unit, Repr2>> for Instant<Unit, Repr>
296where
297    Repr: SubAssign<Repr2> + Copy,
298    Repr2: Copy,
299{
300    type Output = Self;
301
302    fn sub(mut self, rhs: Amount<Unit, Repr2>) -> Self {
303        self.sub_assign(rhs);
304        self
305    }
306}
307
308impl<Unit, Repr> MulAssign<Repr> for Instant<Unit, Repr>
309where
310    Repr: MulAssign + Copy,
311{
312    fn mul_assign(&mut self, rhs: Repr) {
313        self.0 *= rhs;
314    }
315}
316
317impl<Unit, Repr> Mul<Repr> for Instant<Unit, Repr>
318where
319    Repr: MulAssign + Copy,
320{
321    type Output = Self;
322
323    fn mul(mut self, rhs: Repr) -> Self {
324        self.mul_assign(rhs);
325        self
326    }
327}
328
329impl<Unit, Repr> Div<Self> for Instant<Unit, Repr>
330where
331    Repr: Div<Repr> + Copy,
332{
333    type Output = <Repr as Div>::Output;
334
335    fn div(self, rhs: Self) -> Self::Output {
336        self.0.div(rhs.0)
337    }
338}
339
340impl<Unit, Repr> fmt::Debug for Instant<Unit, Repr>
341where
342    Repr: fmt::Debug,
343{
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        self.0.fmt(f)
346    }
347}
348
349impl<Unit, Repr> fmt::Display for Instant<Unit, Repr>
350where
351    Repr: fmt::Display,
352{
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        self.0.fmt(f)
355    }
356}
357
358#[cfg(feature = "serde")]
359impl<Unit, Repr: Serialize> Serialize for Instant<Unit, Repr> {
360    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
361        self.0.serialize(serializer)
362    }
363}
364
365#[cfg(feature = "serde")]
366impl<'de, Unit, Repr> Deserialize<'de> for Instant<Unit, Repr>
367where
368    Repr: Deserialize<'de>,
369{
370    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
371        Repr::deserialize(deserializer).map(Instant::<Unit, Repr>::new)
372    }
373}
374
375#[cfg(test)]
376mod tests {
377    use super::*;
378
379    #[test]
380    fn test_complex_instant_arithmetics() {
381        enum Seconds {}
382        enum UTC {}
383
384        type Timestamp = Instant<Seconds, i64>;
385        type TsDiff = Amount<Seconds, i64>;
386        type Date = Instant<UTC, Timestamp>;
387
388        let epoch = Date::new(Timestamp::new(0));
389        let date = Date::new(Timestamp::new(123456789));
390        let span = Amount::<UTC, TsDiff>::new(TsDiff::from(123456789));
391
392        assert_eq!(date - epoch, span);
393        assert_eq!(date - span, epoch);
394        assert_eq!(epoch + span, date);
395    }
396}