types/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2* License, v. 2.0. If a copy of the MPL was not distributed with this
3* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
6use rusqlite::Result as RusqliteResult;
7use serde_derive::*;
8use std::fmt;
9use std::time::{Duration, SystemTime, UNIX_EPOCH};
10
11// Typesafe way to manage timestamps.
12// We should probably work out how to share this too?
13#[derive(
14    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Default,
15)]
16pub struct Timestamp(pub u64);
17
18impl Timestamp {
19    pub fn now() -> Self {
20        SystemTime::now().into()
21    }
22
23    /// Returns None if `other` is later than `self` (Duration may not represent
24    /// negative timespans in rust).
25    #[inline]
26    pub fn duration_since(self, other: Timestamp) -> Option<Duration> {
27        // just do this via SystemTime.
28        SystemTime::from(self).duration_since(other.into()).ok()
29    }
30
31    #[inline]
32    pub fn checked_sub(self, d: Duration) -> Option<Timestamp> {
33        SystemTime::from(self).checked_sub(d).map(Timestamp::from)
34    }
35
36    #[inline]
37    pub fn checked_add(self, d: Duration) -> Option<Timestamp> {
38        SystemTime::from(self).checked_add(d).map(Timestamp::from)
39    }
40
41    pub fn as_millis(self) -> u64 {
42        self.0
43    }
44
45    pub fn as_millis_i64(self) -> i64 {
46        self.0 as i64
47    }
48    /// In desktop sync, bookmarks are clamped to Jan 23, 1993 (which is 727747200000)
49    /// There's no good reason history records could be older than that, so we do
50    /// the same here (even though desktop's history currently doesn't)
51    /// XXX - there's probably a case to be made for this being, say, 5 years ago -
52    /// then all requests earlier than that are collapsed into a single visit at
53    /// this timestamp.
54    pub const EARLIEST: Timestamp = Timestamp(727_747_200_000);
55}
56
57impl From<Timestamp> for u64 {
58    #[inline]
59    fn from(ts: Timestamp) -> Self {
60        ts.0
61    }
62}
63
64impl From<SystemTime> for Timestamp {
65    #[inline]
66    fn from(st: SystemTime) -> Self {
67        let d = st.duration_since(UNIX_EPOCH).unwrap(); // hrmph - unwrap doesn't seem ideal
68        Timestamp((d.as_secs()) * 1000 + (u64::from(d.subsec_nanos()) / 1_000_000))
69    }
70}
71
72impl From<Timestamp> for SystemTime {
73    #[inline]
74    fn from(ts: Timestamp) -> Self {
75        UNIX_EPOCH + Duration::from_millis(ts.into())
76    }
77}
78
79impl From<u64> for Timestamp {
80    #[inline]
81    fn from(ts: u64) -> Self {
82        assert!(ts != 0);
83        Timestamp(ts)
84    }
85}
86
87impl fmt::Display for Timestamp {
88    #[inline]
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "{}", self.0)
91    }
92}
93
94impl ToSql for Timestamp {
95    fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
96        Ok(ToSqlOutput::from(self.0 as i64)) // hrm - no u64 in rusqlite
97    }
98}
99
100impl FromSql for Timestamp {
101    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
102        value.as_i64().map(|v| Timestamp(v as u64)) // hrm - no u64
103    }
104}