libchat/storage/src/sqlite.rs
kaichao 74695877fa
Split storage crate to abstract database layer (#30)
* feat: shared storage crate

* chore: remove backup codes

* chore: remove feature gates

* chore: clean out of order demo

* chore: refactor create session

* chore: shorten error name

* chore: clean errors

* chore: remove table exist check

* chore: remove unused traits

* chore: remove unused functions.

* chore: use tempfile for examples
2026-02-03 09:39:02 +08:00

77 lines
2.2 KiB
Rust

//! SQLite storage backend.
use rusqlite::Connection;
use std::path::Path;
use crate::StorageError;
/// Configuration for SQLite storage.
#[derive(Debug, Clone)]
pub enum StorageConfig {
/// In-memory database (for testing).
InMemory,
/// File-based SQLite database.
File(String),
/// SQLCipher encrypted database.
Encrypted { path: String, key: String },
}
/// SQLite database wrapper.
///
/// This provides the core database connection and can be shared
/// across different domain-specific storage implementations.
pub struct SqliteDb {
conn: Connection,
}
impl SqliteDb {
/// Creates a new SQLite database with the given configuration.
pub fn new(config: StorageConfig) -> Result<Self, StorageError> {
let conn = match config {
StorageConfig::InMemory => Connection::open_in_memory()?,
StorageConfig::File(ref path) => Connection::open(path)?,
StorageConfig::Encrypted { ref path, ref key } => {
let conn = Connection::open(path)?;
conn.pragma_update(None, "key", key)?;
conn
}
};
// Enable foreign keys
conn.execute_batch("PRAGMA foreign_keys = ON;")?;
Ok(Self { conn })
}
/// Opens an existing database file.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, StorageError> {
let conn = Connection::open(path)?;
conn.execute_batch("PRAGMA foreign_keys = ON;")?;
Ok(Self { conn })
}
/// Creates an in-memory database (useful for testing).
pub fn in_memory() -> Result<Self, StorageError> {
Self::new(StorageConfig::InMemory)
}
pub fn sqlcipher(path: String, key: String) -> Result<Self, StorageError> {
Self::new(StorageConfig::Encrypted {
path: path,
key: key,
})
}
/// Returns a reference to the underlying connection.
///
/// Use this for domain-specific storage operations.
pub fn connection(&self) -> &Connection {
&self.conn
}
/// Begins a transaction.
pub fn transaction(&mut self) -> Result<rusqlite::Transaction<'_>, StorageError> {
Ok(self.conn.transaction()?)
}
}