1use rusqlite::params;
2
3use crate::types::EventLogEntry;
4use crate::Result;
5
6use super::Store;
7
8impl Store {
9 pub fn write_event(&self, action: &str, key: &str, scope: &str, tokens: i32) -> Result<()> {
15 self.conn().execute(
16 "INSERT INTO event_log (action, key, scope, tokens) VALUES (?1, ?2, ?3, ?4)",
17 params![action, key, scope, tokens],
18 )?;
19 Ok(())
20 }
21
22 pub fn recent_events(&self, limit: i32) -> Result<Vec<EventLogEntry>> {
24 let mut stmt = self.conn().prepare(
25 "SELECT id, action, key, scope, tokens, created_at
26 FROM event_log
27 ORDER BY created_at DESC, id DESC
28 LIMIT ?1",
29 )?;
30 let results = stmt
31 .query_map(params![limit], |row| {
32 Ok(EventLogEntry {
33 id: row.get(0)?,
34 action: row.get(1)?,
35 key: row.get(2)?,
36 scope: row.get(3)?,
37 tokens: row.get(4)?,
38 created_at: row.get(5)?,
39 })
40 })?
41 .collect::<std::result::Result<Vec<_>, _>>()?;
42 Ok(results)
43 }
44
45 pub fn events_today_summary(&self) -> Result<(i64, i64, i64, i64)> {
47 Ok(self.conn().query_row(
48 "SELECT
49 COALESCE(SUM(CASE WHEN action = 'save' THEN 1 ELSE 0 END), 0),
50 COALESCE(SUM(CASE WHEN action = 'inject' THEN 1 ELSE 0 END), 0),
51 COALESCE(SUM(CASE WHEN action = 'search' THEN 1 ELSE 0 END), 0),
52 COALESCE(SUM(CASE WHEN action = 'inject' THEN tokens ELSE 0 END), 0)
53 FROM event_log
54 WHERE date(created_at) = date('now')",
55 [],
56 |row| {
57 Ok((
58 row.get::<_, i64>(0)?,
59 row.get::<_, i64>(1)?,
60 row.get::<_, i64>(2)?,
61 row.get::<_, i64>(3)?,
62 ))
63 },
64 )?)
65 }
66
67 pub fn purge_old_events(&self, days: u32) -> Result<u64> {
69 let deleted = self.conn().execute(
70 "DELETE FROM event_log
71 WHERE created_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-' || ?1 || ' days')",
72 params![days],
73 )?;
74 Ok(deleted as u64)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_write_and_read_events() {
84 let store = Store::open_in_memory().unwrap();
85 store
86 .write_event("save", "arch/decision", "/myproject", 0)
87 .unwrap();
88 store
89 .write_event("inject", "3 memories", "/myproject", 450)
90 .unwrap();
91
92 let events = store.recent_events(10).unwrap();
93 assert_eq!(events.len(), 2);
94 assert_eq!(events[0].action, "inject");
96 assert_eq!(events[0].tokens, 450);
97 assert_eq!(events[1].action, "save");
98 assert_eq!(events[1].key, "arch/decision");
99 }
100
101 #[test]
102 fn test_events_today_summary() {
103 let store = Store::open_in_memory().unwrap();
104 store.write_event("save", "k1", "/", 0).unwrap();
105 store.write_event("save", "k2", "/", 0).unwrap();
106 store.write_event("search", "rust async", "/", 0).unwrap();
107 store.write_event("inject", "2 memories", "/", 300).unwrap();
108
109 let (saves, injections, searches, tokens) = store.events_today_summary().unwrap();
110 assert_eq!(saves, 2);
111 assert_eq!(injections, 1);
112 assert_eq!(searches, 1);
113 assert_eq!(tokens, 300);
114 }
115
116 #[test]
117 fn test_purge_old_events() {
118 let store = Store::open_in_memory().unwrap();
119 store
121 .conn()
122 .execute(
123 "INSERT INTO event_log (action, key, scope, tokens, created_at)
124 VALUES ('save', 'old', '/', 0, '2020-01-01T00:00:00.000Z')",
125 [],
126 )
127 .unwrap();
128 store.write_event("save", "new", "/", 0).unwrap();
129
130 let deleted = store.purge_old_events(30).unwrap();
131 assert_eq!(deleted, 1);
132 let events = store.recent_events(10).unwrap();
133 assert_eq!(events.len(), 1);
134 assert_eq!(events[0].key, "new");
135 }
136}