libchat/core/sqlite/src/common.rs
kaichao c44c52b127
feat: storage implementation and trait abstraction (#79)
* feat: storage for conversations

* fix: db types conversion

* feat: run migrations from sql files

* feat: persist identity

* fix: revert double ratchet storage refactor

* fix: clean

* refactor: use result wrapper for ffi

* refactor: uniform storage error into chat error

* fix: zeroize identity record

* fix: zeroize for secret keys in db operations

* fix: transactional sql migration

* fix: remove destroy_string

* feat: db storage for inbox ephermeral keys

* chore: remove in memory hashmap for ephemeral keys

* feat: persist conversation store

* feat: wire with the double ratchet storage

* feat: remove conversation store

* chore: fix conversation type not used

* feat: mock chat store implementation

* chore: sqlite module

* feat: sqlite crate

* chore: sqlite rename

* chore: more refactor

* extract ratchet store trait

* chore: clear error conversion

* chore: remove customized db conn

* chore: fix clippy

* chore: refactor to use generics and enum

* chore: further clean for review comments
2026-04-03 08:25:26 +08:00

67 lines
2.0 KiB
Rust

//! SQLite storage backend.
use rusqlite::Connection;
use storage::StorageError;
use crate::errors::map_rusqlite_error;
/// 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().map_err(map_rusqlite_error)?,
StorageConfig::File(ref path) => Connection::open(path).map_err(map_rusqlite_error)?,
StorageConfig::Encrypted { ref path, ref key } => {
let conn = Connection::open(path).map_err(map_rusqlite_error)?;
conn.pragma_update(None, "key", key)
.map_err(map_rusqlite_error)?;
conn
}
};
// Enable foreign keys
conn.execute_batch("PRAGMA foreign_keys = ON;")
.map_err(map_rusqlite_error)?;
Ok(Self { conn })
}
/// Returns a reference to the underlying connection.
///
/// Use this for domain-specific storage operations.
pub fn connection(&self) -> &Connection {
&self.conn
}
/// Returns a mutable reference to the underlying connection.
///
/// Use this for operations that require mutable access, such as transactions.
pub fn connection_mut(&mut self) -> &mut Connection {
&mut self.conn
}
/// Begins a transaction.
pub fn transaction(&mut self) -> Result<rusqlite::Transaction<'_>, StorageError> {
self.conn.transaction().map_err(map_rusqlite_error)
}
}