simple_pg 0.5.8

Provides extentions and utilites for working with postgres.
Documentation
use std::marker::PhantomData;

use postgres_types::to_sql_checked;
use simple_pg_client::types::ToSql;

trait SqlConversion<T> {
    type Output: Sized + ToSql;
    fn map(input: &T) -> Self::Output;
}

#[repr(transparent)]
/// Wrapper type to allow using another method for converting a type to SQL.
pub struct ToSqlVia<T: Sized, M> {
    x: T,
    __marker: PhantomData<M>,
}

impl<T: std::fmt::Debug, M> std::fmt::Debug for ToSqlVia<T, M> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ToSqlVia").finish()
    }
}

impl<T, M> ToSqlVia<T, M> {
    /// Convert refrence to sql
    pub fn from_ref(x: &T) -> &ToSqlVia<T, M> {
        unsafe { std::mem::transmute(x) }
    }
}

impl<T, X: ToSql, F: Fn(&T) -> X> SqlConversion<T> for F {
    type Output = X;

    fn map(input: &T) -> Self::Output {
        assert_eq!(std::mem::size_of::<F>(), 0);
        // Safe because above assert, if type is ZST layout/aligment same as ()
        let func: Self = unsafe { std::mem::transmute_copy::<(), Self>(&()) };
        (func)(input)
    }
}

/// Create lazy convertion to an sql type.
/// NOTE: the function not close over the enviroment, otherwise panic at runtime
/// Mostly used as a workaround while contructioning SQL.
pub fn lazy_binding<T, X, F: Fn(&T) -> X>(x: &T, _f: F) -> &ToSqlVia<T, F> {
    assert_eq!(
        std::mem::size_of::<F>(),
        0,
        "Function must not capture anything."
    );
    ToSqlVia::from_ref(x)
}

impl<T, M> ToSql for ToSqlVia<T, M>
where
    T: std::fmt::Debug,
    M: SqlConversion<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,
    {
        (M::map(&self.x)).to_sql(ty, out)
    }

    fn accepts(ty: &postgres_types::Type) -> bool
    where
        Self: Sized,
    {
        <M::Output>::accepts(ty)
    }

    to_sql_checked! {}
}

/// Ignore the type of binding, a workaround primary used for enums,
/// for APIs like binary writer that need the type declared up front
/// but a generic Type::Varchar would work.
#[derive(Debug)]
pub struct IgnoreType<T>(pub T);

impl<T> ToSql for IgnoreType<T>
where
    T: std::fmt::Debug + ToSql,
{
    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.0.to_sql(ty, out)
    }

    fn accepts(_ty: &postgres_types::Type) -> bool
    where
        Self: Sized,
    {
        true
    }

    fn to_sql_checked(
        &self,
        ty: &postgres_types::Type,
        out: &mut bytes::BytesMut,
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
        // We do check here becasue we are ignore the type difference.
        self.0.to_sql(ty, out)
    }
}