pub mod params; pub mod value; pub use value::Value; pub use params::params_from_iter; use crate::params::*; use std::num::NonZero; use std::rc::Rc; use std::sync::{Arc, Mutex}; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("SQL conversion failure: `{0}`")] ToSqlConversionFailure(BoxError), #[error("Mutex lock error: {0}")] MutexError(String), } impl From for Error { fn from(_err: limbo_core::LimboError) -> Self { todo!(); } } pub(crate) type BoxError = Box; pub type Result = std::result::Result; pub struct Builder { path: String, } impl Builder { pub fn new_local(path: &str) -> Self { Self { path: path.to_string(), } } #[allow(unused_variables, clippy::arc_with_non_send_sync)] pub async fn build(self) -> Result { match self.path.as_str() { ":memory:" => { let io: Arc = Arc::new(limbo_core::MemoryIO::new()); let db = limbo_core::Database::open_file(io, self.path.as_str(), false)?; Ok(Database { inner: db }) } path => { let io: Arc = Arc::new(limbo_core::PlatformIO::new()?); let db = limbo_core::Database::open_file(io, path, false)?; Ok(Database { inner: db }) } } } } pub struct Database { inner: Arc, } unsafe impl Send for Database {} unsafe impl Sync for Database {} impl Database { pub fn connect(&self) -> Result { let conn = self.inner.connect()?; #[allow(clippy::arc_with_non_send_sync)] let connection = Connection { inner: Arc::new(Mutex::new(conn)), }; Ok(connection) } } pub struct Connection { inner: Arc>>, } impl Clone for Connection { fn clone(&self) -> Self { Self { inner: Arc::clone(&self.inner), } } } unsafe impl Send for Connection {} unsafe impl Sync for Connection {} impl Connection { pub async fn query(&self, sql: &str, params: impl IntoParams) -> Result { let mut stmt = self.prepare(sql).await?; stmt.query(params).await } pub async fn execute(&self, sql: &str, params: impl IntoParams) -> Result { let mut stmt = self.prepare(sql).await?; stmt.execute(params).await } pub async fn prepare(&self, sql: &str) -> Result { let conn = self .inner .lock() .map_err(|e| Error::MutexError(e.to_string()))?; let stmt = conn.prepare(sql)?; #[allow(clippy::arc_with_non_send_sync)] let statement = Statement { inner: Arc::new(Mutex::new(stmt)), }; Ok(statement) } } pub struct Statement { inner: Arc>, } unsafe impl Send for Statement {} unsafe impl Sync for Statement {} impl Statement { pub async fn query(&mut self, params: impl IntoParams) -> Result { let params = params.into_params()?; match params { params::Params::None => (), params::Params::Positional(values) => { for (i, value) in values.into_iter().enumerate() { let mut stmt = self.inner.lock().unwrap(); stmt.bind_at(NonZero::new(i + 1).unwrap(), value.into()); } } params::Params::Named(_items) => todo!(), } #[allow(clippy::arc_with_non_send_sync)] let rows = Rows { inner: Arc::clone(&self.inner), }; Ok(rows) } pub async fn execute(&mut self, params: impl IntoParams) -> Result { let params = params.into_params()?; match params { params::Params::None => (), params::Params::Positional(values) => { for (i, value) in values.into_iter().enumerate() { let mut stmt = self.inner.lock().unwrap(); stmt.bind_at(NonZero::new(i + 1).unwrap(), value.into()); } } params::Params::Named(_items) => todo!(), } loop { let mut stmt = self.inner.lock().unwrap(); match stmt.step() { Ok(limbo_core::StepResult::Row) => { // unexpected row during execution, error out. return Ok(2); } Ok(limbo_core::StepResult::Done) => { return Ok(0); } Ok(limbo_core::StepResult::IO) => { let _ = stmt.run_once(); //return Ok(1); } Ok(limbo_core::StepResult::Busy) => { return Ok(4); } Ok(limbo_core::StepResult::Interrupt) => { return Ok(3); } Err(err) => { return Err(err.into()); } } } } } pub trait IntoValue { fn into_value(self) -> Result; } #[derive(Debug, Clone)] pub enum Params { None, Positional(Vec), Named(Vec<(String, Value)>), } pub struct Transaction {} pub struct Rows { inner: Arc>, } unsafe impl Send for Rows {} unsafe impl Sync for Rows {} impl Rows { pub async fn next(&mut self) -> Result> { let mut stmt = self .inner .lock() .map_err(|e| Error::MutexError(e.to_string()))?; match stmt.step() { Ok(limbo_core::StepResult::Row) => { let row = stmt.row().unwrap(); Ok(Some(Row { values: row.get_values().map(|v| v.to_owned()).collect(), })) } _ => Ok(None), } } } pub struct Row { values: Vec, } unsafe impl Send for Row {} unsafe impl Sync for Row {} impl Row { pub fn get_value(&self, index: usize) -> Result { let value = &self.values[index]; match value { limbo_core::OwnedValue::Integer(i) => Ok(Value::Integer(*i)), limbo_core::OwnedValue::Null => Ok(Value::Null), limbo_core::OwnedValue::Float(f) => Ok(Value::Real(*f)), limbo_core::OwnedValue::Text(text) => Ok(Value::Text(text.to_string())), limbo_core::OwnedValue::Blob(items) => Ok(Value::Blob(items.to_vec())), } } }