Skip to main content

perspt_store/store/
sessions.rs

1use super::*;
2
3impl SessionStore {
4    /// Create a new session
5    pub fn create_session(&self, session: &SessionRecord) -> Result<()> {
6        self.conn.lock().unwrap().execute(
7            r#"
8            INSERT INTO sessions (session_id, task, working_dir, merkle_root, detected_toolchain, status)
9            VALUES (?, ?, ?, ?, ?, ?)
10            "#,
11            [
12                &session.session_id,
13                &session.task,
14                &session.working_dir,
15                &session.merkle_root.as_ref().map(hex::encode).unwrap_or_default(),
16                &session.detected_toolchain.clone().unwrap_or_default(),
17                &session.status,
18            ],
19        )?;
20        Ok(())
21    }
22
23    /// Get session by ID
24    pub fn get_session(&self, session_id: &str) -> Result<Option<SessionRecord>> {
25        let conn = self.conn.lock().unwrap();
26        let mut stmt = conn.prepare(
27            "SELECT session_id, task, working_dir, merkle_root, detected_toolchain, status FROM sessions \
28             WHERE session_id = ?"
29        )?;
30
31        let mut rows = stmt.query([session_id])?;
32        if let Some(row) = rows.next()? {
33            // merkle_root is stored as BLOB; read directly as Option<Vec<u8>>
34            // to match list_recent_sessions and avoid type mismatch on Blob columns.
35            let merkle_root: Option<Vec<u8>> = row.get(3).ok();
36
37            Ok(Some(SessionRecord {
38                session_id: row.get(0)?,
39                task: row.get(1)?,
40                working_dir: row.get(2)?,
41                merkle_root,
42                detected_toolchain: row.get(4)?,
43                status: row.get(5)?,
44            }))
45        } else {
46            Ok(None)
47        }
48    }
49
50    /// Get the directory for session artifacts (`~/.local/share/perspt/sessions/<id>`)
51    pub fn get_session_dir(&self, session_id: &str) -> Result<PathBuf> {
52        let data_dir = dirs::data_local_dir()
53            .context("Could not find local data directory")?
54            .join("perspt")
55            .join("sessions")
56            .join(session_id);
57        Ok(data_dir)
58    }
59
60    /// Ensure a session directory exists and return the path
61    pub fn create_session_dir(&self, session_id: &str) -> Result<PathBuf> {
62        let dir = self.get_session_dir(session_id)?;
63        if !dir.exists() {
64            std::fs::create_dir_all(&dir).context("Failed to create session directory")?;
65        }
66        Ok(dir)
67    }
68
69    /// List recent sessions (newest first)
70    pub fn list_recent_sessions(&self, limit: usize) -> Result<Vec<SessionRecord>> {
71        self.list_sessions_paginated(limit, 0)
72    }
73
74    /// List sessions with pagination (most recent first).
75    pub fn list_sessions_paginated(
76        &self,
77        limit: usize,
78        offset: usize,
79    ) -> Result<Vec<SessionRecord>> {
80        let conn = self.conn.lock().unwrap();
81        let mut stmt = conn.prepare(
82            "SELECT session_id, task, working_dir, merkle_root, detected_toolchain, status
83             FROM sessions ORDER BY created_at DESC LIMIT ? OFFSET ?",
84        )?;
85
86        let mut rows = stmt.query([limit.to_string(), offset.to_string()])?;
87        let mut records = Vec::new();
88
89        while let Some(row) = rows.next()? {
90            let merkle_root: Option<Vec<u8>> = row.get(3).ok();
91
92            records.push(SessionRecord {
93                session_id: row.get(0)?,
94                task: row.get(1)?,
95                working_dir: row.get(2)?,
96                merkle_root,
97                detected_toolchain: row.get(4)?,
98                status: row.get(5)?,
99            });
100        }
101
102        Ok(records)
103    }
104
105    /// Count total number of sessions.
106    pub fn count_sessions(&self) -> Result<usize> {
107        let conn = self.conn.lock().unwrap();
108        let mut stmt = conn.prepare("SELECT COUNT(*) FROM sessions")?;
109        let mut rows = stmt.query([])?;
110        if let Some(row) = rows.next()? {
111            let count: i64 = row.get(0)?;
112            Ok(count as usize)
113        } else {
114            Ok(0)
115        }
116    }
117
118    /// Update session status
119    pub fn update_session_status(&self, session_id: &str, status: &str) -> Result<()> {
120        self.conn.lock().unwrap().execute(
121            "UPDATE sessions SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE session_id = ?",
122            [status, session_id],
123        )?;
124        Ok(())
125    }
126}