From 41a1599f9fb10f8daff7ef3b5229b67fcdf2afba Mon Sep 17 00:00:00 2001 From: kaichaosun Date: Mon, 30 Mar 2026 16:44:53 +0800 Subject: [PATCH] chore: clear error conversion --- Cargo.lock | 41 ++++++------- core/conversations/Cargo.toml | 2 +- core/double-ratchets/Cargo.toml | 4 +- core/double-ratchets/src/lib.rs | 1 - core/double-ratchets/src/storage/mod.rs | 2 +- core/sqlite/Cargo.toml | 4 +- .../src/sqlite.rs => sqlite/src/common.rs} | 59 +++++++++++++++---- core/sqlite/src/errors.rs | 32 ++++++++++ core/sqlite/src/lib.rs | 12 ++-- core/sqlite/src/migrations.rs | 4 +- core/storage/Cargo.toml | 1 - core/storage/src/errors.rs | 6 -- core/storage/src/lib.rs | 7 +-- core/storage/src/store.rs | 9 ++- 14 files changed, 122 insertions(+), 62 deletions(-) rename core/{storage/src/sqlite.rs => sqlite/src/common.rs} (55%) create mode 100644 core/sqlite/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 1223d50..e24f171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.54" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "shlex", @@ -108,6 +108,19 @@ dependencies = [ "prost", ] +[[package]] +name = "chat-sqlite" +version = "0.1.0" +dependencies = [ + "crypto", + "hex", + "rusqlite", + "storage", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "cipher" version = "0.4.4" @@ -233,12 +246,12 @@ version = "0.0.1" dependencies = [ "blake2", "chacha20poly1305", + "chat-sqlite", "hkdf", "rand", "rand_core", "safer-ffi", "serde", - "sqlite", "storage", "tempfile", "thiserror", @@ -353,9 +366,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "foldhash" @@ -504,13 +517,13 @@ dependencies = [ "base64", "blake2", "chat-proto", + "chat-sqlite", "crypto", "double-ratchets", "hex", "prost", "rand_core", "safer-ffi", - "sqlite", "storage", "tempfile", "thiserror", @@ -580,9 +593,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", @@ -893,17 +906,6 @@ dependencies = [ "der", ] -[[package]] -name = "sqlite" -version = "0.1.0" -dependencies = [ - "crypto", - "hex", - "storage", - "tempfile", - "zeroize", -] - [[package]] name = "stabby" version = "36.2.2" @@ -944,7 +946,6 @@ name = "storage" version = "0.1.0" dependencies = [ "crypto", - "rusqlite", "thiserror", ] diff --git a/core/conversations/Cargo.toml b/core/conversations/Cargo.toml index fe47d4b..dcbef43 100644 --- a/core/conversations/Cargo.toml +++ b/core/conversations/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["rlib","staticlib","dylib"] [dependencies] base64 = "0.22" -sqlite = { path = "../sqlite" } +chat-sqlite = { path = "../sqlite" } blake2.workspace = true chat-proto = { git = "https://github.com/logos-messaging/chat_proto" } crypto = { path = "../crypto" } diff --git a/core/double-ratchets/Cargo.toml b/core/double-ratchets/Cargo.toml index dc76a17..513103a 100644 --- a/core/double-ratchets/Cargo.toml +++ b/core/double-ratchets/Cargo.toml @@ -27,5 +27,5 @@ serde = "1.0" headers = ["safer-ffi/headers"] [dev-dependencies] -sqlite = { path = "../sqlite" } -tempfile = "3" \ No newline at end of file +chat-sqlite = { path = "../sqlite" } +tempfile = "3" diff --git a/core/double-ratchets/src/lib.rs b/core/double-ratchets/src/lib.rs index db4c741..b8778fc 100644 --- a/core/double-ratchets/src/lib.rs +++ b/core/double-ratchets/src/lib.rs @@ -10,7 +10,6 @@ pub mod types; pub use keypair::InstallationKeyPair; pub use state::{Header, RatchetState, SkippedKey}; -pub use storage::StorageConfig; pub use storage::{ RatchetSession, SessionError, restore_ratchet_state, to_ratchet_record, to_skipped_key_records, }; diff --git a/core/double-ratchets/src/storage/mod.rs b/core/double-ratchets/src/storage/mod.rs index 92c7336..4a45618 100644 --- a/core/double-ratchets/src/storage/mod.rs +++ b/core/double-ratchets/src/storage/mod.rs @@ -9,5 +9,5 @@ mod types; pub use errors::SessionError; pub use session::RatchetSession; -pub use storage::{RatchetStateRecord, RatchetStore, SkippedKeyRecord, StorageConfig, StorageError}; +pub use storage::{RatchetStateRecord, RatchetStore, SkippedKeyRecord, StorageError}; pub use types::{restore_ratchet_state, to_ratchet_record, to_skipped_key_records}; diff --git a/core/sqlite/Cargo.toml b/core/sqlite/Cargo.toml index 7f7e5aa..f17a11b 100644 --- a/core/sqlite/Cargo.toml +++ b/core/sqlite/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sqlite" +name = "chat-sqlite" version = "0.1.0" edition = "2024" description = "SQLite storage implementation for libchat" @@ -9,6 +9,8 @@ crypto = { path = "../crypto" } hex = "0.4.3" storage = { path = "../storage" } zeroize = { version = "1.8.2", features = ["derive"] } +rusqlite = { version = "0.35", features = ["bundled-sqlcipher-vendored-openssl"] } +thiserror = "2" [dev-dependencies] tempfile = "3" diff --git a/core/storage/src/sqlite.rs b/core/sqlite/src/common.rs similarity index 55% rename from core/storage/src/sqlite.rs rename to core/sqlite/src/common.rs index c6b00ab..1132fe4 100644 --- a/core/storage/src/sqlite.rs +++ b/core/sqlite/src/common.rs @@ -1,9 +1,10 @@ //! SQLite storage backend. -use rusqlite::Connection; +use rusqlite::{Connection, Row, Transaction}; use std::path::Path; +use storage::StorageError; -use crate::StorageError; +use crate::errors::SqliteError; /// Configuration for SQLite storage. #[derive(Debug, Clone)] @@ -16,17 +17,51 @@ pub enum StorageConfig { Encrypted { path: String, key: String }, } +pub struct DbConn(rusqlite::Connection); +impl DbConn { + fn map_err(e: rusqlite::Error) -> StorageError { + StorageError::Database(e.to_string()) + } + pub fn prepare(&self, sql: &str) -> Result, StorageError> { + self.0.prepare(sql).map_err(Self::map_err) + } + + pub fn transaction(&mut self) -> Result, StorageError> { + self.0.transaction().map_err(Self::map_err) + } + + pub fn execute(&self, sql: &str, params: impl rusqlite::Params) -> Result { + self.0.execute(sql, params).map_err(Self::map_err) + } + + pub fn execute_batch(&self, sql: &str) -> Result<(), StorageError> { + self.0.execute_batch(sql).map_err(Self::map_err) + } + + pub fn query_row( + &self, + sql: &str, + params: impl rusqlite::Params, + f: F, + ) -> Result + where + F: FnOnce(&Row) -> Result, + { + self.0.query_row(sql, params, f).map_err(Self::map_err) + } +} + /// SQLite database wrapper. /// /// This provides the core database connection and can be shared /// across different domain-specific storage implementations. pub struct SqliteDb { - conn: Connection, + conn: DbConn, } impl SqliteDb { /// Creates a new SQLite database with the given configuration. - pub fn new(config: StorageConfig) -> Result { + pub fn new(config: StorageConfig) -> Result { let conn = match config { StorageConfig::InMemory => Connection::open_in_memory()?, StorageConfig::File(ref path) => Connection::open(path)?, @@ -40,41 +75,41 @@ impl SqliteDb { // Enable foreign keys conn.execute_batch("PRAGMA foreign_keys = ON;")?; - Ok(Self { conn }) + Ok(Self { conn: DbConn(conn) }) } /// Opens an existing database file. - pub fn open>(path: P) -> Result { + pub fn open>(path: P) -> Result { let conn = Connection::open(path)?; conn.execute_batch("PRAGMA foreign_keys = ON;")?; - Ok(Self { conn }) + Ok(Self { conn: DbConn(conn) }) } /// Creates an in-memory database (useful for testing). - pub fn in_memory() -> Result { + pub fn in_memory() -> Result { Self::new(StorageConfig::InMemory) } - pub fn sqlcipher(path: String, key: String) -> Result { + pub fn sqlcipher(path: String, key: String) -> Result { Self::new(StorageConfig::Encrypted { path, key }) } /// Returns a reference to the underlying connection. /// /// Use this for domain-specific storage operations. - pub fn connection(&self) -> &Connection { + pub fn connection(&self) -> &DbConn { &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 { + pub fn connection_mut(&mut self) -> &mut DbConn { &mut self.conn } /// Begins a transaction. - pub fn transaction(&mut self) -> Result, StorageError> { + pub fn transaction(&mut self) -> Result, SqliteError> { Ok(self.conn.transaction()?) } } diff --git a/core/sqlite/src/errors.rs b/core/sqlite/src/errors.rs new file mode 100644 index 0000000..e45b0a8 --- /dev/null +++ b/core/sqlite/src/errors.rs @@ -0,0 +1,32 @@ +use storage::StorageError; +use thiserror::Error; + +// #[derive(Debug, thiserror::Error, Display)] +// pub struct SqliteError(pub rusqlite::Error); +// +// #[derive(Debug, thiserror::Error)] +// pub enum SqliteError { +// #[error(transparent)] +// Rusqlite(#[from] rusqlite::Error), + +// #[error(transparent)] +// Storage(#[from] StorageError), +// } + +#[derive(Debug, Error)] +pub enum SqliteError { + #[error("sqlite error: {0}")] + Rusqlite(#[from] rusqlite::Error), + + #[error(transparent)] + Storage(#[from] StorageError), +} + +// impl From for StorageError { +// fn from(err: SqliteError) -> Self { +// match err { +// SqliteError::Storage(e) => e, +// SqliteError::Rusqlite(e) => StorageError::Database(e.to_string()), +// } +// } +// } diff --git a/core/sqlite/src/lib.rs b/core/sqlite/src/lib.rs index 2736fca..63ae206 100644 --- a/core/sqlite/src/lib.rs +++ b/core/sqlite/src/lib.rs @@ -1,19 +1,24 @@ //! Chat-specific SQLite storage implementation. +mod common; +mod errors; mod migrations; mod types; use std::collections::HashSet; use crypto::{Identity, PrivateKey}; +use rusqlite::{Error as RusqliteError, Transaction, params}; use storage::{ ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore, - RatchetStateRecord, RatchetStore, RusqliteError, SkippedKeyRecord, SqliteDb, StorageConfig, - StorageError, Transaction, params, + RatchetStateRecord, RatchetStore, SkippedKeyRecord, StorageError, }; use zeroize::Zeroize; -use crate::types::IdentityRecord; +use crate::{ + common::{SqliteDb, StorageConfig}, + types::IdentityRecord, +}; /// Chat-specific storage operations. /// @@ -440,7 +445,6 @@ fn blob_to_array(blob: Vec) -> [u8; N] { mod tests { use storage::{ ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore, - StorageConfig, }; use super::*; diff --git a/core/sqlite/src/migrations.rs b/core/sqlite/src/migrations.rs index e274055..700b1e4 100644 --- a/core/sqlite/src/migrations.rs +++ b/core/sqlite/src/migrations.rs @@ -3,7 +3,7 @@ //! SQL migrations are embedded at compile time and applied in order. //! Each migration is applied atomically within a transaction. -use storage::{Connection, StorageError}; +use crate::{common::DbConn, errors::SqliteError}; /// Embeds and returns all migration SQL files in order. pub fn get_migrations() -> Vec<(&'static str, &'static str)> { @@ -22,7 +22,7 @@ pub fn get_migrations() -> Vec<(&'static str, &'static str)> { /// Applies all migrations to the database. /// /// Uses a simple version tracking table to avoid re-running migrations. -pub fn apply_migrations(conn: &mut Connection) -> Result<(), StorageError> { +pub fn apply_migrations(conn: &mut DbConn) -> Result<(), SqliteError> { // Create migrations tracking table if it doesn't exist conn.execute_batch( "CREATE TABLE IF NOT EXISTS _migrations ( diff --git a/core/storage/Cargo.toml b/core/storage/Cargo.toml index 3339759..b176087 100644 --- a/core/storage/Cargo.toml +++ b/core/storage/Cargo.toml @@ -7,4 +7,3 @@ description = "Shared storage layer for libchat" [dependencies] crypto = { path = "../crypto" } thiserror = "2" -rusqlite = { version = "0.35", features = ["bundled-sqlcipher-vendored-openssl"] } diff --git a/core/storage/src/errors.rs b/core/storage/src/errors.rs index 9d65d64..eb4b8b7 100644 --- a/core/storage/src/errors.rs +++ b/core/storage/src/errors.rs @@ -31,9 +31,3 @@ pub enum StorageError { #[error("invalid data: {0}")] InvalidData(String), } - -impl From for StorageError { - fn from(e: rusqlite::Error) -> Self { - StorageError::Database(e.to_string()) - } -} diff --git a/core/storage/src/lib.rs b/core/storage/src/lib.rs index 5d9488d..6c8f367 100644 --- a/core/storage/src/lib.rs +++ b/core/storage/src/lib.rs @@ -3,18 +3,13 @@ //! This crate provides a common storage abstraction that can be used by //! multiple crates in the libchat workspace (double-ratchets, conversations, etc.). //! -//! Uses SQLCipher for encrypted SQLite storage. +//! The storage implementation is handled by other crates. mod errors; -mod sqlite; mod store; pub use errors::StorageError; -pub use sqlite::{SqliteDb, StorageConfig}; pub use store::{ ChatStore, ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore, RatchetStateRecord, RatchetStore, SkippedKeyRecord, }; - -// Re-export rusqlite types that domain crates will need -pub use rusqlite::{Connection, Error as RusqliteError, Transaction, params}; diff --git a/core/storage/src/store.rs b/core/storage/src/store.rs index c7fd480..a24ad25 100644 --- a/core/storage/src/store.rs +++ b/core/storage/src/store.rs @@ -101,10 +101,8 @@ pub trait RatchetStore { ) -> Result<(), StorageError>; /// Loads ratchet state for a conversation. - fn load_ratchet_state( - &self, - conversation_id: &str, - ) -> Result; + fn load_ratchet_state(&self, conversation_id: &str) + -> Result; /// Loads skipped keys for a conversation. fn load_skipped_keys( @@ -124,4 +122,5 @@ pub trait RatchetStore { pub trait ChatStore: IdentityStore + EphemeralKeyStore + ConversationStore + RatchetStore {} -impl ChatStore for T where T: IdentityStore + EphemeralKeyStore + ConversationStore + RatchetStore {} +impl ChatStore for T where T: IdentityStore + EphemeralKeyStore + ConversationStore + RatchetStore +{}