mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-04-01 17:13:13 +00:00
chore: clear error conversion
This commit is contained in:
parent
45ad505508
commit
41a1599f9f
41
Cargo.lock
generated
41
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
|
||||
@ -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" }
|
||||
|
||||
@ -27,5 +27,5 @@ serde = "1.0"
|
||||
headers = ["safer-ffi/headers"]
|
||||
|
||||
[dev-dependencies]
|
||||
sqlite = { path = "../sqlite" }
|
||||
tempfile = "3"
|
||||
chat-sqlite = { path = "../sqlite" }
|
||||
tempfile = "3"
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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<rusqlite::Statement<'_>, StorageError> {
|
||||
self.0.prepare(sql).map_err(Self::map_err)
|
||||
}
|
||||
|
||||
pub fn transaction(&mut self) -> Result<Transaction<'_>, StorageError> {
|
||||
self.0.transaction().map_err(Self::map_err)
|
||||
}
|
||||
|
||||
pub fn execute(&self, sql: &str, params: impl rusqlite::Params) -> Result<usize, StorageError> {
|
||||
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<T, F>(
|
||||
&self,
|
||||
sql: &str,
|
||||
params: impl rusqlite::Params,
|
||||
f: F,
|
||||
) -> Result<T, StorageError>
|
||||
where
|
||||
F: FnOnce(&Row) -> Result<T, rusqlite::Error>,
|
||||
{
|
||||
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<Self, StorageError> {
|
||||
pub fn new(config: StorageConfig) -> Result<Self, SqliteError> {
|
||||
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<P: AsRef<Path>>(path: P) -> Result<Self, StorageError> {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, SqliteError> {
|
||||
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<Self, StorageError> {
|
||||
pub fn in_memory() -> Result<Self, SqliteError> {
|
||||
Self::new(StorageConfig::InMemory)
|
||||
}
|
||||
|
||||
pub fn sqlcipher(path: String, key: String) -> Result<Self, StorageError> {
|
||||
pub fn sqlcipher(path: String, key: String) -> Result<Self, SqliteError> {
|
||||
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<rusqlite::Transaction<'_>, StorageError> {
|
||||
pub fn transaction(&mut self) -> Result<rusqlite::Transaction<'_>, SqliteError> {
|
||||
Ok(self.conn.transaction()?)
|
||||
}
|
||||
}
|
||||
32
core/sqlite/src/errors.rs
Normal file
32
core/sqlite/src/errors.rs
Normal file
@ -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<SqliteError> for StorageError {
|
||||
// fn from(err: SqliteError) -> Self {
|
||||
// match err {
|
||||
// SqliteError::Storage(e) => e,
|
||||
// SqliteError::Rusqlite(e) => StorageError::Database(e.to_string()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@ -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<const N: usize>(blob: Vec<u8>) -> [u8; N] {
|
||||
mod tests {
|
||||
use storage::{
|
||||
ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore,
|
||||
StorageConfig,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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"] }
|
||||
|
||||
@ -31,9 +31,3 @@ pub enum StorageError {
|
||||
#[error("invalid data: {0}")]
|
||||
InvalidData(String),
|
||||
}
|
||||
|
||||
impl From<rusqlite::Error> for StorageError {
|
||||
fn from(e: rusqlite::Error) -> Self {
|
||||
StorageError::Database(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -101,10 +101,8 @@ pub trait RatchetStore {
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
/// Loads ratchet state for a conversation.
|
||||
fn load_ratchet_state(
|
||||
&self,
|
||||
conversation_id: &str,
|
||||
) -> Result<RatchetStateRecord, StorageError>;
|
||||
fn load_ratchet_state(&self, conversation_id: &str)
|
||||
-> Result<RatchetStateRecord, StorageError>;
|
||||
|
||||
/// 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<T> ChatStore for T where T: IdentityStore + EphemeralKeyStore + ConversationStore + RatchetStore {}
|
||||
impl<T> ChatStore for T where T: IdentityStore + EphemeralKeyStore + ConversationStore + RatchetStore
|
||||
{}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user