intent_engine/
workspace.rs1use crate::db::models::Task;
2use crate::error::{IntentError, Result};
3use serde::Serialize;
4use sqlx::SqlitePool;
5
6#[derive(Debug, Serialize)]
7pub struct CurrentTaskResponse {
8 pub current_task_id: Option<i64>,
9 #[serde(skip_serializing_if = "Option::is_none")]
10 pub task: Option<Task>,
11}
12
13pub struct WorkspaceManager<'a> {
14 pool: &'a SqlitePool,
15}
16
17impl<'a> WorkspaceManager<'a> {
18 pub fn new(pool: &'a SqlitePool) -> Self {
19 Self { pool }
20 }
21
22 pub async fn get_current_task(&self) -> Result<CurrentTaskResponse> {
24 let current_task_id: Option<String> =
25 sqlx::query_scalar("SELECT value FROM workspace_state WHERE key = 'current_task_id'")
26 .fetch_optional(self.pool)
27 .await?;
28
29 let current_task_id = current_task_id.and_then(|id| id.parse::<i64>().ok());
30
31 let task = if let Some(id) = current_task_id {
32 sqlx::query_as::<_, Task>(
33 r#"
34 SELECT id, parent_id, name, spec, status, complexity, priority, first_todo_at, first_doing_at, first_done_at
35 FROM tasks
36 WHERE id = ?
37 "#,
38 )
39 .bind(id)
40 .fetch_optional(self.pool)
41 .await?
42 } else {
43 None
44 };
45
46 Ok(CurrentTaskResponse {
47 current_task_id,
48 task,
49 })
50 }
51
52 pub async fn set_current_task(&self, task_id: i64) -> Result<CurrentTaskResponse> {
54 let task_exists: bool =
56 sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM tasks WHERE id = ?)")
57 .bind(task_id)
58 .fetch_one(self.pool)
59 .await?;
60
61 if !task_exists {
62 return Err(IntentError::TaskNotFound(task_id));
63 }
64
65 sqlx::query(
67 r#"
68 INSERT OR REPLACE INTO workspace_state (key, value)
69 VALUES ('current_task_id', ?)
70 "#,
71 )
72 .bind(task_id.to_string())
73 .execute(self.pool)
74 .await?;
75
76 self.get_current_task().await
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::tasks::TaskManager;
84 use crate::test_utils::test_helpers::TestContext;
85
86 #[tokio::test]
87 async fn test_get_current_task_none() {
88 let ctx = TestContext::new().await;
89 let workspace_mgr = WorkspaceManager::new(ctx.pool());
90
91 let response = workspace_mgr.get_current_task().await.unwrap();
92
93 assert!(response.current_task_id.is_none());
94 assert!(response.task.is_none());
95 }
96
97 #[tokio::test]
98 async fn test_set_current_task() {
99 let ctx = TestContext::new().await;
100 let task_mgr = TaskManager::new(ctx.pool());
101 let workspace_mgr = WorkspaceManager::new(ctx.pool());
102
103 let task = task_mgr.add_task("Test task", None, None).await.unwrap();
104
105 let response = workspace_mgr.set_current_task(task.id).await.unwrap();
106
107 assert_eq!(response.current_task_id, Some(task.id));
108 assert!(response.task.is_some());
109 assert_eq!(response.task.unwrap().id, task.id);
110 }
111
112 #[tokio::test]
113 async fn test_set_current_task_nonexistent() {
114 let ctx = TestContext::new().await;
115 let workspace_mgr = WorkspaceManager::new(ctx.pool());
116
117 let result = workspace_mgr.set_current_task(999).await;
118 assert!(matches!(result, Err(IntentError::TaskNotFound(999))));
119 }
120
121 #[tokio::test]
122 async fn test_update_current_task() {
123 let ctx = TestContext::new().await;
124 let task_mgr = TaskManager::new(ctx.pool());
125 let workspace_mgr = WorkspaceManager::new(ctx.pool());
126
127 let task1 = task_mgr.add_task("Task 1", None, None).await.unwrap();
128 let task2 = task_mgr.add_task("Task 2", None, None).await.unwrap();
129
130 workspace_mgr.set_current_task(task1.id).await.unwrap();
132
133 let response = workspace_mgr.set_current_task(task2.id).await.unwrap();
135
136 assert_eq!(response.current_task_id, Some(task2.id));
137 assert_eq!(response.task.unwrap().id, task2.id);
138 }
139
140 #[tokio::test]
141 async fn test_get_current_task_after_set() {
142 let ctx = TestContext::new().await;
143 let task_mgr = TaskManager::new(ctx.pool());
144 let workspace_mgr = WorkspaceManager::new(ctx.pool());
145
146 let task = task_mgr.add_task("Test task", None, None).await.unwrap();
147 workspace_mgr.set_current_task(task.id).await.unwrap();
148
149 let response = workspace_mgr.get_current_task().await.unwrap();
150
151 assert_eq!(response.current_task_id, Some(task.id));
152 assert!(response.task.is_some());
153 }
154}