use core::ffi::CStr;
use core::ffi::c_int;
use core::mem::MaybeUninit;
use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use alloc::ffi::CString;
#[cfg(feature = "std")]
use std::path::Path;
use crate::ffi;
use crate::utils::c_to_error_text;
use crate::{Code, Connection, Error, Result};
#[derive(Clone, Copy, Debug)]
pub struct OpenOptions {
raw: c_int,
}
impl OpenOptions {
#[inline]
pub fn new() -> Self {
Self {
raw: ffi::SQLITE_OPEN_EXRESCODE,
}
}
#[inline]
pub fn empty() -> Self {
Self { raw: 0 }
}
#[inline]
pub fn read_only(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_READONLY;
self
}
#[inline]
pub fn read_write(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_READWRITE;
self
}
#[inline]
pub fn create(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_CREATE;
self
}
#[inline]
pub fn uri(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_URI;
self
}
#[inline]
pub fn memory(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_MEMORY;
self
}
#[inline]
pub fn no_mutex(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_NOMUTEX;
self
}
#[inline]
pub fn full_mutex(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_FULLMUTEX;
self
}
#[inline]
pub fn shared_cache(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_SHAREDCACHE;
self
}
#[inline]
pub fn private_cache(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_PRIVATECACHE;
self
}
#[inline]
pub fn no_follow(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_NOFOLLOW;
self
}
#[inline]
pub fn extended_result_codes(&mut self) -> &mut Self {
self.raw |= ffi::SQLITE_OPEN_EXRESCODE;
self
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, cfg(feature = "std"))]
#[inline]
pub fn open(&self, path: impl AsRef<Path>) -> Result<Connection> {
let path = path_to_cstring(path.as_ref())?;
self._open(&path)
}
#[inline]
pub fn open_c_str(&self, name: &CStr) -> Result<Connection> {
self._open(name)
}
#[inline]
pub fn open_in_memory(&self) -> Result<Connection> {
self._open(c":memory:")
}
fn _open(&self, name: &CStr) -> Result<Connection> {
unsafe {
let mut raw = MaybeUninit::uninit();
let code = ffi::sqlite3_open_v2(name.as_ptr(), raw.as_mut_ptr(), self.raw, ptr::null());
let raw = raw.assume_init();
if code != ffi::SQLITE_OK {
let error = Error::new(Code::new(code), c_to_error_text(ffi::sqlite3_errmsg(raw)));
ffi::sqlite3_close_v2(raw);
return Err(error);
}
let is_thread_safe = ffi::sqlite3_threadsafe() != 0
&& (self.raw & (ffi::SQLITE_OPEN_NOMUTEX | ffi::SQLITE_OPEN_FULLMUTEX)) != 0;
Ok(Connection::from_raw(
NonNull::new_unchecked(raw),
is_thread_safe,
))
}
}
}
#[cfg(feature = "std")]
pub(crate) fn path_to_cstring(p: &Path) -> Result<CString> {
let Some(bytes) = p.to_str() else {
return Err(Error::new(Code::MISUSE, "path is not valid utf-8"));
};
let Ok(string) = CString::new(bytes) else {
return Err(Error::new(
Code::MISUSE,
"path utf-8 contains internal null",
));
};
Ok(string)
}