simple_pg 0.5.8

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

use simple_pg_client as postgres;
pub mod protocol;
pub use simple_pg_client::NoTls;
pub use simple_pg_client::{
    binary_copy, types,
    types::{FromSqlOwned, ToSql},
    Error, ErrorKind, Row,
};
pub use simple_pg_macros::{append_sql, sql, test};
pub use simple_pg_pool::PoolConn;
pub use simple_pg_pool::{Config, ManagerConfig, Pool, PoolConfig, RecyclingMethod, Timeouts};

pub mod bindings;

pub type RawConn = simple_pg_client::Client;
pub use simple_pg_client::IsolationLevel;
pub use simple_pg_pool::Conn;
pub use simple_pg_pool::PoolError;
pub use simple_pg_pool::Transaction;
pub use simple_pg_pool::TransactionConfig;

static TEST_RUNTIME: std::sync::OnceLock<tokio::runtime::Runtime> = std::sync::OnceLock::new();
#[doc(hidden)]
pub fn block_on_test_runtime(
    test_future: std::pin::Pin<&mut dyn std::future::Future<Output = ()>>,
) {
    let rt = TEST_RUNTIME.get_or_init(|| {
        tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .expect("Failed building the Runtime")
    });
    rt.block_on(test_future);
}
#[macro_export]
macro_rules! fieldset {
    (#[lazy] $pub:vis struct $struct: ident {$first: ident $(,$ident: ident)* $(,)?}) => {
        #[repr(C)]
        $pub struct $struct {
            $pub $first: usize,
            $($pub $ident: usize),*
        }
        #[allow(unused)]
        impl $struct {
            pub const FIELDS: &'static [&'static str] = &[
                stringify!($first),
                $(stringify!($ident)),*
            ];
            fn arrays(&mut self) -> (&mut [usize; Self::FIELDS.len()], &[&'static str; Self::FIELDS.len()]) {
                unsafe {
                    (
                        &mut *(self as *mut $struct as *mut [usize; Self::FIELDS.len()]),
                        &*(Self::FIELDS.as_ptr() as *const [&'static str; Self::FIELDS.len()])
                    )
                }
            }
            pub fn lazy_init(&mut self, fields: &[$crate::types::Field]) -> Result<(), $crate::Error> {
                if self.$first != usize::MAX {
                    return Ok(());
                }
                let (values, names) = self.arrays();
                for (i, field) in fields.iter().enumerate() {
                    for (vi, name) in names.iter().enumerate() {
                        if *name == field.name() {
                            values[vi] = i;
                            break;
                        }
                    }
                }
                let (values, names) = self.arrays();
                for (i, value) in values.iter().enumerate() {
                    if *value == usize::MAX {
                        return Err($crate::Error::custom(format!("missing field: {}", names[i]).into()))
                    }
                }
                return Ok(());
            }
        }

        impl Default for $struct {
            fn default() -> Self {
                Self {
                    $first: usize::MAX,
                    $($ident: usize::MAX),*
                }
            }
        }
    };
    ($pub:vis struct $struct: ident {$first: ident $(,$ident: ident)* $(,)?}) => {
        #[repr(C)]
        $pub struct $struct {
            $pub $first: usize,
            $($pub $ident: usize),*
        }
    };
}

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

#[derive(Clone, Copy)]
pub enum SqlFragment<'a> {
    Raw(&'a str),
    Ident(&'a str),
    Bindings(&'a [&'a (dyn ToSql + Sync)]),
    Binding(&'a (dyn ToSql + Sync)),
    Fn(&'a (dyn SqlSnippet<'a> + Sync)),
    Lucky(&'a Sql<'a>),
}
pub use SqlFragment::Raw;

impl<'a> SqlSnippet<'a> for SqlFragment<'a> {
    fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
        raw.push(self)
    }
    fn borrow_into_sql_fragment(&self) -> Self {
        *self
    }
}
// impl<'a> SqlFragment<'a> {
//     pub fn borrow_into_sql_fragment(self) -> Self {
//         self
//     }
// }
impl<'a, T1: SqlSnippet<'a>, T2: SqlSnippet<'a>> SqlSnippet<'a> for (T1, T2) {
    fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
        self.0.render_sql(raw);
        self.1.render_sql(raw);
    }
}

impl<'a, T1: SqlSnippet<'a>, T2: SqlSnippet<'a>, T3: SqlSnippet<'a>> SqlSnippet<'a>
    for (T1, T2, T3)
{
    fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
        self.0.render_sql(raw);
        self.1.render_sql(raw);
        self.2.render_sql(raw);
    }
}
pub trait SqlSnippet<'a> {
    fn render_sql(&self, raw: &mut SqlBuilder<'a>);
    fn borrow_into_sql_fragment(&'a self) -> SqlFragment<'a>
    where
        Self: Sized + Sync,
    {
        SqlFragment::Fn(self)
    }
}
impl<'a, F: Fn(&mut SqlBuilder<'a>)> SqlSnippet<'a> for F {
    fn render_sql(&self, raw: &mut SqlBuilder<'a>) {
        (self)(raw);
    }
}
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> + Sync,
//     {
//         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::Ident(raw) => {
                self.raw.push_str(" ");
                self.raw.push_str(raw);
                self.raw.push_str(" ");
            }
            SqlFragment::Bindings(binds) => {
                self.bindings.extend_from_slice(binds);
            }
            SqlFragment::Lucky(s) => {
                if s.bindings.len() > 0 {
                    todo!("Not implemented")
                }
                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 fn add(&mut self, frag: Sql<'a>) {
        self.raw.push_str(frag.raw);
        self.bindings.extend_from_slice(frag.bindings);
    }
}
pub struct GeneratedSql<'a> {
    pub raw: String,
    pub bindings: Vec<&'a (dyn ToSql + Sync)>,
}

// mod impl_raw_client;
mod impl_pool_client;

impl<'a> GeneratedSql<'a> {
    pub fn query_string(&self) -> &str {
        &self.raw
    }
    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,
        }
    }
}

/// Join a sequence SQL fragments with an optional seperator
pub struct JoinSql<'a, T> {
    list: &'a [T],
    sep: Option<SqlFragment<'a>>,
}
impl<'a, T> JoinSql<'a, T> {
    pub fn new(list: &'a [T]) -> Self {
        JoinSql { list, sep: None }
    }
    pub fn with_seperator(self, sep: SqlFragment<'a>) -> Self {
        JoinSql {
            list: self.list,
            sep: Some(sep),
        }
    }
}
impl<'a, T> SqlSnippet<'a> for JoinSql<'a, T>
where
    &'a T: SqlSnippet<'a>,
{
    fn render_sql(&self, builder: &mut SqlBuilder<'a>) {
        let mut parts = self.list.iter();
        if let Some(part) = parts.next() {
            part.render_sql(builder);
        }
        for part in parts {
            if let Some(sep) = &self.sep {
                builder.push(sep);
            }
            part.render_sql(builder);
        }
    }
}