chore: sqlite rename

This commit is contained in:
kaichaosun 2026-03-30 12:44:26 +08:00
parent 9a90e86cb3
commit 927be286e8
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
12 changed files with 47 additions and 150 deletions

24
Cargo.lock generated
View File

@ -108,17 +108,6 @@ dependencies = [
"prost", "prost",
] ]
[[package]]
name = "chat-sqlite"
version = "0.1.0"
dependencies = [
"crypto",
"hex",
"storage",
"tempfile",
"zeroize",
]
[[package]] [[package]]
name = "cipher" name = "cipher"
version = "0.4.4" version = "0.4.4"
@ -513,13 +502,13 @@ dependencies = [
"base64", "base64",
"blake2", "blake2",
"chat-proto", "chat-proto",
"chat-sqlite",
"crypto", "crypto",
"double-ratchets", "double-ratchets",
"hex", "hex",
"prost", "prost",
"rand_core", "rand_core",
"safer-ffi", "safer-ffi",
"sqlite",
"storage", "storage",
"tempfile", "tempfile",
"thiserror", "thiserror",
@ -902,6 +891,17 @@ dependencies = [
"der", "der",
] ]
[[package]]
name = "sqlite"
version = "0.1.0"
dependencies = [
"crypto",
"hex",
"storage",
"tempfile",
"zeroize",
]
[[package]] [[package]]
name = "stabby" name = "stabby"
version = "36.2.2" version = "36.2.2"

View File

@ -3,7 +3,7 @@
resolver = "3" resolver = "3"
members = [ members = [
"core/chat-sqlite", "core/sqlite",
"core/conversations", "core/conversations",
"core/crypto", "core/crypto",
"core/double-ratchets", "core/double-ratchets",

View File

@ -8,7 +8,7 @@ crate-type = ["rlib","staticlib","dylib"]
[dependencies] [dependencies]
base64 = "0.22" base64 = "0.22"
chat-sqlite = { path = "../chat-sqlite" } sqlite = { path = "../sqlite" }
blake2.workspace = true blake2.workspace = true
chat-proto = { git = "https://github.com/logos-messaging/chat_proto" } chat-proto = { git = "https://github.com/logos-messaging/chat_proto" }
crypto = { path = "../crypto" } crypto = { path = "../crypto" }

View File

@ -13,7 +13,7 @@ use safer_ffi::{
prelude::{c_slice, repr_c}, prelude::{c_slice, repr_c},
}; };
use chat_sqlite::ChatStorage; use sqlite::ChatStorage;
use storage::StorageConfig; use storage::StorageConfig;
use crate::{ use crate::{

View File

@ -1,12 +1,10 @@
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use chat_sqlite::ChatStorage;
use crypto::Identity; use crypto::Identity;
use double_ratchets::{RatchetState, RatchetStorage}; use double_ratchets::{RatchetState, RatchetStorage};
use storage::{ use sqlite::ChatStorage;
ChatStore, ConversationKind, ConversationMeta, IdentityStore, StorageConfig, use storage::{ChatStore, ConversationKind, ConversationMeta, IdentityStore, StorageConfig};
};
use crate::{ use crate::{
conversation::{ConversationId, Convo, Id, PrivateV1Convo}, conversation::{ConversationId, Convo, Id, PrivateV1Convo},
@ -24,7 +22,7 @@ pub use crate::inbox::Introduction;
pub struct Context<T: ChatStore> { pub struct Context<T: ChatStore> {
_identity: Rc<Identity>, _identity: Rc<Identity>,
inbox: Inbox, inbox: Inbox,
storage: T, store: T,
ratchet_storage: RatchetStorage, ratchet_storage: RatchetStorage,
} }
@ -57,7 +55,7 @@ impl<T: ChatStore> Context<T> {
Ok(Self { Ok(Self {
_identity: identity, _identity: identity,
inbox, inbox,
storage: store, store,
ratchet_storage, ratchet_storage,
}) })
} }
@ -95,7 +93,7 @@ impl<T: ChatStore> Context<T> {
} }
pub fn list_conversations(&self) -> Result<Vec<ConversationIdOwned>, ChatError> { pub fn list_conversations(&self) -> Result<Vec<ConversationIdOwned>, ChatError> {
let records = self.storage.load_conversations()?; let records = self.store.load_conversations()?;
Ok(records Ok(records
.into_iter() .into_iter()
.map(|r| Arc::from(r.local_convo_id.as_str())) .map(|r| Arc::from(r.local_convo_id.as_str()))
@ -128,7 +126,7 @@ impl<T: ChatStore> Context<T> {
let enc = EncryptedPayload::decode(env.payload)?; let enc = EncryptedPayload::decode(env.payload)?;
match convo_id { match convo_id {
c if c == self.inbox.id() => self.dispatch_to_inbox(enc), c if c == self.inbox.id() => self.dispatch_to_inbox(enc),
c if self.storage.has_conversation(&c)? => self.dispatch_to_convo(&c, enc), c if self.store.has_conversation(&c)? => self.dispatch_to_convo(&c, enc),
_ => Ok(None), _ => Ok(None),
} }
} }
@ -141,14 +139,14 @@ impl<T: ChatStore> Context<T> {
// Look up the ephemeral key from storage // Look up the ephemeral key from storage
let key_hex = Inbox::extract_ephemeral_key_hex(&enc_payload)?; let key_hex = Inbox::extract_ephemeral_key_hex(&enc_payload)?;
let ephemeral_key = self let ephemeral_key = self
.storage .store
.load_ephemeral_key(&key_hex)? .load_ephemeral_key(&key_hex)?
.ok_or(ChatError::UnknownEphemeralKey())?; .ok_or(ChatError::UnknownEphemeralKey())?;
let (convo, content) = self.inbox.handle_frame(&ephemeral_key, enc_payload)?; let (convo, content) = self.inbox.handle_frame(&ephemeral_key, enc_payload)?;
// Remove consumed ephemeral key from storage // Remove consumed ephemeral key from storage
self.storage.remove_ephemeral_key(&key_hex)?; self.store.remove_ephemeral_key(&key_hex)?;
self.persist_convo(convo.as_ref()); self.persist_convo(convo.as_ref());
Ok(content) Ok(content)
@ -170,7 +168,7 @@ impl<T: ChatStore> Context<T> {
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> { pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> {
let (intro, public_key_hex, private_key) = self.inbox.create_intro_bundle(); let (intro, public_key_hex, private_key) = self.inbox.create_intro_bundle();
self.storage self.store
.save_ephemeral_key(&public_key_hex, &private_key)?; .save_ephemeral_key(&public_key_hex, &private_key)?;
Ok(intro.into()) Ok(intro.into())
} }
@ -178,7 +176,7 @@ impl<T: ChatStore> Context<T> {
/// Loads a conversation from DB by constructing it from metadata + ratchet state. /// Loads a conversation from DB by constructing it from metadata + ratchet state.
fn load_convo(&self, convo_id: ConversationId) -> Result<PrivateV1Convo, ChatError> { fn load_convo(&self, convo_id: ConversationId) -> Result<PrivateV1Convo, ChatError> {
let record = self let record = self
.storage .store
.load_conversation(convo_id)? .load_conversation(convo_id)?
.ok_or_else(|| ChatError::NoConvo(convo_id.into()))?; .ok_or_else(|| ChatError::NoConvo(convo_id.into()))?;
@ -208,124 +206,22 @@ impl<T: ChatStore> Context<T> {
remote_convo_id: convo.remote_id(), remote_convo_id: convo.remote_id(),
kind: convo.convo_type().into(), kind: convo.convo_type().into(),
}; };
let _ = self.storage.save_conversation(&convo_info); let _ = self.store.save_conversation(&convo_info);
let _ = convo.save_ratchet_state(&mut self.ratchet_storage); let _ = convo.save_ratchet_state(&mut self.ratchet_storage);
Arc::from(convo.id()) Arc::from(convo.id())
} }
} }
#[cfg(test)]
mod mock {
use crypto::PrivateKey;
use storage::{
ConversationStore, EphemeralKeyStore, IdentityStore, StorageError,
};
use super::*;
use std::collections::HashMap;
use std::sync::Mutex;
// Simple in-memory implementation of ChatStore for tests.
// Adjust the methods to match the exact trait definition in `crate::store::ChatStore`.
#[derive(Default)]
pub struct MockChatStore {
identity: Option<Identity>,
conversations: Mutex<HashMap<String, ConversationMeta>>,
ephemeral_keys: Mutex<HashMap<String, PrivateKey>>,
}
impl IdentityStore for MockChatStore {
fn load_identity(&self) -> Result<Option<Identity>, StorageError> {
Ok(self.identity.clone())
}
fn save_identity(&mut self, identity: &Identity) -> Result<(), StorageError> {
self.identity = Some(identity.clone());
Ok(())
}
}
impl EphemeralKeyStore for MockChatStore {
fn load_ephemeral_key(&self, key_hex: &str) -> Result<Option<PrivateKey>, StorageError> {
Ok(self.ephemeral_keys.lock().unwrap().get(key_hex).cloned())
}
fn save_ephemeral_key(
&mut self,
key_hex: &str,
private_key: &PrivateKey,
) -> Result<(), StorageError> {
self.ephemeral_keys
.lock()
.unwrap()
.insert(key_hex.to_string(), private_key.clone());
Ok(())
}
fn remove_ephemeral_key(&mut self, key_hex: &str) -> Result<(), StorageError> {
self.ephemeral_keys.lock().unwrap().remove(key_hex);
Ok(())
}
}
impl ConversationStore for MockChatStore {
fn save_conversation(&mut self, meta: &ConversationMeta) -> Result<(), StorageError> {
self.conversations
.lock()
.unwrap()
.insert(meta.local_convo_id.clone(), meta.clone());
Ok(())
}
fn load_conversation(
&self,
local_convo_id: &str,
) -> Result<Option<ConversationMeta>, StorageError> {
Ok(self
.conversations
.lock()
.unwrap()
.get(local_convo_id)
.cloned())
}
fn remove_conversation(&mut self, local_convo_id: &str) -> Result<(), StorageError> {
self.conversations.lock().unwrap().remove(local_convo_id);
Ok(())
}
fn load_conversations(&self) -> Result<Vec<ConversationMeta>, StorageError> {
Ok(self
.conversations
.lock()
.unwrap()
.values()
.cloned()
.collect())
}
fn has_conversation(&self, local_convo_id: &str) -> Result<bool, StorageError> {
Ok(self
.conversations
.lock()
.unwrap()
.contains_key(local_convo_id))
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use chat_sqlite::ChatStorage; use sqlite::ChatStorage;
use storage::ConversationStore; use storage::ConversationStore;
use crate::context::mock::MockChatStore;
use super::*; use super::*;
fn send_and_verify( fn send_and_verify(
sender: &mut Context<MockChatStore>, sender: &mut Context<ChatStorage>,
receiver: &mut Context<MockChatStore>, receiver: &mut Context<ChatStorage>,
convo_id: ConversationId, convo_id: ConversationId,
content: &[u8], content: &[u8],
) { ) {
@ -341,8 +237,8 @@ mod tests {
#[test] #[test]
fn ctx_integration() { fn ctx_integration() {
let mut saro = Context::new_with_name("saro", MockChatStore::default()); let mut saro = Context::new_with_name("saro", ChatStorage::in_memory());
let mut raya = Context::new_with_name("raya", MockChatStore::default()); let mut raya = Context::new_with_name("raya", ChatStorage::in_memory());
// Raya creates intro bundle and sends to Saro // Raya creates intro bundle and sends to Saro
let bundle = raya.create_intro_bundle().unwrap(); let bundle = raya.create_intro_bundle().unwrap();
@ -383,12 +279,12 @@ mod tests {
.to_string(); .to_string();
let config = StorageConfig::File(db_path); let config = StorageConfig::File(db_path);
let ctx1 = Context::open("alice", config.clone(), MockChatStore::default()).unwrap(); let ctx1 = Context::open("alice", config.clone(), ChatStorage::in_memory()).unwrap();
let pubkey1 = ctx1._identity.public_key(); let pubkey1 = ctx1._identity.public_key();
let name1 = ctx1.installation_name().to_string(); let name1 = ctx1.installation_name().to_string();
drop(ctx1); drop(ctx1);
let ctx2 = Context::open("alice", config, MockChatStore::default()).unwrap(); let ctx2 = Context::open("alice", config, ChatStorage::in_memory()).unwrap();
let pubkey2 = ctx2._identity.public_key(); let pubkey2 = ctx2._identity.public_key();
let name2 = ctx2.installation_name().to_string(); let name2 = ctx2.installation_name().to_string();
@ -415,7 +311,7 @@ mod tests {
let mut ctx2 = Context::open("alice", config.clone(), store2).unwrap(); let mut ctx2 = Context::open("alice", config.clone(), store2).unwrap();
let intro = Introduction::try_from(bundle1.as_slice()).unwrap(); let intro = Introduction::try_from(bundle1.as_slice()).unwrap();
let mut bob = Context::new_with_name("bob", MockChatStore::default()); let mut bob = Context::new_with_name("bob", ChatStorage::in_memory());
let (_, payloads) = bob.create_private_convo(&intro, b"hello after restart"); let (_, payloads) = bob.create_private_convo(&intro, b"hello after restart");
let payload = payloads.first().unwrap(); let payload = payloads.first().unwrap();
@ -439,7 +335,7 @@ mod tests {
let store = ChatStorage::new(config.clone()).unwrap(); let store = ChatStorage::new(config.clone()).unwrap();
let mut alice = Context::open("alice", config.clone(), store).unwrap(); let mut alice = Context::open("alice", config.clone(), store).unwrap();
let mut bob = Context::new_with_name("bob", MockChatStore::default()); let mut bob = Context::new_with_name("bob", ChatStorage::in_memory());
let bundle = alice.create_intro_bundle().unwrap(); let bundle = alice.create_intro_bundle().unwrap();
let intro = Introduction::try_from(bundle.as_slice()).unwrap(); let intro = Introduction::try_from(bundle.as_slice()).unwrap();
@ -449,14 +345,14 @@ mod tests {
let content = alice.handle_payload(&payload.data).unwrap().unwrap(); let content = alice.handle_payload(&payload.data).unwrap().unwrap();
assert!(content.is_new_convo); assert!(content.is_new_convo);
let convos = alice.storage.load_conversations().unwrap(); let convos = alice.store.load_conversations().unwrap();
assert_eq!(convos.len(), 1); assert_eq!(convos.len(), 1);
assert_eq!(convos[0].kind.as_str(), "private_v1"); assert_eq!(convos[0].kind.as_str(), "private_v1");
drop(alice); drop(alice);
let store2 = ChatStorage::new(config.clone()).unwrap(); let store2 = ChatStorage::new(config.clone()).unwrap();
let alice2 = Context::open("alice", config, store2).unwrap(); let alice2 = Context::open("alice", config, store2).unwrap();
let convos = alice2.storage.load_conversations().unwrap(); let convos = alice2.store.load_conversations().unwrap();
assert_eq!(convos.len(), 1, "conversation metadata should persist"); assert_eq!(convos.len(), 1, "conversation metadata should persist");
} }
@ -473,7 +369,7 @@ mod tests {
// Alice and Bob establish a conversation // Alice and Bob establish a conversation
let store = ChatStorage::new(config.clone()).unwrap(); let store = ChatStorage::new(config.clone()).unwrap();
let mut alice = Context::open("alice", config.clone(), store).unwrap(); let mut alice = Context::open("alice", config.clone(), store).unwrap();
let mut bob = Context::new_with_name("bob", MockChatStore::default()); let mut bob = Context::new_with_name("bob", ChatStorage::in_memory());
let bundle = alice.create_intro_bundle().unwrap(); let bundle = alice.create_intro_bundle().unwrap();
let intro = Introduction::try_from(bundle.as_slice()).unwrap(); let intro = Introduction::try_from(bundle.as_slice()).unwrap();

View File

@ -10,10 +10,10 @@ use crypto::{PrekeyBundle, SymmetricKey32};
use crate::context::Introduction; use crate::context::Introduction;
use crate::conversation::{ChatError, ConversationId, Convo, Id, PrivateV1Convo}; use crate::conversation::{ChatError, ConversationId, Convo, Id, PrivateV1Convo};
use crate::crypto::{CopyBytes, PrivateKey, PublicKey}; use crate::crypto::{CopyBytes, PrivateKey, PublicKey};
use crypto::Identity;
use crate::inbox::handshake::InboxHandshake; use crate::inbox::handshake::InboxHandshake;
use crate::proto; use crate::proto;
use crate::types::{AddressedEncryptedPayload, ContentData}; use crate::types::{AddressedEncryptedPayload, ContentData};
use crypto::Identity;
/// Compute the deterministic Delivery_address for an installation /// Compute the deterministic Delivery_address for an installation
fn delivery_address_for_installation(_: PublicKey) -> String { fn delivery_address_for_installation(_: PublicKey) -> String {
@ -239,7 +239,7 @@ impl Id for Inbox {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use chat_sqlite::ChatStorage; use sqlite::ChatStorage;
use storage::{EphemeralKeyStore, StorageConfig}; use storage::{EphemeralKeyStore, StorageConfig};
#[test] #[test]

View File

@ -9,9 +9,9 @@ mod types;
mod utils; mod utils;
pub use api::*; pub use api::*;
pub use chat_sqlite::ChatStorage;
pub use context::{Context, Introduction}; pub use context::{Context, Introduction};
pub use errors::ChatError; pub use errors::ChatError;
pub use sqlite::ChatStorage;
pub use storage::StorageConfig; pub use storage::StorageConfig;
#[cfg(test)] #[cfg(test)]

View File

@ -1,5 +1,5 @@
[package] [package]
name = "chat-sqlite" name = "sqlite"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
description = "SQLite storage implementation for libchat" description = "SQLite storage implementation for libchat"

View File

@ -29,6 +29,10 @@ impl ChatStorage {
Self::run_migrations(db) Self::run_migrations(db)
} }
pub fn in_memory() -> Self {
Self::new(StorageConfig::InMemory).unwrap()
}
/// Applies all migrations and returns the storage instance. /// Applies all migrations and returns the storage instance.
fn run_migrations(mut db: SqliteDb) -> Result<Self, StorageError> { fn run_migrations(mut db: SqliteDb) -> Result<Self, StorageError> {
migrations::apply_migrations(db.connection_mut())?; migrations::apply_migrations(db.connection_mut())?;
@ -111,10 +115,7 @@ impl EphemeralKeyStore for ChatStorage {
} }
/// Loads a single ephemeral key by its public key hex. /// Loads a single ephemeral key by its public key hex.
fn load_ephemeral_key( fn load_ephemeral_key(&self, public_key_hex: &str) -> Result<Option<PrivateKey>, StorageError> {
&self,
public_key_hex: &str,
) -> Result<Option<PrivateKey>, StorageError> {
let mut stmt = self let mut stmt = self
.db .db
.connection() .connection()