retrom_codegen/
timestamp.rs

1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3use diesel::sql_types::{self, SqlType};
4use diesel::{
5    deserialize::{FromSql, FromSqlRow},
6    expression::AsExpression,
7    pg::PgValue,
8    serialize::{Output, ToSql},
9};
10
11fn pg_epoch() -> SystemTime {
12    let thirty_years = Duration::from_secs(946_684_800);
13    UNIX_EPOCH + thirty_years
14}
15
16#[derive(
17    Clone,
18    Eq,
19    Hash,
20    Copy,
21    PartialEq,
22    PartialOrd,
23    Ord,
24    ::prost::Message,
25    ::serde::Serialize,
26    ::serde::Deserialize,
27    SqlType,
28    AsExpression,
29    FromSqlRow,
30)]
31#[diesel(postgres_type(oid = 1114, array_oid = 1115))]
32#[diesel(sql_type = diesel::sql_types::Timestamp)]
33#[diesel(sql_type = diesel::sql_types::Timestamptz)]
34pub struct Timestamp {
35    #[prost(int64, tag = "1")]
36    pub seconds: i64,
37    #[prost(int32, tag = "2")]
38    pub nanos: i32,
39}
40
41impl Timestamp {
42    pub fn elapsed_since(&self, earlier: &Timestamp) -> Option<Duration> {
43        let self_duration = Duration::new(self.seconds as u64, self.nanos as u32);
44        let earlier_duration = Duration::new(earlier.seconds as u64, earlier.nanos as u32);
45
46        self_duration.checked_sub(earlier_duration)
47    }
48}
49
50impl TryFrom<Timestamp> for prost_types::Timestamp {
51    type Error = prost::DecodeError;
52
53    fn try_from(value: Timestamp) -> Result<Self, Self::Error> {
54        Ok(prost_types::Timestamp {
55            seconds: value.seconds,
56            nanos: value.nanos,
57        })
58    }
59}
60
61impl From<prost_types::Timestamp> for Timestamp {
62    fn from(value: prost_types::Timestamp) -> Self {
63        Timestamp {
64            seconds: value.seconds,
65            nanos: value.nanos,
66        }
67    }
68}
69
70impl From<std::time::SystemTime> for Timestamp {
71    fn from(time: std::time::SystemTime) -> Self {
72        prost_types::Timestamp::from(time).into()
73    }
74}
75
76impl ToSql<sql_types::Timestamp, diesel::pg::Pg> for Timestamp {
77    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result {
78        let thirty_years_seconds = 946_684_800;
79        let micros = (self.seconds - thirty_years_seconds) * 1_000_000 + self.nanos as i64 / 1_000;
80
81        ToSql::<sql_types::BigInt, diesel::pg::Pg>::to_sql(&micros, &mut out.reborrow())
82    }
83}
84
85impl ToSql<sql_types::Timestamptz, diesel::pg::Pg> for Timestamp {
86    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result {
87        ToSql::<sql_types::Timestamp, diesel::pg::Pg>::to_sql(self, out)
88    }
89}
90
91impl FromSql<sql_types::Timestamp, diesel::pg::Pg> for Timestamp {
92    fn from_sql(bytes: PgValue<'_>) -> diesel::deserialize::Result<Self> {
93        let micros = i64::from_sql(bytes)?;
94
95        let pg_timestamp = match micros < 0 {
96            true => pg_epoch() - Duration::from_micros(micros.unsigned_abs()),
97            false => pg_epoch() + Duration::from_micros(micros.unsigned_abs()),
98        };
99
100        Ok(pg_timestamp.into())
101    }
102}
103
104impl FromSql<sql_types::Timestamptz, diesel::pg::Pg> for Timestamp {
105    fn from_sql(bytes: PgValue<'_>) -> diesel::deserialize::Result<Self> {
106        FromSql::<sql_types::Timestamp, diesel::pg::Pg>::from_sql(bytes)
107    }
108}