1use chrono::{DateTime, Utc};
4use rusqlite;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8use uuid::Uuid;
9
10#[derive(Debug, Error)]
12pub enum TodoError {
13 #[error("todo database error: {0}")]
15 Database(String),
16
17 #[error("todo metadata JSON error: {0}")]
19 Json(String),
20
21 #[error("todo item not found: {0}")]
23 NotFound(Uuid),
24
25 #[error("todo dependency would create a cycle: {parent_id} -> {child_id}")]
27 DependencyCycle {
28 parent_id: Uuid,
30 child_id: Uuid,
32 },
33
34 #[error("todo item cannot depend on itself: {0}")]
36 SelfDependency(Uuid),
37}
38
39impl From<rusqlite::Error> for TodoError {
40 fn from(err: rusqlite::Error) -> Self {
41 TodoError::Database(err.to_string())
42 }
43}
44
45impl From<serde_json::Error> for TodoError {
46 fn from(err: serde_json::Error) -> Self {
47 TodoError::Json(err.to_string())
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
53#[serde(rename_all = "snake_case")]
54pub enum TodoStatus {
55 Todo,
57 InProgress,
59 Completed,
61 Blocked,
63}
64
65impl TodoStatus {
66 #[must_use]
68 pub fn as_str(self) -> &'static str {
69 match self {
70 TodoStatus::Todo => "todo",
71 TodoStatus::InProgress => "in_progress",
72 TodoStatus::Completed => "completed",
73 TodoStatus::Blocked => "blocked",
74 }
75 }
76
77 pub(super) fn from_str(value: &str) -> Self {
78 match value {
79 "in_progress" => TodoStatus::InProgress,
80 "completed" => TodoStatus::Completed,
81 "blocked" => TodoStatus::Blocked,
82 _ => TodoStatus::Todo,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
89#[serde(rename_all = "snake_case")]
90pub enum TodoPriority {
91 Low,
93 Medium,
95 High,
97 Critical,
99}
100
101impl TodoPriority {
102 #[must_use]
104 pub fn as_str(self) -> &'static str {
105 match self {
106 TodoPriority::Low => "low",
107 TodoPriority::Medium => "medium",
108 TodoPriority::High => "high",
109 TodoPriority::Critical => "critical",
110 }
111 }
112
113 pub(super) fn from_str(value: &str) -> Self {
114 match value {
115 "low" => TodoPriority::Low,
116 "high" => TodoPriority::High,
117 "critical" => TodoPriority::Critical,
118 _ => TodoPriority::Medium,
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125pub struct TodoItem {
126 pub id: Uuid,
128 pub session_id: Uuid,
130 pub title: String,
132 pub description: Option<String>,
134 pub status: TodoStatus,
136 pub priority: TodoPriority,
138 pub created_at: DateTime<Utc>,
140 pub completed_at: Option<DateTime<Utc>>,
142 pub assigned_to_turn: Option<String>,
144 pub tags: Vec<String>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
150pub struct TodoDependency {
151 pub session_id: Uuid,
153 pub parent_id: Uuid,
155 pub child_id: Uuid,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct CreateTodo {
162 pub session_id: Uuid,
164 pub title: String,
166 pub description: Option<String>,
168 pub priority: TodoPriority,
170 pub assigned_to_turn: Option<String>,
172 pub tags: Vec<String>,
174}
175
176#[derive(Debug, Clone, Default, Serialize, Deserialize)]
178pub struct TodoUpdate {
179 pub title: Option<String>,
181 pub description: Option<Option<String>>,
183 pub priority: Option<TodoPriority>,
185 pub assigned_to_turn: Option<Option<String>>,
187 pub tags: Option<Vec<String>>,
189}
190
191#[derive(Debug, Clone, Default, Serialize, Deserialize)]
193pub struct TodoQuery {
194 pub status: Option<TodoStatus>,
196 pub priority: Option<TodoPriority>,
198 pub tag: Option<String>,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
207pub struct TodoCreateInput {
208 pub title: String,
210 #[serde(default)]
212 pub description: Option<String>,
213 #[serde(default = "default_priority")]
215 pub priority: TodoPriority,
216 #[serde(default)]
218 pub assigned_to_turn: Option<String>,
219 #[serde(default)]
221 pub tags: Vec<String>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
226pub struct TodoUpdateStatusInput {
227 pub id: String,
229 pub status: TodoStatus,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
235pub struct TodoUpdateInput {
236 pub id: String,
238 #[serde(default)]
240 pub title: Option<String>,
241 #[serde(default)]
243 pub description: Option<String>,
244 #[serde(default)]
246 pub clear_description: bool,
247 #[serde(default)]
249 pub priority: Option<TodoPriority>,
250 #[serde(default)]
252 pub assigned_to_turn: Option<String>,
253 #[serde(default)]
255 pub clear_assigned_to_turn: bool,
256 #[serde(default)]
258 pub tags: Option<Vec<String>>,
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
263pub struct TodoDeleteInput {
264 pub id: String,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
270pub struct TodoDependencyInput {
271 pub parent_id: String,
273 pub child_id: String,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
279pub struct TodoQueryInput {
280 #[serde(default)]
282 pub status: Option<TodoStatus>,
283 #[serde(default)]
285 pub priority: Option<TodoPriority>,
286 #[serde(default)]
288 pub tag: Option<String>,
289}
290
291fn default_priority() -> TodoPriority {
292 TodoPriority::Medium
293}