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}