//! Example: Chat Session with Automatic Persistence //! //! This example demonstrates using ChatSession which automatically //! persists all state changes to SQLite storage. //! //! Run with: cargo run -p logos-chat --example chat_session use logos_chat::storage::ChatSession; fn main() { println!("=== Chat Session Example ===\n"); // Use a temporary file for this example let alice_db = "/tmp/alice_session.db"; let bob_db = "/tmp/bob_session.db"; // Clean up from previous runs let _ = std::fs::remove_file(alice_db); let _ = std::fs::remove_file(bob_db); // ========================================= // Create sessions for Alice and Bob // ========================================= println!("Step 1: Creating chat sessions...\n"); let mut alice = ChatSession::open_or_create(alice_db, "alice_secret_key") .expect("Failed to create Alice's session"); println!(" Alice's session created"); println!(" Address: {}", truncate(&alice.local_address())); let mut bob = ChatSession::open_or_create(bob_db, "bob_secret_key") .expect("Failed to create Bob's session"); println!(" Bob's session created"); println!(" Address: {}", truncate(&bob.local_address())); println!(); // ========================================= // Bob creates intro bundle // ========================================= println!("Step 2: Bob creates introduction bundle...\n"); let bob_intro = bob .create_intro_bundle() .expect("Failed to create intro bundle"); println!(" Bob's intro bundle created"); println!( " Installation key: {}", truncate(&hex::encode(bob_intro.installation_key.as_bytes())) ); println!(); // ========================================= // Alice starts a chat with Bob // ========================================= println!("Step 3: Alice starts a private chat with Bob...\n"); let (chat_id, envelopes) = alice .start_private_chat(&bob_intro, "Hello Bob! 👋") .expect("Failed to start chat"); println!(" Chat created: {}", chat_id); println!(" Envelopes to deliver: {}", envelopes.len()); println!(" Chat automatically saved to storage!"); println!(); // ========================================= // Verify persistence by checking storage // ========================================= println!("Step 4: Verifying persistence...\n"); // Check Alice's storage directly let chat_record = alice .storage() .load_chat(&chat_id) .expect("Failed to load chat"); if let Some(record) = chat_record { println!(" Chat record found in storage:"); println!(" - ID: {}", record.chat_id); println!(" - Type: {}", record.chat_type); println!(" - Remote: {}", record.remote_address); } println!(); // ========================================= // Alice sends more messages // ========================================= println!("Step 5: Alice sends more messages...\n"); let messages = [ "How are you?", "Are you there?", "Let me know when you're free!", ]; for msg in &messages { let envelopes = alice .send_message(&chat_id, msg.as_bytes()) .expect("Failed to send message"); println!(" Sent: \"{}\" ({} envelope(s))", msg, envelopes.len()); } println!(); // ========================================= // Simulate app restart - reopen session // ========================================= println!("Step 6: Simulating app restart...\n"); drop(alice); // Close Alice's session println!(" Session closed. Reopening...\n"); let alice_restored = ChatSession::open(alice_db, "alice_secret_key").expect("Failed to reopen Alice's session"); println!(" Session restored!"); println!(" Address: {}", truncate(&alice_restored.local_address())); // Note: The chats list will be empty because we haven't implemented // full chat restoration yet (which requires restoring ratchet states) println!(" Chats in memory: {}", alice_restored.list_chats().len()); // But the chat metadata is persisted in storage let stored_chats = alice_restored .storage() .list_chat_ids() .expect("Failed to list chats"); println!(" Chats in storage: {:?}", stored_chats); println!(); println!("=== Chat Session Example Complete ===\n"); println!("Key features demonstrated:"); println!(" ✓ Automatic identity persistence"); println!(" ✓ Automatic chat metadata persistence"); println!(" ✓ Session recovery after restart"); println!(); println!("TODO:"); println!(" - Full inbox key persistence"); println!(" - Ratchet state persistence (integration with double-ratchets storage)"); println!(" - Complete chat restoration on session open"); // Clean up let _ = std::fs::remove_file(alice_db); let _ = std::fs::remove_file(bob_db); } fn truncate(s: &str) -> String { if s.len() > 16 { format!("{}...{}", &s[..8], &s[s.len() - 8..]) } else { s.to_string() } }