use rusqlite::{Connection, ToSql};
use std::cell::RefCell;
use std::rc::Rc;
use crate::errors::{DbError, DbResult};
use crate::traits::repo::{IConnection, IExecutor};
pub struct Executor<Cnn: IConnection> {
cnn: Cnn,
}
impl<Cnn: IConnection> Executor<Cnn> {
#[inline]
pub fn new(cnn: Cnn) -> Self {
Self { cnn }
}
}
fn signal_error() -> rusqlite::Error {
rusqlite::Error::ExecuteReturnedResults
}
fn unwrap_err<T>(
handler: &RefCell<Option<DbError>>,
res: Result<T, rusqlite::Error>,
) -> Result<T, DbError> {
if let Some(ref val) = *handler.borrow() {
Err(val.clone())
} else {
Ok(res?)
}
}
impl<Cnn: IConnection> IExecutor for Executor<Cnn> {
type Locked = Executor<Cnn::Locked>;
fn lock(&self) -> DbResult<Self::Locked> {
Ok(Executor::new(self.cnn.lock_sync()?))
}
fn get_one<T, F: FnMut(&rusqlite::Row<'_>) -> DbResult<T>>(
&self,
query: &str,
params: &[&dyn ToSql],
mut serializer: F,
) -> DbResult<T> {
let real_error = Rc::new(RefCell::new(None));
let err_handler_copy = real_error.clone();
let m_serializer = move |row: &rusqlite::Row| -> rusqlite::Result<T> {
let other_copy = err_handler_copy.clone();
serializer(row).map_err(move |err| {
*other_copy.borrow_mut() = Some(err);
signal_error()
})
};
let fun = move |cnn: &Connection| -> DbResult<T> {
let mut statement = cnn.prepare(query)?;
let qmap = statement.query_map(params, 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, F: FnMut(&rusqlite::Row<'_>) -> DbResult<T>>(
&self,
query: &str,
params: &[&dyn ToSql],
mut serializer: F,
) -> DbResult<Vec<T>> {
let real_error = Rc::new(RefCell::new(None));
let err_handler_copy = real_error.clone();
let m_serializer = move |row: &rusqlite::Row| -> rusqlite::Result<T> {
serializer(row).map_err(|err| {
*err_handler_copy.borrow_mut() = Some(err);
signal_error()
})
};
let fun = move |cnn: &Connection| -> DbResult<Vec<T>> {
let mut statement = cnn.prepare(query)?;
let qmap = statement.query_map(params, 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]) -> DbResult<()> {
self.cnn.with(move |cnn| {
cnn.execute(query, params)?;
Ok(())
})
}
fn execute_return_id(&self, query: &str, params: &[&dyn ToSql]) -> DbResult<i64> {
self.cnn.with(move |cnn| {
cnn.execute(query, params)?;
Ok(cnn.last_insert_rowid())
})
}
}