mojentic/llm/tools/ephemeral_task_manager/
task_list.rs1use super::task::{Task, TaskStatus};
2use crate::error::{MojenticError, Result};
3
4#[derive(Debug, Clone)]
9pub struct TaskList {
10 tasks: Vec<Task>,
11 next_id: usize,
12}
13
14impl TaskList {
15 pub fn new() -> Self {
17 Self {
18 tasks: Vec::new(),
19 next_id: 1,
20 }
21 }
22
23 fn claim_next_id(&mut self) -> usize {
25 let id = self.next_id;
26 self.next_id += 1;
27 id
28 }
29
30 pub fn append_task(&mut self, description: String) -> Task {
34 let id = self.claim_next_id();
35 let task = Task::new(id, description);
36 self.tasks.push(task.clone());
37 task
38 }
39
40 pub fn prepend_task(&mut self, description: String) -> Task {
44 let id = self.claim_next_id();
45 let task = Task::new(id, description);
46 self.tasks.insert(0, task.clone());
47 task
48 }
49
50 pub fn insert_task_after(
54 &mut self,
55 existing_task_id: usize,
56 description: String,
57 ) -> Result<Task> {
58 let position =
59 self.tasks.iter().position(|t| t.id == existing_task_id).ok_or_else(|| {
60 MojenticError::ToolError(format!("No task with ID '{}' exists", existing_task_id))
61 })?;
62
63 let id = self.claim_next_id();
64 let task = Task::new(id, description);
65 self.tasks.insert(position + 1, task.clone());
66 Ok(task)
67 }
68
69 pub fn start_task(&mut self, task_id: usize) -> Result<Task> {
73 let task = self.tasks.iter_mut().find(|t| t.id == task_id).ok_or_else(|| {
74 MojenticError::ToolError(format!("No task with ID '{}' exists", task_id))
75 })?;
76
77 if task.status != TaskStatus::Pending {
78 return Err(MojenticError::ToolError(format!(
79 "Task '{}' cannot be started because it is not in PENDING status",
80 task_id
81 )));
82 }
83
84 task.status = TaskStatus::InProgress;
85 Ok(task.clone())
86 }
87
88 pub fn complete_task(&mut self, task_id: usize) -> Result<Task> {
92 let task = self.tasks.iter_mut().find(|t| t.id == task_id).ok_or_else(|| {
93 MojenticError::ToolError(format!("No task with ID '{}' exists", task_id))
94 })?;
95
96 if task.status != TaskStatus::InProgress {
97 return Err(MojenticError::ToolError(format!(
98 "Task '{}' cannot be completed because it is not in IN_PROGRESS status",
99 task_id
100 )));
101 }
102
103 task.status = TaskStatus::Completed;
104 Ok(task.clone())
105 }
106
107 pub fn list_tasks(&self) -> Vec<Task> {
109 self.tasks.clone()
110 }
111
112 pub fn clear_tasks(&mut self) -> usize {
116 let count = self.tasks.len();
117 self.tasks.clear();
118 count
119 }
120}
121
122impl Default for TaskList {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_new_task_list() {
134 let task_list = TaskList::new();
135 assert_eq!(task_list.list_tasks().len(), 0);
136 }
137
138 #[test]
139 fn test_append_task() {
140 let mut task_list = TaskList::new();
141 let task = task_list.append_task("Test task".to_string());
142
143 assert_eq!(task.id, 1);
144 assert_eq!(task.description, "Test task");
145 assert_eq!(task.status, TaskStatus::Pending);
146
147 let tasks = task_list.list_tasks();
148 assert_eq!(tasks.len(), 1);
149 assert_eq!(tasks[0].id, task.id);
150 }
151
152 #[test]
153 fn test_prepend_task() {
154 let mut task_list = TaskList::new();
155 task_list.append_task("Existing task".to_string());
156 let task = task_list.prepend_task("New task".to_string());
157
158 let tasks = task_list.list_tasks();
159 assert_eq!(tasks.len(), 2);
160 assert_eq!(tasks[0].id, task.id);
161 assert_eq!(tasks[0].description, "New task");
162 }
163
164 #[test]
165 fn test_insert_task_after() {
166 let mut task_list = TaskList::new();
167 let task1 = task_list.append_task("Task 1".to_string());
168 task_list.append_task("Task 3".to_string());
169
170 let task2 = task_list.insert_task_after(task1.id, "Task 2".to_string()).unwrap();
171
172 let tasks = task_list.list_tasks();
173 assert_eq!(tasks.len(), 3);
174 assert_eq!(tasks[1].id, task2.id);
175 assert_eq!(tasks[1].description, "Task 2");
176 }
177
178 #[test]
179 fn test_insert_task_after_nonexistent() {
180 let mut task_list = TaskList::new();
181 let result = task_list.insert_task_after(999, "Task".to_string());
182 assert!(result.is_err());
183 }
184
185 #[test]
186 fn test_start_task() {
187 let mut task_list = TaskList::new();
188 let task = task_list.append_task("Task 1".to_string());
189
190 let started = task_list.start_task(task.id).unwrap();
191 assert_eq!(started.status, TaskStatus::InProgress);
192 }
193
194 #[test]
195 fn test_start_non_pending_task() {
196 let mut task_list = TaskList::new();
197 let task = task_list.append_task("Task 1".to_string());
198 task_list.start_task(task.id).unwrap();
199
200 let result = task_list.start_task(task.id);
201 assert!(result.is_err());
202 }
203
204 #[test]
205 fn test_complete_task() {
206 let mut task_list = TaskList::new();
207 let task = task_list.append_task("Task 1".to_string());
208 task_list.start_task(task.id).unwrap();
209
210 let completed = task_list.complete_task(task.id).unwrap();
211 assert_eq!(completed.status, TaskStatus::Completed);
212 }
213
214 #[test]
215 fn test_complete_non_in_progress_task() {
216 let mut task_list = TaskList::new();
217 let task = task_list.append_task("Task 1".to_string());
218
219 let result = task_list.complete_task(task.id);
220 assert!(result.is_err());
221 }
222
223 #[test]
224 fn test_clear_tasks() {
225 let mut task_list = TaskList::new();
226 task_list.append_task("Task 1".to_string());
227 task_list.append_task("Task 2".to_string());
228
229 let count = task_list.clear_tasks();
230 assert_eq!(count, 2);
231 assert_eq!(task_list.list_tasks().len(), 0);
232 }
233
234 #[test]
235 fn test_maintain_task_ids() {
236 let mut task_list = TaskList::new();
237 let task1 = task_list.append_task("Task 1".to_string());
238 let task2 = task_list.append_task("Task 2".to_string());
239
240 task_list.start_task(task1.id).unwrap();
241 task_list.complete_task(task1.id).unwrap();
242
243 let task3 = task_list.append_task("Task 3".to_string());
244
245 let tasks = task_list.list_tasks();
246 assert_eq!(tasks.len(), 3);
247 assert!(tasks.iter().any(|t| t.id == task1.id && t.status == TaskStatus::Completed));
248 assert!(tasks.iter().any(|t| t.id == task2.id && t.status == TaskStatus::Pending));
249 assert!(tasks.iter().any(|t| t.id == task3.id && t.status == TaskStatus::Pending));
250 }
251}