Skip to main content

max / goingson

11.6 KB · 231 lines History Blame Raw
1 use super::*;
2
3 /// Core task entity persistence: CRUD, listing/filtering, bulk edits,
4 /// lifecycle transitions, and date-range read queries used by reviews.
5 #[async_trait]
6 pub trait TaskCrud: Send + Sync {
7 /// Lists all non-deleted tasks for a user.
8 async fn list_all(&self, user_id: UserId) -> Result<Vec<Task>>;
9
10 /// Lists tasks belonging to a specific project.
11 async fn list_by_project(&self, user_id: UserId, project_id: ProjectId) -> Result<Vec<Task>>;
12
13 /// Lists tasks linked to a specific contact.
14 async fn list_by_contact(&self, user_id: UserId, contact_id: ContactId) -> Result<Vec<Task>>;
15
16 /// Lists tasks matching the given filter criteria with pagination.
17 /// Returns (tasks, total_count) for pagination UI.
18 async fn list_filtered(&self, user_id: UserId, query: TaskFilterQuery) -> Result<(Vec<Task>, i64)>;
19
20 /// Retrieves a task by ID.
21 async fn get_by_id(&self, id: TaskId, user_id: UserId) -> Result<Option<Task>>;
22
23 /// Lightweight fetch of fields needed for update logic (avoids annotation/subtask/session sub-queries).
24 /// Returns (created_at, status, completed_at, scheduled_start, scheduled_duration).
25 async fn get_update_context(&self, id: TaskId, user_id: UserId) -> Result<Option<crate::models::TaskUpdateContext>>;
26
27 /// Creates a new task.
28 async fn create(&self, user_id: UserId, task: NewTask) -> Result<Task>;
29
30 /// Restores a task verbatim from a backup, preserving its original ID,
31 /// `status`, `completed_at`, and `created_at`. Idempotent (`INSERT OR
32 /// IGNORE`). Annotations and subtasks are restored separately by the caller.
33 async fn restore(&self, user_id: UserId, task: &Task) -> Result<()>;
34
35 /// Updates an existing task.
36 async fn update(&self, id: TaskId, user_id: UserId, task: UpdateTask) -> Result<Option<Task>>;
37
38 /// Sets the project for many tasks in one transaction. Returns rows affected.
39 /// Avoids the per-task fetch+update round-trips the bulk UI did before
40 /// (ultra-fuzz Run #27 Perf S4). Project does not affect urgency.
41 async fn bulk_set_project(&self, user_id: UserId, ids: &[TaskId], project_id: Option<ProjectId>) -> Result<usize>;
42
43 /// Sets the priority for many tasks in one transaction, recomputing each task's
44 /// urgency (priority is an urgency input). Returns rows affected.
45 async fn bulk_set_priority(&self, user_id: UserId, ids: &[TaskId], priority: crate::models::Priority) -> Result<usize>;
46
47 /// Soft-deletes a task.
48 async fn delete(&self, id: TaskId, user_id: UserId) -> Result<bool>;
49
50 /// Marks a task as started.
51 async fn start(&self, id: TaskId, user_id: UserId) -> Result<bool>;
52
53 /// Marks a task as completed and returns it.
54 /// The caller is responsible for handling recurrence and milestone auto-completion.
55 async fn complete(&self, id: TaskId, user_id: UserId) -> Result<Option<Task>>;
56
57 /// Atomically completes a task and creates the next recurring instance.
58 /// Returns (completed_task, next_task). If the task has no recurrence,
59 /// next_task is None. The entire operation is wrapped in a transaction
60 /// so a crash cannot break the recurrence chain.
61 async fn complete_recurring(&self, id: TaskId, user_id: UserId, next: Option<NewTask>) -> Result<(Option<Task>, Option<Task>)>;
62
63 /// Counts non-deleted, non-completed tasks in a milestone.
64 async fn count_incomplete_by_milestone(&self, milestone_id: MilestoneId, user_id: UserId) -> Result<i64>;
65
66 /// Lists tasks completed within a date range (for weekly review).
67 async fn list_completed_between(&self, user_id: UserId, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<Task>>;
68
69 /// Lists tasks that became overdue within a date range (for weekly review).
70 async fn list_became_overdue_between(&self, user_id: UserId, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<Task>>;
71
72 /// Lists tasks due within a date range (for weekly review).
73 async fn list_due_between(&self, user_id: UserId, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<Task>>;
74
75 /// Lists tasks created within a date range (for monthly review).
76 async fn list_created_between(&self, user_id: UserId, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<Task>>;
77
78 /// Lists all tasks in a recurrence chain (root + all descendants).
79 async fn list_recurrence_chain(&self, root_id: TaskId, user_id: UserId) -> Result<Vec<Task>>;
80 }
81
82 /// Annotation (note) and subtask persistence for a task.
83 #[async_trait]
84 pub trait TaskAnnotations: Send + Sync {
85 /// Gets all annotations for a task.
86 async fn get_annotations_for_task(&self, task_id: TaskId) -> Result<Vec<Annotation>>;
87
88 /// Adds an annotation (note) to a task.
89 async fn add_annotation(&self, task_id: TaskId, user_id: UserId, note: &str) -> Result<Option<Annotation>>;
90
91 /// Deletes an annotation.
92 async fn delete_annotation(&self, annotation_id: AnnotationId, user_id: UserId) -> Result<bool>;
93
94 /// Gets all subtasks for a task.
95 async fn get_subtasks_for_task(&self, task_id: TaskId) -> Result<Vec<Subtask>>;
96
97 /// Adds a subtask to a task.
98 async fn add_subtask(&self, task_id: TaskId, user_id: UserId, text: &str) -> Result<Option<Subtask>>;
99
100 /// Toggles a subtask's completion status.
101 async fn toggle_subtask(&self, subtask_id: SubtaskId, user_id: UserId) -> Result<Option<Subtask>>;
102
103 /// Updates subtask text.
104 async fn update_subtask(&self, subtask_id: SubtaskId, user_id: UserId, text: &str) -> Result<Option<Subtask>>;
105
106 /// Deletes a subtask.
107 async fn delete_subtask(&self, subtask_id: SubtaskId, user_id: UserId) -> Result<bool>;
108
109 /// Adds a linked task as a subtask.
110 ///
111 /// This creates a subtask that links to another task, enabling multi-phase
112 /// features where Phase 2 is a full task linked as a subtask of Phase 1.
113 /// The linked subtask's completion status syncs with the linked task's status.
114 async fn add_subtask_link(&self, task_id: TaskId, user_id: UserId, linked_task_id: TaskId) -> Result<Option<Subtask>>;
115
116 /// Gets all status tokens on a task, in append order.
117 async fn get_status_tokens_for_task(&self, task_id: TaskId) -> Result<Vec<StatusToken>>;
118
119 /// Records (upserts) a status token on a task, returning it.
120 ///
121 /// The token id is deterministic in `(task_id, kind, reference)`, so recording
122 /// the same token twice is idempotent — a re-record updates its `state` and
123 /// `is_primary` in place rather than duplicating. When `is_primary` is true this
124 /// becomes the task's primary token and any prior primary flag on the task is
125 /// cleared (at most one primary per task). Returns `None` if the task is missing
126 /// or not owned.
127 async fn record_status_token(&self, task_id: TaskId, user_id: UserId, kind: &str, reference: &str, state: TokenState, is_primary: bool) -> Result<Option<StatusToken>>;
128
129 /// Deletes a status token by id, scoped to the user's own tasks. Returns
130 /// whether a row was removed.
131 async fn delete_status_token(&self, token_id: StatusTokenId, user_id: UserId) -> Result<bool>;
132 }
133
134 /// Scheduling and triage state: snooze, waiting, focus, time-block schedule,
135 /// and the date-scoped listings that drive the planner views.
136 #[async_trait]
137 pub trait TaskScheduling: Send + Sync {
138 /// Snoozes a task until the specified time.
139 async fn snooze(&self, id: TaskId, user_id: UserId, until: DateTime<Utc>) -> Result<Option<Task>>;
140
141 /// Removes snooze from a task.
142 async fn unsnooze(&self, id: TaskId, user_id: UserId) -> Result<Option<Task>>;
143
144 /// Lists all currently snoozed tasks.
145 async fn list_snoozed(&self, user_id: UserId) -> Result<Vec<Task>>;
146
147 /// Marks a task as waiting for external response.
148 async fn mark_waiting(&self, id: TaskId, user_id: UserId, expected_response: Option<DateTime<Utc>>) -> Result<Option<Task>>;
149
150 /// Clears the waiting status from a task.
151 async fn clear_waiting(&self, id: TaskId, user_id: UserId) -> Result<Option<Task>>;
152
153 /// Lists all tasks marked as waiting.
154 async fn list_waiting(&self, user_id: UserId) -> Result<Vec<Task>>;
155
156 /// Lists tasks scheduled for a specific date.
157 async fn list_scheduled_for_date(&self, user_id: UserId, date: NaiveDate) -> Result<Vec<Task>>;
158
159 /// Lists unscheduled tasks due on a specific date.
160 async fn list_unscheduled_due_on_date(&self, user_id: UserId, date: NaiveDate) -> Result<Vec<Task>>;
161
162 /// Lists unscheduled tasks whose `due` falls within a UTC instant window
163 /// (inclusive upper bound). Lets callers honor a user-local day boundary
164 /// instead of an implicit UTC one.
165 async fn list_unscheduled_due_between(&self, user_id: UserId, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<Task>>;
166
167 /// Updates a task's time-block schedule.
168 async fn update_schedule(&self, id: TaskId, user_id: UserId, start: Option<DateTime<Utc>>, duration: Option<i32>) -> Result<Option<Task>>;
169
170 /// Sets or clears the focus status on a task.
171 async fn set_focus(&self, id: TaskId, user_id: UserId, is_focus: bool) -> Result<Option<Task>>;
172
173 /// Lists all tasks marked as focus.
174 async fn list_focused(&self, user_id: UserId) -> Result<Vec<Task>>;
175
176 /// Clears focus from all tasks.
177 async fn clear_all_focus(&self, user_id: UserId) -> Result<u64>;
178
179 /// Lists high-priority pending tasks for focus selection.
180 async fn list_available_for_focus(&self, user_id: UserId, limit: i64) -> Result<Vec<Task>>;
181 }
182
183 /// Time-tracking persistence: live timers, manual entries, and summaries.
184 #[async_trait]
185 pub trait TaskTimeTracking: Send + Sync {
186 /// Starts a timer on a task. Fails if any session is already active for the user.
187 async fn start_timer(&self, task_id: TaskId, user_id: UserId) -> Result<TimeSession>;
188
189 /// Stops the active timer on a task, updating duration and actual_minutes cache.
190 async fn stop_timer(&self, task_id: TaskId, user_id: UserId) -> Result<Option<TimeSession>>;
191
192 /// Discards the active timer without updating actual_minutes.
193 async fn discard_timer(&self, task_id: TaskId, user_id: UserId) -> Result<bool>;
194
195 /// Gets the currently active timer for a user (at most one).
196 async fn get_active_timer(&self, user_id: UserId) -> Result<Option<(TimeSession, String)>>;
197
198 /// Lists all time sessions for a task.
199 async fn list_time_sessions(&self, task_id: TaskId, user_id: UserId) -> Result<Vec<TimeSession>>;
200
201 /// Lists every time session for a user across all tasks (for full backup export).
202 async fn list_all_time_sessions(&self, user_id: UserId) -> Result<Vec<TimeSession>>;
203
204 /// Logs a manual time entry (retroactive, no live timer).
205 ///
206 /// `minutes` is a validated [`PositiveMinutes`], so the repository cannot be
207 /// handed a zero/negative duration.
208 async fn log_manual_time(&self, task_id: TaskId, user_id: UserId, minutes: PositiveMinutes, date: DateTime<Utc>) -> Result<TimeSession>;
209
210 /// Gets aggregated time tracking summary grouped by project and date.
211 async fn get_time_summary(&self, user_id: UserId, start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Vec<TimeTrackingSummary>>;
212 }
213
214 /// Aggregate task-persistence contract.
215 ///
216 /// This is the trait consumers hold as `Arc<dyn TaskRepository>`. It carries no
217 /// methods of its own; the surface lives in the four capability sub-traits
218 /// ([`TaskCrud`], [`TaskAnnotations`], [`TaskScheduling`], [`TaskTimeTracking`]).
219 /// The blanket impl below means any type implementing all four automatically
220 /// satisfies `TaskRepository`, so a single trait object still exposes the whole
221 /// API while the definition stays split by concern.
222 pub trait TaskRepository:
223 TaskCrud + TaskAnnotations + TaskScheduling + TaskTimeTracking
224 {
225 }
226
227 impl<T> TaskRepository for T where
228 T: TaskCrud + TaskAnnotations + TaskScheduling + TaskTimeTracking
229 {
230 }
231