Skip to main content

behest_store/memory/
composite.rs

1//! Composite in-memory store that coordinates cascading operations
2//! across all storage domains.
3
4use uuid::Uuid;
5
6use crate::memory::{
7    MemoryArtifactStore, MemoryEmbeddingStore, MemoryExecutionStore, MemorySessionStore,
8};
9use crate::{ArtifactStore, EmbeddingStore, ExecutionStore, SessionStore, StoreResult};
10
11/// A composite in-memory store aggregating all four storage domains
12/// with coordinated operations, including cascading deletes.
13///
14/// Provides direct access to each sub-store through public fields for
15/// fine-grained control, plus `delete_session_cascading()` for atomic
16/// cross-domain cleanup.
17///
18/// # Example
19///
20/// ```rust
21/// use behest_store::memory::MemoryStore;
22///
23/// # async fn example() -> Result<(), behest_core::error::StorageError> {
24/// let store = MemoryStore::new();
25/// // Use store.sessions, store.executions, store.embeddings, store.artifacts
26/// // individually, or use store.delete_session_cascading() for full cleanup.
27/// # Ok(())
28/// # }
29/// ```
30#[derive(Default)]
31pub struct MemoryStore {
32    /// Session and message storage.
33    pub sessions: MemorySessionStore,
34    /// Embedding storage.
35    pub embeddings: MemoryEmbeddingStore,
36    /// Artifact storage.
37    pub artifacts: MemoryArtifactStore,
38    /// Tool execution and usage storage.
39    pub executions: MemoryExecutionStore,
40}
41
42impl MemoryStore {
43    /// Creates a new empty composite memory store with all four sub-stores.
44    #[must_use]
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Deletes a session and all related data across all sub-stores.
50    ///
51    /// This is semantically equivalent to what a real database would do
52    /// with foreign-key `ON DELETE CASCADE`, but across in-memory collections.
53    /// Collects errors from all sub-stores instead of short-circuiting.
54    ///
55    /// # Errors
56    ///
57    /// Returns [`StorageError::BackendError`](behest_core::error::StorageError::BackendError)
58    /// aggregating errors from any sub-stores that failed.
59    pub async fn delete_session_cascading(&self, id: &Uuid) -> StoreResult<()> {
60        // Collect errors from all sub-stores
61        let mut errors: Vec<String> = Vec::new();
62
63        if let Err(e) = self.sessions.delete_session(id).await {
64            errors.push(format!("sessions: {e}"));
65        }
66        if let Err(e) = self.embeddings.delete_by_session(id).await {
67            errors.push(format!("embeddings: {e}"));
68        }
69        if let Err(e) = self.artifacts.delete_by_session(id).await {
70            errors.push(format!("artifacts: {e}"));
71        }
72        if let Err(e) = self.executions.delete_by_session(id).await {
73            errors.push(format!("executions: {e}"));
74        }
75
76        if errors.is_empty() {
77            Ok(())
78        } else {
79            Err(behest_core::error::StorageError::BackendError {
80                backend: "memory".to_owned(),
81                message: format!("cascade delete failed: {}", errors.join("; ")),
82                source: None,
83            })
84        }
85    }
86}