Transition a task to Complete status, setting `completed_at` to now and `branch_name`. Guarded: only updates tasks with status = RUNNING. If the task exists but is no longer RUNNING (e.g., already CANCELLED), returns the current task as-is (no-op).
(
&self,
id: &str,
branch_name: &str,
)
| 412 | /// Guarded: only updates tasks with status = RUNNING. If the task exists but is |
| 413 | /// no longer RUNNING (e.g., already CANCELLED), returns the current task as-is (no-op). |
| 414 | pub async fn mark_complete( |
| 415 | &self, |
| 416 | id: &str, |
| 417 | branch_name: &str, |
| 418 | ) -> Result<Task, Box<dyn std::error::Error + Send + Sync>> { |
| 419 | let conn = Arc::clone(&self.conn); |
| 420 | let id = id.to_string(); |
| 421 | let branch_name = branch_name.to_string(); |
| 422 | tokio::task::spawn_blocking(move || { |
| 423 | let conn = conn.lock().map_err(|e| format!("Lock error: {e}"))?; |
| 424 | let now = chrono::Utc::now().to_rfc3339(); |
| 425 | let rows_affected = conn.execute( |
| 426 | "UPDATE tasks SET status = 'COMPLETE', completed_at = ?1, branch_name = ?2 WHERE id = ?3 AND status = 'RUNNING'", |
| 427 | rusqlite::params![now, branch_name, id], |
| 428 | )?; |
| 429 | if rows_affected == 0 { |
| 430 | // Task may not exist, or may already be in a terminal state (e.g. CANCELLED) |
| 431 | return read_task_by_id(&conn, &id); |
| 432 | } |
| 433 | read_task_by_id(&conn, &id) |
| 434 | }) |
| 435 | .await? |
| 436 | } |
| 437 | |
| 438 | /// Transition a task to Failed status, setting `completed_at` to now and `error_message`. |
| 439 | /// |