inline_postgres_impl/
key.rs

1use std::marker::PhantomData;
2
3use chrono::NaiveDateTime;
4use tokio_postgres as postgres;
5use tokio_postgres::types::{to_sql_checked, FromSql, ToSql};
6use ulid::Ulid;
7
8use crate::Table;
9
10use super::untyped_key;
11
12/// A type that is suitable for keys in the database.
13///
14/// Internally it uses an [`Ulid`].
15/// It is randomly generated and contains 80 bits of entropy.
16/// The first 48 bits encode a timestamp such that the keys are ordered by time of insertion
17///
18/// Chance of collision is negligible - `4e-7` if a billion keys are generated in the same millisecond.
19#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct Key<T> {
21    key: untyped_key::Key,
22    _phantom: PhantomData<T>,
23}
24
25impl<T: Table> Clone for Key<T> {
26    fn clone(&self) -> Self {
27        *self
28    }
29}
30
31impl<T: Table> Copy for Key<T> {}
32
33impl<T: Table> FromSql<'_> for Key<T> {
34    fn from_sql(
35        ty: &postgres::types::Type,
36        raw: &[u8],
37    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
38        Ok(Self {
39            key: untyped_key::Key::from_sql(ty, raw)?,
40            _phantom: PhantomData,
41        })
42    }
43
44    fn accepts(ty: &postgres::types::Type) -> bool {
45        untyped_key::Key::accepts_impl(ty)
46    }
47}
48impl<T: std::fmt::Debug + Table> ToSql for Key<T> {
49    fn to_sql(
50        &self,
51        ty: &postgres::types::Type,
52        out: &mut bytes::BytesMut,
53    ) -> Result<postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
54    where
55        Self: Sized,
56    {
57        self.key.to_sql(ty, out)
58    }
59
60    fn accepts(ty: &postgres::types::Type) -> bool
61    where
62        Self: Sized,
63    {
64        untyped_key::Key::accepts_impl(ty)
65    }
66
67    to_sql_checked!();
68}
69
70impl<T: Table> Key<T> {
71    pub fn from_untyped(key: untyped_key::Key) -> Self {
72        Self {
73            key,
74            _phantom: PhantomData,
75        }
76    }
77    pub fn new() -> Self {
78        Self {
79            key: untyped_key::Key::new(),
80            _phantom: PhantomData,
81        }
82    }
83    pub async fn fetch<C: postgres::GenericClient + Sync>(
84        self,
85        client: &C,
86    ) -> Result<Option<T>, postgres::Error> {
87        T::fetch_by_key(self, client).await
88    }
89    pub async fn delete<C: postgres::GenericClient + Sync>(
90        self,
91        client: &C,
92    ) -> Result<u64, postgres::Error> {
93        T::delete_by_key(self, client).await
94    }
95    pub fn inner(&self) -> Ulid {
96        self.key.0
97    }
98    pub fn timestamp(&self) -> NaiveDateTime {
99        let ms = self.key.0.timestamp_ms();
100        let ms = ms as i64;
101        NaiveDateTime::from_timestamp_millis(ms).unwrap()
102    }
103    pub fn untyped(&self) -> untyped_key::Key {
104        self.key
105    }
106}
107
108impl<T: Table + std::fmt::Debug> std::fmt::Debug for Key<T> {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        write!(f, "{}", self.key.0)
111    }
112}