postgres_extras 0.1.0

Provides extentions and utilites for working with postgres.
Documentation
use std::{iter::Map, marker::PhantomData};

use postgres::{types::{ToSql, FromSqlOwned}, Row, CopyInWriter};
pub use postgres_extras_macros::sql;

#[derive(Debug)]
pub struct Sql<'a> {
    pub raw: &'a str,
    pub bindings: &'a [&'a(dyn ToSql + Sync) ]
}

impl<'a> Sql<'a> {
    pub fn fragment(&self) -> SqlFragment<'_> {
        SqlFragment::Lucky(self)
    }
    pub fn execute(self, tx: &mut impl postgres::GenericClient) -> Result<u64, postgres::error::Error> {
        tx.execute(self.raw, self.bindings)
    }
    pub fn batch_execute(self, tx: &mut impl postgres::GenericClient) -> Result<(), postgres::error::Error> {
        tx.batch_execute(self.raw)
    }
    pub fn query(self, tx: &mut impl postgres::GenericClient)-> Result<Vec<Row>, postgres::error::Error> {
        tx.query(self.raw, self.bindings)
    }
    pub fn query_one(self, tx: &mut impl postgres::GenericClient)-> Result<Row, postgres::error::Error> {
        tx.query_one(self.raw, self.bindings)
    }
    pub fn query_into<T: FromSqlOwned>(self, tx: &mut impl postgres::GenericClient)-> Result<T, postgres::error::Error> {
        Ok(tx.query_one(self.raw, self.bindings)?.get(0))
    }
    pub fn query_opt(self, tx: &mut impl postgres::GenericClient)-> Result<Option<Row>, postgres::error::Error> {
        tx.query_opt(self.raw, self.bindings)
    }
    pub fn copy_in(self, tx: &mut impl postgres::GenericClient) -> Result<CopyInWriter<'_>, postgres::error::Error> {
        tx.copy_in(self.raw)
    }
}
#[derive(Clone, Copy)]
pub enum SqlFragment<'a> {
    Raw(&'a str),
    Bindings(&'a [ &'a (dyn ToSql + Sync) ]),
    Binding(&'a (dyn ToSql + Sync)),
    Fn(&'a dyn SqlSnippet<'a>),
    Lucky(&'a Sql<'a>),
}

impl<'a> SqlFragment<'a> {
    pub fn borrow_into_sql_fragment(self) -> Self {
        self
    }
}
pub trait SqlSnippet<'a> {
    fn render_sql(&self, raw: &mut SqlBuilder<'a>);
}
pub type Binding<'a> = &'a (dyn ToSql+Sync);
#[derive(Clone)]
pub struct MapIntoRow<'a, const N: usize, T>{
    iter: T,
    s: PhantomData<&'a ()>
}
impl<'a, const N: usize, T> Iterator for MapIntoRow<'a, N, T>
where T: Iterator,
      <T as Iterator>::Item: IntoTupleSlice<'a, N>
{
    type Item = [Binding<'a>; N];

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.iter.next()?.sql_tuple())
    }

}

pub trait SqlIterExt: Iterator + Sized {
    fn map_into_row<'a, const N: usize, F: FnMut(Self::Item) -> [Binding<'a>; N]>(self, fnx: F) -> Map<Self, F> {
        self.map(fnx)
    }
    fn into_sql_tuples<'a, const N: usize>(self) -> MapIntoRow<'a, N, Self>
        where Self::Item: IntoTupleSlice<'a, N>
    {
        MapIntoRow{
            iter: self,
            s: PhantomData
        }
    }

    fn borrow_into_sql_fragment<'a>(&'a self) -> SqlFragment<'a>
    where Self: IntoIterator + Clone, <Self as IntoIterator>::Item : SqlSnippet<'a>
    {
        SqlFragment::Fn(unsafe {std::mem::transmute::<&'a Self, &'a IterValues<Self>>(self)})
    }
}

impl<T: Iterator + Sized> SqlIterExt for T{}

pub trait IntoTupleSlice<'a, const N: usize> {
    fn sql_tuple(&self) -> [Binding<'a>; N];
}

macro_rules! impl_into_tuple_slice {
    ($count: literal; $($T:ident : $N: tt),*) => {
        impl<'a, $($T: ToSql + Sync,)*> IntoTupleSlice<'a, $count> for ($(&'a $T,)*) {
            fn sql_tuple(&self) -> [Binding<'a>; $count] {
                [$(self.$N),*]
            }
        }
        impl<'a, $($T: ToSql + Sync,)*> IntoTupleSlice<'a, $count> for &'a ($($T,)*) {
            fn sql_tuple(&self) -> [Binding<'a>; $count] {
                [$(&self.$N),*]
            }
        }
    };
}

impl_into_tuple_slice!(1; T0: 0);
impl_into_tuple_slice!(2; T0: 0, T1: 1);
impl_into_tuple_slice!(3; T0: 0, T1: 1, T2: 2);
impl_into_tuple_slice!(4; T0: 0, T1: 1, T2: 2, T3: 3);
impl_into_tuple_slice!(5; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4);
impl_into_tuple_slice!(6; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5);
impl_into_tuple_slice!(7; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6);
impl_into_tuple_slice!(8; T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7);



#[repr(transparent)]
pub struct IterValues<T> { pub iter: T}
impl <'a ,T: IntoIterator + Clone> SqlSnippet<'a> for IterValues<T>
    where <T as IntoIterator>::Item : SqlSnippet<'a>
{
    fn render_sql(&self, sql: &mut SqlBuilder<'a>) {
        for (x,i) in self.iter.clone().into_iter().enumerate() {
            if x > 0 {
                sql.raw.push(',');
            } 
            i.render_sql(sql)
        }
    }
}
impl <'a, const N: usize,> SqlSnippet<'a> for [ Binding<'a>;N ] {
    fn render_sql(&self, sql: &mut SqlBuilder<'a>) {
        sql.raw.push('(');
        for (x, i)in self.iter().enumerate() {
            if x > 0 {
                sql.raw.push(',');
            } 
            sql.push_binding(*i);
        }
        sql.raw.push(')');
    }
}

#[derive(Default)]
pub struct SqlBuilder<'a> {
    pub raw: String,
    pub bindings: Vec<&'a(dyn ToSql + Sync)>
}

impl<'a> SqlBuilder<'a> {
    //todo very broken due to dynamic bindings and static binds fixed ids :( SAD
    pub fn push_binding(&mut self, bindings: &'a (dyn ToSql + Sync)) {
        use std::fmt::Write;
        self.bindings.push(bindings);
        write!(self.raw, "${} ", self.bindings.len()).ok();
    }

    pub fn push(&mut self, frag: &SqlFragment<'a>) {
        match frag {
            SqlFragment::Raw(raw) => {
                self.raw.push_str(raw);
            },
            SqlFragment::Bindings(binds) => {
                self.bindings.extend_from_slice(binds);
            },
            SqlFragment::Lucky(s) => {
                self.raw.push_str(s.raw);
                self.bindings.extend_from_slice(s.bindings);
            },
            SqlFragment::Fn(fnx) => {
                fnx.render_sql(self);
            },
            SqlFragment::Binding(binding) => {
                self.push_binding(*binding)
            },
        }
    }
}
pub struct GeneratedSql<'a> {
    pub raw: String,
    pub bindings: Vec<&'a(dyn ToSql + Sync)>
}

impl<'a> GeneratedSql<'a> {
    pub fn execute(self, tx: &mut impl postgres::GenericClient) -> Result<u64, postgres::error::Error> {
        tx.execute(&self.raw, &self.bindings)
    }
    pub fn batch_execute(self, tx: &mut impl postgres::GenericClient) -> Result<(), postgres::error::Error> {
        tx.batch_execute(&self.raw)
    }
    pub fn query(self, tx: &mut impl postgres::GenericClient)-> Result<Vec<Row>, postgres::error::Error> {
        tx.query(&self.raw, &self.bindings)
    }
    pub fn query_one(self, tx: &mut impl postgres::GenericClient)-> Result<Row, postgres::error::Error> {
        tx.query_one(&self.raw, &self.bindings)
    }
    pub fn query_into<T: FromSqlOwned>(self, tx: &mut impl postgres::GenericClient)-> Result<T, postgres::error::Error> {
        Ok(tx.query_one(&self.raw, &self.bindings)?.get(0))
    }
    pub fn query_opt(self, tx: &mut impl postgres::GenericClient)-> Result<Option<Row>, postgres::error::Error> {
        tx.query_opt(&self.raw, &self.bindings)
    }
    pub fn copy_in(self, tx: &mut impl postgres::GenericClient) -> Result<CopyInWriter<'_>, postgres::error::Error> {
        tx.copy_in(&self.raw)
    }
}
impl<'a> GeneratedSql<'a> {
    pub fn generated(fragments: &[ SqlFragment<'a>]) -> GeneratedSql<'a> {
        let mut builder = SqlBuilder::default();
        for frag in fragments {
            builder.push(frag)
        }
        GeneratedSql { raw: builder.raw, bindings: builder.bindings }
    }
}