Skip to main content

zeph_memory/store/
history.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use super::SqliteStore;
5use crate::error::MemoryError;
6#[allow(unused_imports)]
7use zeph_db::sql;
8
9impl SqliteStore {
10    /// Load the most recent input history entries, ordered chronologically.
11    ///
12    /// # Errors
13    ///
14    /// Returns an error if the query fails.
15    pub async fn load_input_history(&self, limit: i64) -> Result<Vec<String>, MemoryError> {
16        let rows: Vec<(String,)> = zeph_db::query_as(sql!(
17            "SELECT input FROM input_history ORDER BY id ASC LIMIT ?"
18        ))
19        .bind(limit)
20        .fetch_all(&self.pool)
21        .await?;
22        Ok(rows.into_iter().map(|(s,)| s).collect())
23    }
24
25    /// Persist a new input history entry.
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if the insert fails.
30    pub async fn save_input_entry(&self, text: &str) -> Result<(), MemoryError> {
31        zeph_db::query(sql!("INSERT INTO input_history (input) VALUES (?)"))
32            .bind(text)
33            .execute(&self.pool)
34            .await?;
35        Ok(())
36    }
37
38    /// Delete all input history entries.
39    ///
40    /// # Errors
41    ///
42    /// Returns an error if the delete fails.
43    pub async fn clear_input_history(&self) -> Result<(), MemoryError> {
44        zeph_db::query(sql!("DELETE FROM input_history"))
45            .execute(&self.pool)
46            .await?;
47        Ok(())
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    async fn test_store() -> SqliteStore {
56        SqliteStore::new(":memory:").await.unwrap()
57    }
58
59    #[tokio::test]
60    async fn load_input_history_empty() {
61        let store = test_store().await;
62        let entries = store.load_input_history(100).await.unwrap();
63        assert!(entries.is_empty());
64    }
65
66    #[tokio::test]
67    async fn save_and_load_input_history() {
68        let store = test_store().await;
69        store.save_input_entry("hello").await.unwrap();
70        store.save_input_entry("world").await.unwrap();
71        let entries = store.load_input_history(100).await.unwrap();
72        assert_eq!(entries, vec!["hello", "world"]);
73    }
74
75    #[tokio::test]
76    async fn load_input_history_respects_limit() {
77        let store = test_store().await;
78        for i in 0..10 {
79            store.save_input_entry(&format!("entry {i}")).await.unwrap();
80        }
81        let entries = store.load_input_history(3).await.unwrap();
82        assert_eq!(entries.len(), 3);
83        assert_eq!(entries[0], "entry 0");
84    }
85
86    #[tokio::test]
87    async fn clear_input_history_removes_all() {
88        let store = test_store().await;
89        store.save_input_entry("a").await.unwrap();
90        store.save_input_entry("b").await.unwrap();
91        store.clear_input_history().await.unwrap();
92        let entries = store.load_input_history(100).await.unwrap();
93        assert!(entries.is_empty());
94    }
95}