Skip to main content

store_sqlite/
memory_store.rs

1//! `SqliteMemoryStore`: persistent [`MemoryPort`] backed by SQLite.
2
3use std::sync::Mutex;
4
5use chrono::Utc;
6use rusqlite::{params, Connection};
7use uuid::Uuid;
8
9use substrate_core::memory_port::{MemoryEntry, MemoryPort};
10
11use crate::error::StoreError;
12use crate::schema;
13
14/// Durable memory store with full history in SQLite.
15pub struct SqliteMemoryStore {
16    conn: Mutex<Connection>,
17}
18
19impl SqliteMemoryStore {
20    /// Open (or create) a store at the given file path.
21    pub fn open(path: &str) -> Result<Self, StoreError> {
22        let conn = Connection::open(path)?;
23        schema::init(&conn)?;
24        Ok(Self {
25            conn: Mutex::new(conn),
26        })
27    }
28
29    /// Open a transient in-memory database (useful in tests).
30    pub fn open_in_memory() -> Result<Self, StoreError> {
31        let conn = Connection::open_in_memory()?;
32        schema::init(&conn)?;
33        Ok(Self {
34            conn: Mutex::new(conn),
35        })
36    }
37}
38
39impl MemoryPort for SqliteMemoryStore {
40    type Error = StoreError;
41
42    fn append(&self, key: &str, content: &str) -> Result<Uuid, Self::Error> {
43        let id = Uuid::new_v4();
44        let created_at = Utc::now().timestamp();
45        let conn = self.conn.lock().unwrap();
46        conn.execute(
47            "INSERT INTO memory (id, mem_key, content, created_at) VALUES (?1, ?2, ?3, ?4)",
48            params![id.to_string(), key, content, created_at],
49        )?;
50        Ok(id)
51    }
52
53    fn get(&self, key: &str) -> Result<Option<String>, Self::Error> {
54        let conn = self.conn.lock().unwrap();
55        let mut stmt = conn.prepare(
56            "SELECT content FROM memory WHERE mem_key = ?1 ORDER BY created_at DESC LIMIT 1",
57        )?;
58        let mut rows = stmt.query(params![key])?;
59        if let Some(row) = rows.next()? {
60            Ok(Some(row.get(0)?))
61        } else {
62            Ok(None)
63        }
64    }
65
66    fn recent(&self, limit: usize) -> Result<Vec<MemoryEntry>, Self::Error> {
67        self.history_limited(limit)
68    }
69
70    fn history(&self) -> Result<Vec<MemoryEntry>, Self::Error> {
71        self.history_limited(usize::MAX)
72    }
73}
74
75impl SqliteMemoryStore {
76    fn history_limited(&self, limit: usize) -> Result<Vec<MemoryEntry>, StoreError> {
77        let conn = self.conn.lock().unwrap();
78        let mut stmt = conn.prepare(
79            "SELECT id, mem_key, content, created_at FROM memory ORDER BY created_at DESC LIMIT ?1",
80        )?;
81        let rows = stmt.query_map(params![limit as i64], |row| {
82            Ok(MemoryEntry {
83                id: Uuid::parse_str(&row.get::<_, String>(0)?).unwrap_or_else(|_| Uuid::nil()),
84                key: row.get(1)?,
85                content: row.get(2)?,
86                created_at: row.get(3)?,
87            })
88        })?;
89        rows.collect::<Result<Vec<_>, _>>()
90            .map_err(StoreError::from)
91    }
92}