use oxisql_core::{
ColumnInfo, Connection, ForeignKeyInfo, IndexInfo, OxiSqlError, PreparedStatement, Row,
TableInfo, ToSqlValue, Transaction,
};
use crate::connection::SqliteConnection;
fn block_local<F, T>(fut: F) -> Result<T, OxiSqlError>
where
F: std::future::Future<Output = Result<T, OxiSqlError>>,
{
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| OxiSqlError::Other(format!("runtime build error: {e}")))?;
rt.block_on(fut)
}
fn block_static<F, T>(fut: F) -> Result<T, OxiSqlError>
where
F: std::future::Future<Output = Result<T, OxiSqlError>> + Send + 'static,
T: Send + 'static,
{
match tokio::runtime::Handle::try_current() {
Ok(handle) => std::thread::spawn(move || handle.block_on(fut))
.join()
.map_err(|_| OxiSqlError::Other("blocking thread panicked".into()))?,
Err(_) => {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| OxiSqlError::Other(format!("runtime build error: {e}")))?;
Ok(rt.block_on(fut)?)
}
}
}
#[derive(Clone)]
pub struct SqliteConnectionBlocking(SqliteConnection);
impl SqliteConnectionBlocking {
pub fn open(path: &str) -> Result<Self, OxiSqlError> {
let path_owned = path.to_owned();
let inner = block_static(async move { SqliteConnection::open(&path_owned).await })?;
Ok(Self(inner))
}
pub fn open_memory() -> Result<Self, OxiSqlError> {
Self::open(":memory:")
}
pub fn path(&self) -> &str {
self.0.path()
}
pub fn execute(&self, sql: &str, params: &[&dyn ToSqlValue]) -> Result<u64, OxiSqlError> {
block_local(self.0.execute(sql, params))
}
pub fn query(&self, sql: &str, params: &[&dyn ToSqlValue]) -> Result<Vec<Row>, OxiSqlError> {
block_local(self.0.query(sql, params))
}
pub fn execute_batch(&self, sql: &str) -> Result<u64, OxiSqlError> {
block_local(self.0.execute_batch(sql))
}
pub fn ping(&self) -> Result<(), OxiSqlError> {
block_local(self.0.ping())
}
pub fn prepare(&self, sql: &str) -> Result<SqliteBlockingPrepared<'_>, OxiSqlError> {
let inner = block_local(self.0.prepare(sql))?;
Ok(SqliteBlockingPrepared(inner))
}
pub fn transaction(&self) -> Result<SqliteBlockingTransaction<'_>, OxiSqlError> {
let inner = block_local(self.0.transaction())?;
Ok(SqliteBlockingTransaction(inner))
}
pub fn tables(&self) -> Result<Vec<TableInfo>, OxiSqlError> {
block_local(self.0.tables())
}
pub fn columns(&self, table: &str) -> Result<Vec<ColumnInfo>, OxiSqlError> {
block_local(self.0.columns(table))
}
pub fn indexes(&self, table: &str) -> Result<Vec<IndexInfo>, OxiSqlError> {
block_local(self.0.indexes(table))
}
pub fn foreign_keys(&self, table: &str) -> Result<Vec<ForeignKeyInfo>, OxiSqlError> {
block_local(self.0.foreign_keys(table))
}
}
impl std::fmt::Debug for SqliteConnectionBlocking {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SqliteConnectionBlocking")
.field(&self.0)
.finish()
}
}
pub struct SqliteBlockingTransaction<'a>(Box<dyn Transaction + 'a>);
impl<'a> SqliteBlockingTransaction<'a> {
pub fn execute(&mut self, sql: &str, params: &[&dyn ToSqlValue]) -> Result<u64, OxiSqlError> {
block_local(self.0.execute(sql, params))
}
pub fn query(
&mut self,
sql: &str,
params: &[&dyn ToSqlValue],
) -> Result<Vec<Row>, OxiSqlError> {
block_local(self.0.query(sql, params))
}
pub fn commit(self) -> Result<(), OxiSqlError> {
block_local(self.0.commit())
}
pub fn rollback(self) -> Result<(), OxiSqlError> {
block_local(self.0.rollback())
}
}
pub struct SqliteBlockingPrepared<'a>(Box<dyn PreparedStatement + 'a>);
impl<'a> SqliteBlockingPrepared<'a> {
pub fn execute(&mut self, params: &[&dyn ToSqlValue]) -> Result<u64, OxiSqlError> {
block_local(self.0.execute(params))
}
pub fn query(&mut self, params: &[&dyn ToSqlValue]) -> Result<Vec<Row>, OxiSqlError> {
block_local(self.0.query(params))
}
pub fn sql(&self) -> &str {
self.0.sql()
}
}