Skip to main content

bma_ts/
impl_sqlx.rs

1use crate::{Monotonic, Timestamp};
2use sqlx::{
3    Decode, Encode, Postgres, Sqlite, Type,
4    encode::IsNull,
5    error::BoxDynError,
6    postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef},
7    sqlite::{SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef},
8};
9
10type ResultIsNull = Result<IsNull, Box<(dyn std::error::Error + Send + Sync + 'static)>>;
11
12// Timestamp
13
14impl Type<Sqlite> for Timestamp {
15    fn type_info() -> SqliteTypeInfo {
16        <i64 as Type<Sqlite>>::type_info()
17    }
18    fn compatible(ty: &SqliteTypeInfo) -> bool {
19        *ty == <i64 as Type<Sqlite>>::type_info()
20            || *ty == <i32 as Type<Sqlite>>::type_info()
21            || *ty == <i16 as Type<Sqlite>>::type_info()
22            || *ty == <i8 as Type<Sqlite>>::type_info()
23    }
24}
25impl<'q> Encode<'q, Sqlite> for Timestamp {
26    fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> ResultIsNull {
27        args.push(SqliteArgumentValue::Int64(
28            (*self).try_into().expect("timestamp too large"),
29        ));
30        Ok(IsNull::No)
31    }
32}
33impl<'r> Decode<'r, Sqlite> for Timestamp {
34    fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
35        let value = <i64 as Decode<Sqlite>>::decode(value)?;
36        Ok(value.try_into()?)
37    }
38}
39
40const J2000_EPOCH_US: i64 = 946_684_800_000_000;
41
42impl Type<Postgres> for Timestamp {
43    fn type_info() -> PgTypeInfo {
44        PgTypeInfo::with_name("TIMESTAMPTZ")
45    }
46    fn compatible(ty: &PgTypeInfo) -> bool {
47        *ty == PgTypeInfo::with_name("TIMESTAMPTZ") || *ty == PgTypeInfo::with_name("TIMESTAMP")
48    }
49}
50
51impl PgHasArrayType for Timestamp {
52    fn array_type_info() -> PgTypeInfo {
53        PgTypeInfo::with_name("_TIMESTAMPTZ")
54    }
55
56    fn array_compatible(ty: &PgTypeInfo) -> bool {
57        *ty == PgTypeInfo::with_name("_TIMESTAMPTZ") || *ty == PgTypeInfo::with_name("_TIMESTAMP")
58    }
59}
60
61impl Encode<'_, Postgres> for Timestamp {
62    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> ResultIsNull {
63        let us = i64::try_from(self.as_micros()).expect("timestamp too large") - J2000_EPOCH_US;
64        Encode::<Postgres>::encode(us, buf)
65    }
66    fn size_hint(&self) -> usize {
67        std::mem::size_of::<i64>()
68    }
69}
70impl<'r> Decode<'r, Postgres> for Timestamp {
71    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
72        let us: i64 = Decode::<Postgres>::decode(value)?;
73        Ok(Timestamp::from_micros((us + J2000_EPOCH_US).try_into()?))
74    }
75}
76
77// Monotonic
78
79impl Type<Sqlite> for Monotonic {
80    fn type_info() -> SqliteTypeInfo {
81        <i64 as Type<Sqlite>>::type_info()
82    }
83    fn compatible(ty: &SqliteTypeInfo) -> bool {
84        *ty == <i64 as Type<Sqlite>>::type_info()
85            || *ty == <i32 as Type<Sqlite>>::type_info()
86            || *ty == <i16 as Type<Sqlite>>::type_info()
87            || *ty == <i8 as Type<Sqlite>>::type_info()
88    }
89}
90impl<'q> Encode<'q, Sqlite> for Monotonic {
91    fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> ResultIsNull {
92        args.push(SqliteArgumentValue::Int64(
93            (*self).try_into().expect("timestamp too large"),
94        ));
95        Ok(IsNull::No)
96    }
97}
98impl<'r> Decode<'r, Sqlite> for Monotonic {
99    fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
100        let value = <i64 as Decode<Sqlite>>::decode(value)?;
101        Ok(value.try_into()?)
102    }
103}
104
105impl Type<Postgres> for Monotonic {
106    fn type_info() -> PgTypeInfo {
107        PgTypeInfo::with_name("INT8")
108    }
109}
110
111impl PgHasArrayType for Monotonic {
112    fn array_type_info() -> PgTypeInfo {
113        PgTypeInfo::with_name("_INT8")
114    }
115}
116
117impl Encode<'_, Postgres> for Monotonic {
118    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> ResultIsNull {
119        let us = i64::try_from(self.as_nanos()).expect("timestamp too large");
120        Encode::<Postgres>::encode(us, buf)
121    }
122    fn size_hint(&self) -> usize {
123        std::mem::size_of::<i64>()
124    }
125}
126
127impl<'r> Decode<'r, Postgres> for Monotonic {
128    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
129        let ns: i64 = Decode::<Postgres>::decode(value)?;
130        Ok(Monotonic::from_nanos(ns.try_into()?))
131    }
132}