use super::schema;
use crate::storage::Result;
use crate::storage::StorageError;
use rusqlite::Connection;
use std::path::Path;
use std::sync::Mutex;
pub struct SqliteBackend {
pub(crate) conn: Mutex<Connection>,
pub(crate) path: String,
}
impl std::fmt::Debug for SqliteBackend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SqliteBackend").field("path", &self.path).finish_non_exhaustive()
}
}
impl SqliteBackend {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let path_str = path.as_ref().to_string_lossy().to_string();
let conn = Connection::open(path.as_ref())
.map_err(|e| StorageError::Backend(format!("Failed to open SQLite database: {e}")))?;
schema::init_schema(&conn)
.map_err(|e| StorageError::Backend(format!("Failed to initialize schema: {e}")))?;
Ok(Self { conn: Mutex::new(conn), path: path_str })
}
pub fn open_in_memory() -> Result<Self> {
let conn = Connection::open_in_memory()
.map_err(|e| StorageError::Backend(format!("Failed to open in-memory SQLite: {e}")))?;
schema::init_schema(&conn)
.map_err(|e| StorageError::Backend(format!("Failed to initialize schema: {e}")))?;
Ok(Self { conn: Mutex::new(conn), path: ":memory:".to_string() })
}
pub fn open_project<P: AsRef<Path>>(project_dir: P) -> Result<Self> {
let dir = project_dir.as_ref().join(".entrenar");
std::fs::create_dir_all(&dir)?;
Self::open(dir.join("experiments.db"))
}
pub fn path(&self) -> &str {
&self.path
}
pub(crate) fn generate_id() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock must not be before UNIX epoch")
.as_nanos();
format!("{ts:x}")
}
pub(crate) fn lock_conn(
&self,
) -> std::result::Result<std::sync::MutexGuard<'_, Connection>, StorageError> {
self.conn
.lock()
.map_err(|e| StorageError::Backend(format!("Failed to acquire connection lock: {e}")))
}
}