use super::{ Annotation, AnnotationId, ContactId, DateTime, MilestoneId, NaiveDate, NewTask, PositiveMinutes, ProjectId, Result, StatusToken, StatusTokenId, Subtask, SubtaskId, Task, TaskFilterQuery, TaskId, TimeSession, TimeTrackingSummary, TokenState, UpdateTask, UserId, Utc, async_trait, }; /// Core task entity persistence: CRUD, listing/filtering, bulk edits, /// lifecycle transitions, and date-range read queries used by reviews. #[async_trait] pub trait TaskCrud: Send + Sync { /// Lists all non-deleted tasks for a user. async fn list_all(&self, user_id: UserId) -> Result>; /// Lists tasks belonging to a specific project. async fn list_by_project(&self, user_id: UserId, project_id: ProjectId) -> Result>; /// Lists tasks linked to a specific contact. async fn list_by_contact(&self, user_id: UserId, contact_id: ContactId) -> Result>; /// Lists tasks matching the given filter criteria with pagination. /// Returns (tasks, total_count) for pagination UI. async fn list_filtered( &self, user_id: UserId, query: TaskFilterQuery, ) -> Result<(Vec, i64)>; /// Retrieves a task by ID. async fn get_by_id(&self, id: TaskId, user_id: UserId) -> Result>; /// Lightweight fetch of fields needed for update logic (avoids annotation/subtask/session sub-queries). /// Returns (created_at, status, completed_at, scheduled_start, scheduled_duration). async fn get_update_context( &self, id: TaskId, user_id: UserId, ) -> Result>; /// Creates a new task. async fn create(&self, user_id: UserId, task: NewTask) -> Result; /// Restores a task verbatim from a backup, preserving its original ID, /// `status`, `completed_at`, and `created_at`. Idempotent (`INSERT OR /// IGNORE`). Annotations and subtasks are restored separately by the caller. async fn restore(&self, user_id: UserId, task: &Task) -> Result<()>; /// Updates an existing task. async fn update(&self, id: TaskId, user_id: UserId, task: UpdateTask) -> Result>; /// Sets the project for many tasks in one transaction. Returns rows affected. /// Avoids the per-task fetch+update round-trips the bulk UI did before /// (ultra-fuzz Run #27 Perf S4). Project does not affect urgency. async fn bulk_set_project( &self, user_id: UserId, ids: &[TaskId], project_id: Option, ) -> Result; /// Sets the priority for many tasks in one transaction, recomputing each task's /// urgency (priority is an urgency input). Returns rows affected. async fn bulk_set_priority( &self, user_id: UserId, ids: &[TaskId], priority: crate::models::Priority, ) -> Result; /// Soft-deletes a task. async fn delete(&self, id: TaskId, user_id: UserId) -> Result; /// Marks a task as started. async fn start(&self, id: TaskId, user_id: UserId) -> Result; /// Marks a task as completed and returns it. /// The caller is responsible for handling recurrence and milestone auto-completion. async fn complete(&self, id: TaskId, user_id: UserId) -> Result>; /// Atomically completes a task and creates the next recurring instance. /// Returns (completed_task, next_task). If the task has no recurrence, /// next_task is None. The entire operation is wrapped in a transaction /// so a crash cannot break the recurrence chain. async fn complete_recurring( &self, id: TaskId, user_id: UserId, next: Option, ) -> Result<(Option, Option)>; /// Counts non-deleted, non-completed tasks in a milestone. async fn count_incomplete_by_milestone( &self, milestone_id: MilestoneId, user_id: UserId, ) -> Result; /// Lists tasks completed within a date range (for weekly review). async fn list_completed_between( &self, user_id: UserId, start: DateTime, end: DateTime, ) -> Result>; /// Lists tasks that became overdue within a date range (for weekly review). async fn list_became_overdue_between( &self, user_id: UserId, start: DateTime, end: DateTime, ) -> Result>; /// Lists tasks due within a date range (for weekly review). async fn list_due_between( &self, user_id: UserId, start: DateTime, end: DateTime, ) -> Result>; /// Lists tasks created within a date range (for monthly review). async fn list_created_between( &self, user_id: UserId, start: DateTime, end: DateTime, ) -> Result>; /// Lists all tasks in a recurrence chain (root + all descendants). async fn list_recurrence_chain(&self, root_id: TaskId, user_id: UserId) -> Result>; } /// Annotation (note) and subtask persistence for a task. #[async_trait] pub trait TaskAnnotations: Send + Sync { /// Gets all annotations for a task. async fn get_annotations_for_task(&self, task_id: TaskId) -> Result>; /// Adds an annotation (note) to a task. async fn add_annotation( &self, task_id: TaskId, user_id: UserId, note: &str, ) -> Result>; /// Deletes an annotation. async fn delete_annotation(&self, annotation_id: AnnotationId, user_id: UserId) -> Result; /// Gets all subtasks for a task. async fn get_subtasks_for_task(&self, task_id: TaskId) -> Result>; /// Adds a subtask to a task. async fn add_subtask( &self, task_id: TaskId, user_id: UserId, text: &str, ) -> Result>; /// Toggles a subtask's completion status. async fn toggle_subtask( &self, subtask_id: SubtaskId, user_id: UserId, ) -> Result>; /// Updates subtask text. async fn update_subtask( &self, subtask_id: SubtaskId, user_id: UserId, text: &str, ) -> Result>; /// Deletes a subtask. async fn delete_subtask(&self, subtask_id: SubtaskId, user_id: UserId) -> Result; /// Adds a linked task as a subtask. /// /// This creates a subtask that links to another task, enabling multi-phase /// features where Phase 2 is a full task linked as a subtask of Phase 1. /// The linked subtask's completion status syncs with the linked task's status. async fn add_subtask_link( &self, task_id: TaskId, user_id: UserId, linked_task_id: TaskId, ) -> Result>; /// Gets all status tokens on a task, in append order. async fn get_status_tokens_for_task(&self, task_id: TaskId) -> Result>; /// Records (upserts) a status token on a task, returning it. /// /// The token id is deterministic in `(task_id, kind, reference)`, so recording /// the same token twice is idempotent, a re-record updates its `state` and /// `is_primary` in place rather than duplicating. When `is_primary` is true this /// becomes the task's primary token and any prior primary flag on the task is /// cleared (at most one primary per task). Returns `None` if the task is missing /// or not owned. async fn record_status_token( &self, task_id: TaskId, user_id: UserId, kind: &str, reference: &str, state: TokenState, is_primary: bool, ) -> Result>; /// Deletes a status token by id, scoped to the user's own tasks. Returns /// whether a row was removed. async fn delete_status_token(&self, token_id: StatusTokenId, user_id: UserId) -> Result; } /// Scheduling and triage state: snooze, waiting, focus, time-block schedule, /// and the date-scoped listings that drive the planner views. #[async_trait] pub trait TaskScheduling: Send + Sync { /// Snoozes a task until the specified time. async fn snooze( &self, id: TaskId, user_id: UserId, until: DateTime, ) -> Result>; /// Removes snooze from a task. async fn unsnooze(&self, id: TaskId, user_id: UserId) -> Result>; /// Lists all currently snoozed tasks. async fn list_snoozed(&self, user_id: UserId) -> Result>; /// Marks a task as waiting for external response. async fn mark_waiting( &self, id: TaskId, user_id: UserId, expected_response: Option>, ) -> Result>; /// Clears the waiting status from a task. async fn clear_waiting(&self, id: TaskId, user_id: UserId) -> Result>; /// Lists all tasks marked as waiting. async fn list_waiting(&self, user_id: UserId) -> Result>; /// Lists tasks scheduled for a specific date. async fn list_scheduled_for_date(&self, user_id: UserId, date: NaiveDate) -> Result>; /// Lists unscheduled tasks due on a specific date. async fn list_unscheduled_due_on_date( &self, user_id: UserId, date: NaiveDate, ) -> Result>; /// Lists unscheduled tasks whose `due` falls within a UTC instant window /// (inclusive upper bound). Lets callers honor a user-local day boundary /// instead of an implicit UTC one. async fn list_unscheduled_due_between( &self, user_id: UserId, start: DateTime, end: DateTime, ) -> Result>; /// Updates a task's time-block schedule. async fn update_schedule( &self, id: TaskId, user_id: UserId, start: Option>, duration: Option, ) -> Result>; /// Sets or clears the focus status on a task. async fn set_focus(&self, id: TaskId, user_id: UserId, is_focus: bool) -> Result>; /// Lists all tasks marked as focus. async fn list_focused(&self, user_id: UserId) -> Result>; /// Clears focus from all tasks. async fn clear_all_focus(&self, user_id: UserId) -> Result; /// Lists high-priority pending tasks for focus selection. async fn list_available_for_focus(&self, user_id: UserId, limit: i64) -> Result>; } /// Time-tracking persistence: live timers, manual entries, and summaries. #[async_trait] pub trait TaskTimeTracking: Send + Sync { /// Starts a timer on a task. Fails if any session is already active for the user. async fn start_timer(&self, task_id: TaskId, user_id: UserId) -> Result; /// Stops the active timer on a task, updating duration and actual_minutes cache. async fn stop_timer(&self, task_id: TaskId, user_id: UserId) -> Result>; /// Discards the active timer without updating actual_minutes. async fn discard_timer(&self, task_id: TaskId, user_id: UserId) -> Result; /// Gets the currently active timer for a user (at most one). async fn get_active_timer(&self, user_id: UserId) -> Result>; /// Lists all time sessions for a task. async fn list_time_sessions( &self, task_id: TaskId, user_id: UserId, ) -> Result>; /// Lists every time session for a user across all tasks (for full backup export). async fn list_all_time_sessions(&self, user_id: UserId) -> Result>; /// Logs a manual time entry (retroactive, no live timer). /// /// `minutes` is a validated [`PositiveMinutes`], so the repository cannot be /// handed a zero/negative duration. async fn log_manual_time( &self, task_id: TaskId, user_id: UserId, minutes: PositiveMinutes, date: DateTime, ) -> Result; /// Gets aggregated time tracking summary grouped by project and date. async fn get_time_summary( &self, user_id: UserId, start: DateTime, end: DateTime, ) -> Result>; } /// Aggregate task-persistence contract. /// /// This is the trait consumers hold as `Arc`. It carries no /// methods of its own; the surface lives in the four capability sub-traits /// ([`TaskCrud`], [`TaskAnnotations`], [`TaskScheduling`], [`TaskTimeTracking`]). /// The blanket impl below means any type implementing all four automatically /// satisfies `TaskRepository`, so a single trait object still exposes the whole /// API while the definition stays split by concern. pub trait TaskRepository: TaskCrud + TaskAnnotations + TaskScheduling + TaskTimeTracking {} impl TaskRepository for T where T: TaskCrud + TaskAnnotations + TaskScheduling + TaskTimeTracking {}