track/use_cases/
delete_todo.rs1use crate::db::Database;
2use crate::services::TodoService;
3use crate::utils::Result;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct DeleteTodoOutcome {
8 pub task_index: i64,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct DeleteTodoCompletionView {
14 pub summary: String,
15}
16
17impl DeleteTodoOutcome {
18 pub fn completion_view(&self) -> DeleteTodoCompletionView {
19 DeleteTodoCompletionView {
20 summary: format!("Deleted TODO #{}", self.task_index),
21 }
22 }
23}
24
25#[derive(Debug, Clone)]
27pub enum DeleteTodoStep {
28 Completed(DeleteTodoOutcome),
29 NeedsConfirmation(DeleteTodoPrompt),
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct DeleteTodoPrompt {
35 pub task_index: i64,
36 pub content: String,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct DeleteTodoPromptView {
42 pub prompt: String,
43}
44
45impl DeleteTodoPrompt {
46 pub fn view(&self) -> DeleteTodoPromptView {
47 DeleteTodoPromptView {
48 prompt: format!(
49 "Delete TODO #{}: \"{}\"? [y/N]: ",
50 self.task_index, self.content
51 ),
52 }
53 }
54}
55
56pub struct DeleteTodoUseCase<'a> {
58 db: &'a Database,
59}
60
61impl<'a> DeleteTodoUseCase<'a> {
62 pub fn new(db: &'a Database) -> Self {
63 Self { db }
64 }
65
66 pub fn run(&self, task_id: i64, todo_index: i64, force: bool) -> Result<DeleteTodoStep> {
70 if force {
71 return self
72 .execute(task_id, todo_index)
73 .map(DeleteTodoStep::Completed);
74 }
75
76 let todo = TodoService::new(self.db).get_todo_by_index(task_id, todo_index)?;
77 Ok(DeleteTodoStep::NeedsConfirmation(DeleteTodoPrompt {
78 task_index: todo.task_index,
79 content: todo.content,
80 }))
81 }
82
83 pub fn confirm_and_run(&self, task_id: i64, todo_index: i64) -> Result<DeleteTodoOutcome> {
85 self.execute(task_id, todo_index)
86 }
87
88 pub fn execute(&self, task_id: i64, todo_index: i64) -> Result<DeleteTodoOutcome> {
90 let todo_service = TodoService::new(self.db);
91 let todo = todo_service.get_todo_by_index(task_id, todo_index)?;
92 todo_service.delete_todo(todo.id)?;
93
94 Ok(DeleteTodoOutcome {
95 task_index: todo.task_index,
96 })
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::services::TaskService;
104
105 #[test]
106 fn delete_prompt_view_formats_confirmation() {
107 let prompt = DeleteTodoPrompt {
108 task_index: 2,
109 content: "Ship it".to_string(),
110 };
111 let view = prompt.view();
112 assert_eq!(view.prompt, "Delete TODO #2: \"Ship it\"? [y/N]: ");
113 }
114
115 #[test]
116 fn run_with_force_deletes_immediately() {
117 let db = Database::new_in_memory().unwrap();
118 let task = TaskService::new(&db)
119 .create_task("Task", None, None, None)
120 .unwrap();
121 TodoService::new(&db)
122 .add_todo(task.id, "Remove me", false)
123 .unwrap();
124
125 let step = DeleteTodoUseCase::new(&db).run(task.id, 1, true).unwrap();
126 match step {
127 DeleteTodoStep::Completed(outcome) => assert_eq!(outcome.task_index, 1),
128 DeleteTodoStep::NeedsConfirmation(_) => panic!("expected immediate delete"),
129 }
130
131 assert!(TodoService::new(&db)
132 .list_todos(task.id)
133 .unwrap()
134 .is_empty());
135 }
136
137 #[test]
138 fn run_without_force_returns_prompt() {
139 let db = Database::new_in_memory().unwrap();
140 let task = TaskService::new(&db)
141 .create_task("Task", None, None, None)
142 .unwrap();
143 TodoService::new(&db)
144 .add_todo(task.id, "Keep for now", false)
145 .unwrap();
146
147 let step = DeleteTodoUseCase::new(&db).run(task.id, 1, false).unwrap();
148 match step {
149 DeleteTodoStep::NeedsConfirmation(prompt) => {
150 assert_eq!(prompt.task_index, 1);
151 assert_eq!(prompt.content, "Keep for now");
152 }
153 DeleteTodoStep::Completed(_) => panic!("expected confirmation prompt"),
154 }
155 }
156
157 #[test]
158 fn confirm_and_run_deletes_todo() {
159 let db = Database::new_in_memory().unwrap();
160 let task = TaskService::new(&db)
161 .create_task("Task", None, None, None)
162 .unwrap();
163 TodoService::new(&db)
164 .add_todo(task.id, "Gone", false)
165 .unwrap();
166
167 let outcome = DeleteTodoUseCase::new(&db)
168 .confirm_and_run(task.id, 1)
169 .unwrap();
170 assert_eq!(outcome.completion_view().summary, "Deleted TODO #1");
171 assert!(TodoService::new(&db)
172 .list_todos(task.id)
173 .unwrap()
174 .is_empty());
175 }
176}