benzin 0.5.0

An async extension for Diesel the safe, extensible ORM and Query Builder
use diesel::{QueryResult, pg::PgTypeMetadata};
use tokio_postgres::{
    Error, Row, RowStream, Statement, ToStatement,
    types::{BorrowToSql, ToSql, Type},
};

use super::error_helper::ErrorHelper;
use crate::stmt_cache::PrepareCallback;

pub trait PreparedClient
where
    Self: Sync,
{
    async fn query_one<T>(
        &self,
        statement: &T,
        params: &[&(dyn ToSql + Sync)],
    ) -> Result<Row, Error>
    where
        T: ?Sized + Send + Sync + ToStatement;
    async fn prepare_typed(
        &self,
        query: &str,
        parameter_types: &[Type],
    ) -> Result<Statement, Error>;
    async fn query_raw<T, P, I>(&self, statement: &T, params: I) -> Result<RowStream, Error>
    where
        T: ?Sized + Send + Sync + ToStatement,
        P: BorrowToSql,
        I: IntoIterator<Item = P> + Send + Sync,
        I::IntoIter: ExactSizeIterator;
    async fn execute<T>(&self, statement: &T, params: &[&(dyn ToSql + Sync)]) -> Result<u64, Error>
    where
        T: ?Sized + ToStatement;
}

impl<W: PreparedClient> PrepareCallback<Statement, Statement, PgTypeMetadata> for &W
where
    W: Send + Sync,
{
    async fn prepare(&mut self, sql: &str, metadata: &[PgTypeMetadata]) -> QueryResult<Statement> {
        let bind_types = metadata
            .iter()
            .map(type_from_oid)
            .collect::<QueryResult<Vec<_>>>()?;

        let stmt = self
            .prepare_typed(sql, &bind_types)
            .await
            .map_err(ErrorHelper)?;
        Ok(stmt)
    }
    async fn raw(&mut self, sql: &str, metadata: &[PgTypeMetadata]) -> QueryResult<Statement> {
        self.prepare(sql, metadata).await
    }
}

fn type_from_oid(t: &PgTypeMetadata) -> QueryResult<Type> {
    let oid = t
        .oid()
        .map_err(|e| diesel::result::Error::SerializationError(Box::new(e) as _))?;

    if let Some(tpe) = Type::from_oid(oid) {
        return Ok(tpe);
    }

    Ok(Type::new(
        "diesel_custom_type".into(),
        oid,
        tokio_postgres::types::Kind::Simple,
        "public".into(),
    ))
}