agentroot_core/db/
context.rs1use super::Database;
4use crate::error::Result;
5use rusqlite::params;
6
7#[derive(Debug, Clone, serde::Serialize)]
9pub struct ContextInfo {
10 pub path: String,
11 pub context: String,
12 pub created_at: String,
13}
14
15impl Database {
16 pub fn add_context(&self, path: &str, context: &str) -> Result<()> {
18 let now = chrono::Utc::now().to_rfc3339();
19 self.conn.execute(
20 "INSERT OR REPLACE INTO contexts (path, context, created_at) VALUES (?1, ?2, ?3)",
21 params![path, context, now],
22 )?;
23 Ok(())
24 }
25
26 pub fn list_contexts(&self) -> Result<Vec<ContextInfo>> {
28 let mut stmt = self
29 .conn
30 .prepare("SELECT path, context, created_at FROM contexts ORDER BY path")?;
31
32 let results = stmt
33 .query_map([], |row| {
34 Ok(ContextInfo {
35 path: row.get(0)?,
36 context: row.get(1)?,
37 created_at: row.get(2)?,
38 })
39 })?
40 .collect::<std::result::Result<Vec<_>, _>>()?;
41
42 Ok(results)
43 }
44
45 pub fn check_missing_contexts(&self) -> Result<Vec<String>> {
47 let mut stmt = self.conn.prepare(
48 "SELECT c.name FROM collections c
49 WHERE NOT EXISTS (
50 SELECT 1 FROM contexts ctx
51 WHERE ctx.path = 'agentroot://' || c.name || '/'
52 OR ctx.path = '/'
53 )
54 ORDER BY c.name",
55 )?;
56
57 let results = stmt
58 .query_map([], |row| row.get(0))?
59 .collect::<std::result::Result<Vec<_>, _>>()?;
60
61 Ok(results)
62 }
63
64 pub fn remove_context(&self, path: &str) -> Result<bool> {
66 let rows = self
67 .conn
68 .execute("DELETE FROM contexts WHERE path = ?1", params![path])?;
69 Ok(rows > 0)
70 }
71
72 pub fn resolve_context(&self, virtual_path: &str) -> Result<Option<String>> {
74 let mut stmt = self.conn.prepare(
75 "SELECT context FROM contexts
76 WHERE ?1 LIKE path || '%'
77 ORDER BY LENGTH(path) DESC
78 LIMIT 1",
79 )?;
80
81 let result = stmt.query_row(params![virtual_path], |row| row.get(0));
82 match result {
83 Ok(context) => Ok(Some(context)),
84 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
85 Err(e) => Err(e.into()),
86 }
87 }
88}