1use anyhow::Result;
4use chrono::{DateTime, Utc};
5use rusqlite::{Connection, OpenFlags};
6use std::path::{Path, PathBuf};
7use std::sync::Mutex;
8
9pub mod events;
10pub mod schema;
11pub mod sessions;
12pub mod transcripts;
13
14pub struct Database {
16 path: PathBuf,
17 conn: Mutex<Option<Connection>>,
18}
19
20impl Database {
21 pub fn new(project_root: &Path) -> Self {
22 let path = project_root.join(".scud").join("scud.db");
23 Self {
24 path,
25 conn: Mutex::new(None),
26 }
27 }
28
29 pub fn connection(&self) -> Result<std::sync::MutexGuard<'_, Option<Connection>>> {
31 let mut guard = self.conn.lock().unwrap();
32 if guard.is_none() {
33 let conn = Connection::open_with_flags(
34 &self.path,
35 OpenFlags::SQLITE_OPEN_READ_WRITE
36 | OpenFlags::SQLITE_OPEN_CREATE
37 | OpenFlags::SQLITE_OPEN_FULL_MUTEX,
38 )?;
39 conn.execute_batch("PRAGMA journal_mode=WAL;")?;
42 *guard = Some(conn);
43 }
44 Ok(guard)
45 }
46
47 pub fn initialize(&self) -> Result<()> {
49 let guard = self.connection()?;
50 let conn = guard.as_ref().unwrap();
51 schema::create_tables(conn)?;
52 Ok(())
53 }
54
55 pub fn path(&self) -> &Path {
56 &self.path
57 }
58
59 pub fn get_events_for_session_limited(
61 &self,
62 session_id: &str,
63 limit: Option<usize>,
64 since: Option<DateTime<Utc>>,
65 ) -> Result<Vec<crate::commands::swarm::events::AgentEvent>> {
66 let guard = self.connection()?;
67 let conn = guard.as_ref().unwrap();
68 events::get_events_for_session_limited(conn, session_id, limit, since)
69 }
70}