add backlog delete command
change qwnowosqwoworymmtrzmultprlpxqyot
commit 20cfe15f8c544556451c3de53ccbefa5aee0d2bd
author Alpha Chen <alpha@kejadlen.dev>
date
parent xqlsqqml
diff --git a/src/bin/ranger/commands/backlog.rs b/src/bin/ranger/commands/backlog.rs
index a9cfab4..a611656 100644
--- a/src/bin/ranger/commands/backlog.rs
+++ b/src/bin/ranger/commands/backlog.rs
@@ -32,6 +32,13 @@ pub enum BacklogCommands {
         #[arg(long)]
         done: bool,
     },
+    /// Delete a backlog and all its tasks
+    #[command(visible_alias = "rm")]
+    Delete {
+        /// Backlog name
+        #[arg(add = ArgValueCompleter::new(completions::complete_backlog_names))]
+        name: String,
+    },
     /// Rebalance task positions in a backlog
     Rebalance {
         /// Backlog name
@@ -52,6 +59,10 @@ pub async fn run(pool: &SqlitePool, command: BacklogCommands, json: bool) -> Res
             let backlogs = ops::backlog::list(&mut conn).await?;
             output::print_list(&backlogs, json, print_backlog);
         }
+        BacklogCommands::Delete { name } => {
+            let backlog = ops::backlog::delete(&mut conn, &name).await?;
+            output::print(&backlog, json, |b| println!("Deleted backlog: {}", b.name));
+        }
         BacklogCommands::Rebalance { name } => {
             let backlog = ops::backlog::get_by_name(&mut conn, &name).await?;
             let count = ops::task::rebalance(&mut conn, backlog.id).await?;
diff --git a/src/ops/backlog.rs b/src/ops/backlog.rs
index 8466557..a5a239f 100644
--- a/src/ops/backlog.rs
+++ b/src/ops/backlog.rs
@@ -32,6 +32,15 @@ pub async fn get_by_name(conn: &mut SqliteConnection, name: &str) -> Result<Back
     Ok(backlog)
 }
 
+pub async fn delete(conn: &mut SqliteConnection, name: &str) -> Result<Backlog, RangerError> {
+    let backlog = get_by_name(&mut *conn, name).await?;
+    sqlx::query("DELETE FROM backlogs WHERE id = ?")
+        .bind(backlog.id)
+        .execute(&mut *conn)
+        .await?;
+    Ok(backlog)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -73,4 +82,24 @@ mod tests {
         let result = get_by_name(&mut conn, "nonexistent").await;
         assert!(result.is_err());
     }
+
+    #[tokio::test]
+    async fn delete_backlog() {
+        let pool = test_pool().await;
+        let mut conn = pool.acquire().await.unwrap();
+        create(&mut conn, "ToDelete").await.unwrap();
+        let deleted = delete(&mut conn, "ToDelete").await.unwrap();
+        assert_eq!(deleted.name, "ToDelete");
+
+        let result = get_by_name(&mut conn, "ToDelete").await;
+        assert!(result.is_err());
+    }
+
+    #[tokio::test]
+    async fn delete_backlog_not_found() {
+        let pool = test_pool().await;
+        let mut conn = pool.acquire().await.unwrap();
+        let result = delete(&mut conn, "nonexistent").await;
+        assert!(result.is_err());
+    }
 }
diff --git a/tests/cli.rs b/tests/cli.rs
index 885dbfc..40d223b 100644
--- a/tests/cli.rs
+++ b/tests/cli.rs
@@ -421,6 +421,46 @@ fn full_workflow() {
     assert!(!stdout.contains("bug"));
     assert!(stdout.contains("frontend"));
 
+    // --- Backlog delete ---
+
+    // Create a throwaway backlog with a task, then delete it
+    ranger(db_path)
+        .args(["backlog", "create", "Throwaway"])
+        .output()
+        .unwrap();
+    ranger(db_path)
+        .args(["task", "create", "Doomed task", "--backlog", "Throwaway"])
+        .output()
+        .unwrap();
+    let output = ranger(db_path)
+        .args(["backlog", "delete", "Throwaway"])
+        .output()
+        .unwrap();
+    assert!(output.status.success());
+    let stdout = String::from_utf8(output.stdout).unwrap();
+    assert!(stdout.contains("Deleted backlog: Throwaway"));
+
+    // Verify backlog is gone
+    let output = ranger(db_path)
+        .args(["backlog", "list", "--json"])
+        .output()
+        .unwrap();
+    let backlogs: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
+    let names: Vec<&str> = backlogs
+        .as_array()
+        .unwrap()
+        .iter()
+        .map(|b| b["name"].as_str().unwrap())
+        .collect();
+    assert!(!names.contains(&"Throwaway"));
+
+    // Deleting non-existent backlog fails
+    let output = ranger(db_path)
+        .args(["backlog", "delete", "Nonexistent"])
+        .output()
+        .unwrap();
+    assert!(!output.status.success());
+
     // Dynamic shell completions via COMPLETE env var
     for shell in ["bash", "zsh", "fish", "elvish", "powershell"] {
         let output = ranger(db_path).env("COMPLETE", shell).output().unwrap();