inline-postgres-impl 0.1.0

Implementation of the inline-postgres crate - do not use on its own
Documentation
use std::marker::PhantomData;

use chrono::NaiveDateTime;
use tokio_postgres as postgres;
use tokio_postgres::types::{to_sql_checked, FromSql, ToSql};
use ulid::Ulid;

use crate::Table;

use super::untyped_key;

/// A type that is suitable for keys in the database.
///
/// Internally it uses an [`Ulid`].
/// It is randomly generated and contains 80 bits of entropy.
/// The first 48 bits encode a timestamp such that the keys are ordered by time of insertion
///
/// Chance of collision is negligible - `4e-7` if a billion keys are generated in the same millisecond.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Key<T> {
    key: untyped_key::Key,
    _phantom: PhantomData<T>,
}

impl<T: Table> Clone for Key<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T: Table> Copy for Key<T> {}

impl<T: Table> FromSql<'_> for Key<T> {
    fn from_sql(
        ty: &postgres::types::Type,
        raw: &[u8],
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
        Ok(Self {
            key: untyped_key::Key::from_sql(ty, raw)?,
            _phantom: PhantomData,
        })
    }

    fn accepts(ty: &postgres::types::Type) -> bool {
        untyped_key::Key::accepts_impl(ty)
    }
}
impl<T: std::fmt::Debug + Table> ToSql for Key<T> {
    fn to_sql(
        &self,
        ty: &postgres::types::Type,
        out: &mut bytes::BytesMut,
    ) -> Result<postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
    where
        Self: Sized,
    {
        self.key.to_sql(ty, out)
    }

    fn accepts(ty: &postgres::types::Type) -> bool
    where
        Self: Sized,
    {
        untyped_key::Key::accepts_impl(ty)
    }

    to_sql_checked!();
}

impl<T: Table> Key<T> {
    pub fn from_untyped(key: untyped_key::Key) -> Self {
        Self {
            key,
            _phantom: PhantomData,
        }
    }
    pub fn new() -> Self {
        Self {
            key: untyped_key::Key::new(),
            _phantom: PhantomData,
        }
    }
    pub async fn fetch<C: postgres::GenericClient + Sync>(
        self,
        client: &C,
    ) -> Result<Option<T>, postgres::Error> {
        T::fetch_by_key(self, client).await
    }
    pub async fn delete<C: postgres::GenericClient + Sync>(
        self,
        client: &C,
    ) -> Result<u64, postgres::Error> {
        T::delete_by_key(self, client).await
    }
    pub fn inner(&self) -> Ulid {
        self.key.0
    }
    pub fn timestamp(&self) -> NaiveDateTime {
        let ms = self.key.0.timestamp_ms();
        let ms = ms as i64;
        NaiveDateTime::from_timestamp_millis(ms).unwrap()
    }
    pub fn untyped(&self) -> untyped_key::Key {
        self.key
    }
}

impl<T: Table + std::fmt::Debug> std::fmt::Debug for Key<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.key.0)
    }
}