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_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(())
})
}
}