opendev_tools_impl/
update_todo.rs1use std::collections::HashMap;
4use std::sync::{Arc, Mutex};
5
6use opendev_runtime::{TodoManager, parse_status};
7use opendev_tools_core::{BaseTool, ToolContext, ToolResult};
8
9#[derive(Debug)]
11pub struct UpdateTodoTool {
12 manager: Arc<Mutex<TodoManager>>,
13}
14
15impl UpdateTodoTool {
16 pub fn new(manager: Arc<Mutex<TodoManager>>) -> Self {
17 Self { manager }
18 }
19}
20
21#[async_trait::async_trait]
22impl BaseTool for UpdateTodoTool {
23 fn name(&self) -> &str {
24 "update_todo"
25 }
26
27 fn description(&self) -> &str {
28 "Update a todo item's status or title. Supports fuzzy ID matching \
29 (e.g., '3', 'todo-3', 'todo_3', or partial title)."
30 }
31
32 fn parameter_schema(&self) -> serde_json::Value {
33 serde_json::json!({
34 "type": "object",
35 "properties": {
36 "id": {
37 "type": "string",
38 "description": "Todo item ID (e.g., '3', 'todo-3', 'todo_3', or partial title)"
39 },
40 "status": {
41 "type": "string",
42 "description": "New status: pending, in_progress, completed (or aliases: todo, doing, done)"
43 },
44 "title": {
45 "type": "string",
46 "description": "New title for the todo item"
47 }
48 },
49 "required": ["id"]
50 })
51 }
52
53 async fn execute(
54 &self,
55 args: HashMap<String, serde_json::Value>,
56 _ctx: &ToolContext,
57 ) -> ToolResult {
58 let id_str = match args.get("id").and_then(|v| v.as_str()) {
59 Some(s) => s,
60 None => {
61 match args.get("id").and_then(|v| v.as_u64()) {
63 Some(n) => return self.do_update(&n.to_string(), &args),
64 None => return ToolResult::fail("id is required"),
65 }
66 }
67 };
68
69 self.do_update(id_str, &args)
70 }
71}
72
73impl UpdateTodoTool {
74 fn do_update(&self, id_str: &str, args: &HashMap<String, serde_json::Value>) -> ToolResult {
75 let mut mgr = match self.manager.lock() {
76 Ok(m) => m,
77 Err(e) => return ToolResult::fail(format!("Lock error: {e}")),
78 };
79
80 let (id, _) = match mgr.find_todo(id_str) {
81 Some(found) => found,
82 None => return ToolResult::fail(format!("Todo not found: {id_str}")),
83 };
84
85 let mut changed = false;
86
87 if let Some(status_str) = args.get("status").and_then(|v| v.as_str()) {
89 if let Some(status) = parse_status(status_str) {
90 mgr.set_status(id, status);
91 changed = true;
92 } else {
93 return ToolResult::fail(format!(
94 "Unknown status: {status_str}. Use: pending, in_progress, completed (or aliases: todo, doing, done)"
95 ));
96 }
97 }
98
99 if let Some(title) = args.get("title").and_then(|v| v.as_str())
101 && let Some(item) = mgr.todos_mut().get_mut(&id)
102 {
103 item.title = title.to_string();
104 changed = true;
105 }
106
107 if !changed {
108 return ToolResult::fail("No updates provided. Specify status and/or title.");
109 }
110
111 ToolResult::ok(mgr.format_status_sorted())
112 }
113}
114
115#[cfg(test)]
116#[path = "update_todo_tests.rs"]
117mod tests;