easy-sqlite 0.4.1

Little sqlite framework
Documentation
use rusqlite::{Connection, ToSql};
use std::mem;
use std::sync::Arc;

use crate::errors::{DbError, DbResult};
use crate::impls::executor::Executor;
use crate::traits::repo::{DynFut, DynFutDbRes, IAsyncConnection, IAsyncExecutor};
use crate::IConnection;

pub struct AExecutor<Cnn: IAsyncConnection> {
    cnn: Cnn,
}

impl<Cnn: IAsyncConnection> AExecutor<Cnn> {
    #[inline]
    pub fn new(cnn: Cnn) -> Self {
        Self { cnn }
    }
}

fn signal_error() -> rusqlite::Error {
    rusqlite::Error::ExecuteReturnedResults
}

fn unwrap_err<T>(handler: &Option<DbError>, res: Result<T, rusqlite::Error>) -> Result<T, DbError> {
    if let Some(ref val) = *handler {
        return Err(val.clone());
    } else {
        Ok(res?)
    }
}

#[allow(mutable_transmutes)]
fn write_ptr<T>(ptr: &T, val: T) {
    // let as_ptr: &T = &arc;
    let as_mut_ptr: &mut T = unsafe { mem::transmute(ptr) };
    *as_mut_ptr = val;
}

fn to_static_str(s: &str) -> &'static str {
    unsafe { mem::transmute(s) }
}

struct ParamWrapper {
    params: &'static [&'static dyn ToSql],
}

unsafe impl Send for ParamWrapper {}
unsafe impl Sync for ParamWrapper {}

impl ParamWrapper {
    fn new(params: &[&dyn ToSql]) -> Self {
        unsafe {
            Self {
                params: mem::transmute(params),
            }
        }
    }

    fn get(&self) -> &'static [&'static dyn ToSql] {
        self.params
    }
}

impl<L: IConnection + 'static, Cnn: IAsyncConnection<Locked = L>> IAsyncExecutor
    for AExecutor<Cnn>
{
    type Locked = Executor<Cnn::Locked>;

    fn lock(&self) -> DynFut<Self::Locked> {
        let lock = self.cnn.lock_sync();
        Box::pin(async move { Executor::new(lock.await) })
    }

    fn get_one<
        T: Send + Sync + 'static,
        F: FnMut(&rusqlite::Row<'_>) -> DbResult<T> + Send + Sync + 'static,
    >(
        &self,
        query: &str,
        params: &[&dyn ToSql],
        mut serializer: F,
    ) -> DynFutDbRes<T> {
        let query = to_static_str(query);
        let params = ParamWrapper::new(params);
        let real_error = Arc::new(None);
        let err_copy = real_error.clone();
        let m_serializer = move |row: &rusqlite::Row| -> rusqlite::Result<T> {
            let err_copy = err_copy.clone();
            serializer(row).map_err(move |err| {
                write_ptr(&*err_copy, Some(err));
                signal_error()
            })
        };
        let fun = move |cnn: &Connection| -> DbResult<T> {
            let mut statement = cnn.prepare(query)?;
            let qmap = statement.query_map(params.get(), m_serializer);
            let val = unwrap_err(&real_error, qmap)?.next();
            match val {
                Some(val) => unwrap_err(&real_error, val),
                None => Err(DbError::NotFound(None)),
            }
        };
        self.cnn.with(fun)
    }

    fn get_many<
        T: Send + Sync + 'static,
        F: FnMut(&rusqlite::Row<'_>) -> DbResult<T> + Send + Sync + 'static,
    >(
        &self,
        query: &str,
        params: &[&dyn ToSql],
        mut serializer: F,
    ) -> DynFutDbRes<Vec<T>> {
        let query = to_static_str(query);
        let params = ParamWrapper::new(params);
        let real_error = Arc::new(None);
        let err_handler_copy = real_error.clone();
        let m_serializer = move |row: &rusqlite::Row| -> rusqlite::Result<T> {
            serializer(row).map_err(|err| {
                write_ptr(&*err_handler_copy, Some(err));
                signal_error()
            })
        };
        let fun = move |cnn: &Connection| -> DbResult<Vec<T>> {
            let mut statement = cnn.prepare(query)?;
            let qmap = statement.query_map(params.get(), m_serializer);
            let val = unwrap_err(&real_error, qmap)?
                .map(|res| unwrap_err(&real_error, res))
                .collect::<Result<Vec<T>, _>>()?;
            Ok(val)
        };
        self.cnn.with(fun)
    }

    fn execute(&self, query: &str, params: &[&dyn ToSql]) -> DynFutDbRes<()> {
        let query = to_static_str(query);
        let params = ParamWrapper::new(params);
        self.cnn.with(move |cnn| {
            cnn.execute(query, params.get())?;
            Ok(())
        })
    }
}