agentroot_core/db/
context.rs

1//! Context operations
2
3use super::Database;
4use crate::error::Result;
5use rusqlite::params;
6
7/// Context info
8#[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    /// Add context for a path
17    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    /// List all contexts
27    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    /// Check for collections missing context
46    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    /// Remove context for a path
65    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    /// Get context for a document path (hierarchical resolution)
73    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}