chore: shared memory db with name

This commit is contained in:
kaichaosun 2026-02-05 16:25:28 +08:00
parent 4f6705603d
commit 75fd6acda9
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
3 changed files with 43 additions and 27 deletions

View File

@ -1,26 +1,19 @@
//! Example: Ping-Pong Chat
//!
//! This example demonstrates a back-and-forth conversation between two users
//! using temporary file storage.
//! using in-memory storage.
//!
//! Run with: cargo run -p logos-chat --example ping_pong
use logos_chat::{ChatManager, StorageConfig};
use tempfile::tempdir;
use logos_chat::ChatManager;
fn main() {
println!("=== Ping-Pong Chat Example ===\n");
// Create temporary directories for storage
let dir = tempdir().expect("Failed to create temp dir");
let alice_db = dir.path().join("alice.db");
let bob_db = dir.path().join("bob.db");
// Create two chat participants with file-based storage
let mut alice = ChatManager::open(StorageConfig::File(alice_db.to_str().unwrap().to_string()))
.expect("Failed to create Alice's chat manager");
let mut bob = ChatManager::open(StorageConfig::File(bob_db.to_str().unwrap().to_string()))
.expect("Failed to create Bob's chat manager");
// Create two chat participants with in-memory storage
// Each ChatManager has its own shared in-memory SQLite database
let mut alice = ChatManager::in_memory("alice").expect("Failed to create Alice's chat manager");
let mut bob = ChatManager::in_memory("bob").expect("Failed to create Bob's chat manager");
println!("Created participants:");
println!(" Alice: {}", alice.local_address());

View File

@ -39,6 +39,7 @@ pub enum ChatManagerError {
///
/// It manages identity, inbox, and chats with all state persisted to SQLite.
/// Chats are loaded from storage on each operation - no in-memory caching.
/// Uses a single shared database for both chat metadata and ratchet state.
///
/// # Example
///
@ -68,6 +69,8 @@ pub struct ChatManager {
/// Storage for chat metadata (identity, inbox keys, chat records).
storage: ChatStorage,
/// Storage config for creating ratchet storage instances.
/// For file/encrypted databases, SQLite handles connection efficiently.
/// For in-memory testing, use SharedInMemory to share data.
storage_config: StorageConfig,
}
@ -103,8 +106,14 @@ impl ChatManager {
}
/// Creates a new in-memory ChatManager (for testing).
pub fn in_memory() -> Result<Self, ChatManagerError> {
Self::open(StorageConfig::InMemory)
///
/// Uses a shared in-memory SQLite database so that multiple storage
/// instances within the same ChatManager share data.
///
/// The `db_name` should be unique per ChatManager instance to avoid
/// sharing data between different users.
pub fn in_memory(db_name: &str) -> Result<Self, ChatManagerError> {
Self::open(StorageConfig::SharedInMemory(db_name.to_string()))
}
/// Creates a new RatchetStorage instance using the stored config.
@ -334,13 +343,13 @@ mod tests {
#[test]
fn test_create_chat_manager() {
let manager = ChatManager::in_memory().unwrap();
let manager = ChatManager::in_memory("test1").unwrap();
assert!(!manager.local_address().is_empty());
}
#[test]
fn test_identity_persistence() {
let manager = ChatManager::in_memory().unwrap();
let manager = ChatManager::in_memory("test2").unwrap();
let address = manager.local_address();
// Identity should be persisted
@ -351,15 +360,15 @@ mod tests {
#[test]
fn test_create_intro_bundle() {
let mut manager = ChatManager::in_memory().unwrap();
let mut manager = ChatManager::in_memory("test3").unwrap();
let bundle = manager.create_intro_bundle();
assert!(bundle.is_ok());
}
#[test]
fn test_start_private_chat() {
let mut alice = ChatManager::in_memory().unwrap();
let mut bob = ChatManager::in_memory().unwrap();
let mut alice = ChatManager::in_memory("alice1").unwrap();
let mut bob = ChatManager::in_memory("bob1").unwrap();
// Bob creates an intro bundle
let bob_intro = bob.create_intro_bundle().unwrap();
@ -379,7 +388,7 @@ mod tests {
#[test]
fn test_inbox_key_persistence() {
let mut manager = ChatManager::in_memory().unwrap();
let mut manager = ChatManager::in_memory("test4").unwrap();
// Create intro bundle (should persist ephemeral key)
let intro = manager.create_intro_bundle().unwrap();
@ -392,8 +401,8 @@ mod tests {
#[test]
fn test_chat_exists() {
let mut alice = ChatManager::in_memory().unwrap();
let mut bob = ChatManager::in_memory().unwrap();
let mut alice = ChatManager::in_memory("alice2").unwrap();
let mut bob = ChatManager::in_memory("bob2").unwrap();
let bob_intro = bob.create_intro_bundle().unwrap();
let (chat_id, _) = alice.start_private_chat(&bob_intro, "Hello!").unwrap();
@ -405,8 +414,8 @@ mod tests {
#[test]
fn test_delete_chat() {
let mut alice = ChatManager::in_memory().unwrap();
let mut bob = ChatManager::in_memory().unwrap();
let mut alice = ChatManager::in_memory("alice3").unwrap();
let mut bob = ChatManager::in_memory("bob3").unwrap();
let bob_intro = bob.create_intro_bundle().unwrap();
let (chat_id, _) = alice.start_private_chat(&bob_intro, "Hello!").unwrap();
@ -427,7 +436,7 @@ mod tests {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let mut bob = ChatManager::in_memory().unwrap();
let mut bob = ChatManager::in_memory("bob4").unwrap();
let bob_intro = bob.create_intro_bundle().unwrap();
let chat_id;

View File

@ -8,8 +8,11 @@ use crate::StorageError;
/// Configuration for SQLite storage.
#[derive(Debug, Clone)]
pub enum StorageConfig {
/// In-memory database (for testing).
/// In-memory database (isolated, for simple testing).
InMemory,
/// Shared in-memory database with a name (multiple connections share data).
/// Use this when you need multiple storage instances to share the same in-memory DB.
SharedInMemory(String),
/// File-based SQLite database.
File(String),
/// SQLCipher encrypted database.
@ -29,6 +32,17 @@ impl SqliteDb {
pub fn new(config: StorageConfig) -> Result<Self, StorageError> {
let conn = match config {
StorageConfig::InMemory => Connection::open_in_memory()?,
StorageConfig::SharedInMemory(ref name) => {
// Use URI mode to create a shared in-memory database
// Multiple connections with the same name share the same data
let uri = format!("file:{}?mode=memory&cache=shared", name);
Connection::open_with_flags(
&uri,
rusqlite::OpenFlags::SQLITE_OPEN_URI
| rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE
| rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
)?
}
StorageConfig::File(ref path) => Connection::open(path)?,
StorageConfig::Encrypted { ref path, ref key } => {
let conn = Connection::open(path)?;