fix(error): migrate session submodules + btmsg/bttask backends to AppError
- session/*.rs (sessions, layout, settings, ssh, agents, metrics, anchors) now return Result<T, AppError> internally, not just at command boundary - btmsg.rs and bttask.rs backends migrated to AppError::Database - 116 cargo tests passing
This commit is contained in:
parent
eb04e7e5b5
commit
f19b69f018
11 changed files with 264 additions and 255 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use rusqlite::{params, Connection, OpenFlags};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::error::AppError;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
|
|
@ -24,17 +25,17 @@ fn db_path() -> PathBuf {
|
|||
})
|
||||
}
|
||||
|
||||
fn open_db() -> Result<Connection, String> {
|
||||
fn open_db() -> Result<Connection, AppError> {
|
||||
let path = db_path();
|
||||
if !path.exists() {
|
||||
return Err("btmsg database not found".into());
|
||||
return Err(AppError::database("btmsg database not found"));
|
||||
}
|
||||
let conn = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)
|
||||
.map_err(|e| format!("Failed to open btmsg.db: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Failed to open btmsg.db: {e}")))?;
|
||||
conn.query_row("PRAGMA journal_mode=WAL", [], |_| Ok(()))
|
||||
.map_err(|e| format!("Failed to set WAL mode: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Failed to set WAL mode: {e}")))?;
|
||||
conn.query_row("PRAGMA busy_timeout = 5000", [], |_| Ok(()))
|
||||
.map_err(|e| format!("Failed to set busy_timeout: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Failed to set busy_timeout: {e}")))?;
|
||||
|
||||
// Migration: add version column if missing
|
||||
let has_version: i64 = conn
|
||||
|
|
@ -81,7 +82,7 @@ pub struct TaskComment {
|
|||
}
|
||||
|
||||
/// Get all tasks for a group
|
||||
pub fn list_tasks(group_id: &str) -> Result<Vec<Task>, String> {
|
||||
pub fn list_tasks(group_id: &str) -> Result<Vec<Task>, AppError> {
|
||||
let db = open_db()?;
|
||||
let mut stmt = db
|
||||
.prepare(
|
||||
|
|
@ -91,7 +92,7 @@ pub fn list_tasks(group_id: &str) -> Result<Vec<Task>, String> {
|
|||
FROM tasks WHERE group_id = ?1
|
||||
ORDER BY sort_order ASC, created_at DESC",
|
||||
)
|
||||
.map_err(|e| format!("Query error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Query error: {e}")))?;
|
||||
|
||||
let rows = stmt
|
||||
.query_map(params![group_id], |row| {
|
||||
|
|
@ -111,14 +112,14 @@ pub fn list_tasks(group_id: &str) -> Result<Vec<Task>, String> {
|
|||
version: row.get::<_, i64>("version").unwrap_or(1),
|
||||
})
|
||||
})
|
||||
.map_err(|e| format!("Query error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Query error: {e}")))?;
|
||||
|
||||
rows.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Row error: {e}"))
|
||||
.map_err(|e| AppError::database(format!("Row error: {e}")))
|
||||
}
|
||||
|
||||
/// Get comments for a task
|
||||
pub fn task_comments(task_id: &str) -> Result<Vec<TaskComment>, String> {
|
||||
pub fn task_comments(task_id: &str) -> Result<Vec<TaskComment>, AppError> {
|
||||
let db = open_db()?;
|
||||
let mut stmt = db
|
||||
.prepare(
|
||||
|
|
@ -126,7 +127,7 @@ pub fn task_comments(task_id: &str) -> Result<Vec<TaskComment>, String> {
|
|||
FROM task_comments WHERE task_id = ?1
|
||||
ORDER BY created_at ASC",
|
||||
)
|
||||
.map_err(|e| format!("Query error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Query error: {e}")))?;
|
||||
|
||||
let rows = stmt
|
||||
.query_map(params![task_id], |row| {
|
||||
|
|
@ -138,20 +139,20 @@ pub fn task_comments(task_id: &str) -> Result<Vec<TaskComment>, String> {
|
|||
created_at: row.get::<_, String>("created_at").unwrap_or_default(),
|
||||
})
|
||||
})
|
||||
.map_err(|e| format!("Query error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Query error: {e}")))?;
|
||||
|
||||
rows.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Row error: {e}"))
|
||||
.map_err(|e| AppError::database(format!("Row error: {e}")))
|
||||
}
|
||||
|
||||
/// Update task status with optimistic locking.
|
||||
/// `expected_version` must match the current version in the database.
|
||||
/// Returns the new version on success.
|
||||
/// When transitioning to 'review', auto-posts to #review-queue channel if it exists.
|
||||
pub fn update_task_status(task_id: &str, status: &str, expected_version: i64) -> Result<i64, String> {
|
||||
pub fn update_task_status(task_id: &str, status: &str, expected_version: i64) -> Result<i64, AppError> {
|
||||
let valid = ["todo", "progress", "review", "done", "blocked"];
|
||||
if !valid.contains(&status) {
|
||||
return Err(format!("Invalid status '{}'. Valid: {:?}", status, valid));
|
||||
return Err(AppError::database(format!("Invalid status '{}'. Valid: {:?}", status, valid)));
|
||||
}
|
||||
let db = open_db()?;
|
||||
|
||||
|
|
@ -171,10 +172,10 @@ pub fn update_task_status(task_id: &str, status: &str, expected_version: i64) ->
|
|||
WHERE id = ?2 AND version = ?3",
|
||||
params![status, task_id, expected_version],
|
||||
)
|
||||
.map_err(|e| format!("Update error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Update error: {e}")))?;
|
||||
|
||||
if rows_affected == 0 {
|
||||
return Err("Task was modified by another agent (version conflict)".into());
|
||||
return Err(AppError::database("Task was modified by another agent (version conflict)"));
|
||||
}
|
||||
|
||||
let new_version = expected_version + 1;
|
||||
|
|
@ -248,25 +249,25 @@ fn ensure_review_channels(db: &Connection, group_id: &str) -> Option<String> {
|
|||
}
|
||||
|
||||
/// Count tasks in 'review' status for a group
|
||||
pub fn review_queue_count(group_id: &str) -> Result<i64, String> {
|
||||
pub fn review_queue_count(group_id: &str) -> Result<i64, AppError> {
|
||||
let db = open_db()?;
|
||||
db.query_row(
|
||||
"SELECT COUNT(*) FROM tasks WHERE group_id = ?1 AND status = 'review'",
|
||||
params![group_id],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.map_err(|e| format!("Query error: {e}"))
|
||||
.map_err(|e| AppError::database(format!("Query error: {e}")))
|
||||
}
|
||||
|
||||
/// Add a comment to a task
|
||||
pub fn add_comment(task_id: &str, agent_id: &str, content: &str) -> Result<String, String> {
|
||||
pub fn add_comment(task_id: &str, agent_id: &str, content: &str) -> Result<String, AppError> {
|
||||
let db = open_db()?;
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
db.execute(
|
||||
"INSERT INTO task_comments (id, task_id, agent_id, content) VALUES (?1, ?2, ?3, ?4)",
|
||||
params![id, task_id, agent_id, content],
|
||||
)
|
||||
.map_err(|e| format!("Insert error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Insert error: {e}")))?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +279,7 @@ pub fn create_task(
|
|||
group_id: &str,
|
||||
created_by: &str,
|
||||
assigned_to: Option<&str>,
|
||||
) -> Result<String, String> {
|
||||
) -> Result<String, AppError> {
|
||||
let db = open_db()?;
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
db.execute(
|
||||
|
|
@ -286,17 +287,17 @@ pub fn create_task(
|
|||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||||
params![id, title, description, priority, group_id, created_by, assigned_to],
|
||||
)
|
||||
.map_err(|e| format!("Insert error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Insert error: {e}")))?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Delete a task
|
||||
pub fn delete_task(task_id: &str) -> Result<(), String> {
|
||||
pub fn delete_task(task_id: &str) -> Result<(), AppError> {
|
||||
let db = open_db()?;
|
||||
db.execute("DELETE FROM task_comments WHERE task_id = ?1", params![task_id])
|
||||
.map_err(|e| format!("Delete comments error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Delete comments error: {e}")))?;
|
||||
db.execute("DELETE FROM tasks WHERE id = ?1", params![task_id])
|
||||
.map_err(|e| format!("Delete task error: {e}"))?;
|
||||
.map_err(|e| AppError::database(format!("Delete task error: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue