use anyhow::{anyhow, Result};
use rusqlite::Connection;
pub struct DatabaseConn {
pub conn: Connection,
}
impl DatabaseConn {
pub fn open(path: Option<&str>) -> Result<Self> {
let conn = match path {
Some(p) => Connection::open(p)
.map_err(|e| anyhow!("Failed to open database at '{}': {}", p, e))?,
None => Connection::open_in_memory()
.map_err(|e| anyhow!("Failed to create in-memory database: {}", e))?,
};
let db = DatabaseConn { conn };
db.configure()?;
Ok(db)
}
pub fn new(path: &Option<String>) -> Result<Self> {
Self::open(path.as_deref())
}
pub fn open_path(path: &str) -> Result<Self> {
Self::open(Some(path))
}
pub fn open_in_memory() -> Result<Self> {
Self::open(None)
}
fn configure(&self) -> Result<()> {
let _: String = self
.conn
.query_row("PRAGMA journal_mode=WAL", [], |row| row.get(0))
.map_err(|e| anyhow!("Failed to set journal mode: {}", e))?;
self.conn
.execute("PRAGMA synchronous=NORMAL", [])
.map_err(|e| anyhow!("Failed to set synchronous mode: {}", e))?;
self.conn
.execute("PRAGMA cache_size=100000", [])
.map_err(|e| anyhow!("Failed to set cache size: {}", e))?;
self.conn
.execute("PRAGMA temp_store=MEMORY", [])
.map_err(|e| anyhow!("Failed to set temp store: {}", e))?;
self.conn
.execute("PRAGMA foreign_keys=ON", [])
.map_err(|e| anyhow!("Failed to enable foreign keys: {}", e))?;
Ok(())
}
pub fn execute(&self, sql: &str) -> Result<usize> {
self.conn
.execute(sql, [])
.map_err(|e| anyhow!("Failed to execute SQL: {}", e))
}
pub fn execute_with_params<P: rusqlite::Params>(&self, sql: &str, params: P) -> Result<usize> {
self.conn
.execute(sql, params)
.map_err(|e| anyhow!("Failed to execute SQL with params: {}", e))
}
pub fn transaction(&self) -> Result<rusqlite::Transaction<'_>> {
self.conn
.unchecked_transaction()
.map_err(|e| anyhow!("Failed to begin transaction: {}", e))
}
pub fn table_exists(&self, table_name: &str) -> Result<bool> {
let count: i32 = self
.conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?1",
[table_name],
|row| row.get(0),
)
.map_err(|e| anyhow!("Failed to check table existence: {}", e))?;
Ok(count > 0)
}
pub fn table_count(&self, table_name: &str) -> Result<u64> {
let query = format!("SELECT COUNT(*) FROM {}", table_name);
let count: u64 = self
.conn
.query_row(&query, [], |row| row.get(0))
.map_err(|e| anyhow!("Failed to get table count: {}", e))?;
Ok(count)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_open_in_memory() {
let db = DatabaseConn::open_in_memory();
assert!(db.is_ok());
}
#[test]
fn test_execute() {
let db = DatabaseConn::open_in_memory().unwrap();
let result = db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)");
assert!(result.is_ok());
}
#[test]
fn test_table_exists() {
let db = DatabaseConn::open_in_memory().unwrap();
db.execute("CREATE TABLE test_table (id INTEGER PRIMARY KEY)")
.unwrap();
assert!(db.table_exists("test_table").unwrap());
assert!(!db.table_exists("nonexistent_table").unwrap());
}
#[test]
fn test_table_count() {
let db = DatabaseConn::open_in_memory().unwrap();
db.execute("CREATE TABLE test_table (id INTEGER PRIMARY KEY)")
.unwrap();
db.execute("INSERT INTO test_table (id) VALUES (1), (2), (3)")
.unwrap();
assert_eq!(db.table_count("test_table").unwrap(), 3);
}
}