use libsqlite3_sys::{self as ffi};
use std::{ffi::CStr, ptr, time::Duration};
use super::{DatabaseError, OpenFlag, SqliteMutexGuard, error::ConfigureError};
macro_rules! ffi_db {
(@ $method:ident($db:expr $(, $($args:expr),*)?), $into:ty $(, $ret:expr)?) => {
match {
let db = $db;
let result = unsafe { libsqlite3_sys::$method(db $(, $($args),*)?) };
(db,result)
} {
(_, libsqlite3_sys::SQLITE_OK) => Ok({ $($ret)? }),
(db, result) => Err(<$into>::from(super::DatabaseError::from_code(result, db))),
}
};
($method:ident($db:expr $(, $($args:expr),*)?) as _ $(, $ret:expr)?) => {
super::database::ffi_db!(@ $method($db $(, $($args),*)?), super::DatabaseError $(, $ret)?)
};
($method:ident($db:expr $(, $($args:expr),*)?) $(, $ret:expr)?) => {
super::database::ffi_db!(@ $method($db $(, $($args),*)?), _ $(, $ret)?)
};
}
pub(super) use ffi_db;
pub fn open_v2(path: &CStr, flags: OpenFlag) -> Result<*mut ffi::sqlite3, DatabaseError> {
let mut sqlite = ptr::null_mut();
let result = unsafe { ffi::sqlite3_open_v2(path.as_ptr(), &mut sqlite, flags.0, ptr::null()) };
if sqlite.is_null() {
return Err(DatabaseError {
message: ffi::code_to_str(result).into(),
code: result,
});
}
match result {
ffi::SQLITE_OK => Ok(sqlite),
_ => Err(DatabaseError::from_code(result, sqlite)),
}
}
pub trait Database {
fn as_ptr(&self) -> *mut ffi::sqlite3;
}
impl<D> Database for &mut D where D: Database {
fn as_ptr(&self) -> *mut libsqlite3_sys::sqlite3 {
D::as_ptr(self)
}
}
impl<D> Database for &D where D: Database {
fn as_ptr(&self) -> *mut libsqlite3_sys::sqlite3 {
D::as_ptr(self)
}
}
impl Database for *mut ffi::sqlite3 {
fn as_ptr(&self) -> *mut ffi::sqlite3 {
*self
}
}
impl<T> DatabaseExt for T where T: Database { }
pub trait DatabaseExt: Database {
fn last_insert_rowid(&self) -> i64 {
unsafe { ffi::sqlite3_last_insert_rowid(self.as_ptr()) }
}
fn mutex_enter(&self) -> SqliteMutexGuard {
let lock = unsafe {
let lock = ffi::sqlite3_db_mutex(self.as_ptr());
assert!(!lock.is_null(),"connection guarantee to be in serialize mode");
ffi::sqlite3_mutex_enter(lock);
lock
};
SqliteMutexGuard::new(lock)
}
fn busy_timeout(&mut self, timeout: impl Into<Duration>) -> Result<(), ConfigureError> {
let timeout = timeout.into().as_millis().try_into().unwrap_or(i32::MAX);
ffi_db!(sqlite3_busy_timeout(self.as_ptr(), timeout))
}
fn extended_result_codes(&mut self, onoff: bool) -> Result<(), ConfigureError> {
ffi_db!(sqlite3_extended_result_codes(self.as_ptr(), onoff as _))
}
fn errmsg(&self) -> Option<&str> {
let data = unsafe { ffi::sqlite3_errmsg(self.as_ptr()) };
match data.is_null() {
true => None,
false => unsafe { CStr::from_ptr(data) }.to_str().ok(),
}
}
fn errstr(code: i32) -> Option<&'static str> {
let data = unsafe { ffi::sqlite3_errstr(code) };
match data.is_null() {
true => None,
false => unsafe { CStr::from_ptr(data) }.to_str().ok(),
}
}
fn extended_errcode(&self) -> i32 {
unsafe { ffi::sqlite3_extended_errcode(self.as_ptr()) }
}
fn close(&self) -> Result<(), DatabaseError> {
ffi_db!(sqlite3_close(self.as_ptr()) as _)
}
}