1use anyhow::{bail, Result};
2use std::path::Path;
3
4use crate::cli::DepAction;
5use crate::db;
6
7pub fn run(root: &Path, action: &DepAction, json: bool) -> Result<()> {
8 let conn = db::open(root)?;
9
10 match action {
11 DepAction::Add { child, parent } => {
12 if db::would_cycle(&conn, parent, child)? {
13 bail!("adding dependency would create a cycle: {child} → {parent} → … → {child}");
14 }
15 conn.execute(
16 "INSERT OR IGNORE INTO blockers (task_id, blocker_id) VALUES (?1, ?2)",
17 [child, parent],
18 )?;
19 conn.execute(
20 "UPDATE tasks SET updated = ?1 WHERE id = ?2",
21 rusqlite::params![db::now_utc(), child],
22 )?;
23 if json {
24 println!("{}", serde_json::json!({"child": child, "blocker": parent}));
25 } else {
26 let c = crate::color::stdout_theme();
27 println!(
28 "{}{child}{} blocked by {}{parent}{}",
29 c.green, c.reset, c.yellow, c.reset
30 );
31 }
32 }
33 DepAction::Rm { child, parent } => {
34 conn.execute(
35 "DELETE FROM blockers WHERE task_id = ?1 AND blocker_id = ?2",
36 [child, parent],
37 )?;
38 conn.execute(
39 "UPDATE tasks SET updated = ?1 WHERE id = ?2",
40 rusqlite::params![db::now_utc(), child],
41 )?;
42 if !json {
43 let c = crate::color::stdout_theme();
44 println!(
45 "{}{child}{} no longer blocked by {}{parent}{}",
46 c.green, c.reset, c.yellow, c.reset
47 );
48 }
49 }
50 DepAction::Tree { id } => {
51 println!("{id}");
52 let mut stmt = conn.prepare("SELECT id FROM tasks WHERE parent = ?1 ORDER BY id")?;
53 let children: Vec<String> = stmt
54 .query_map([id], |r| r.get(0))?
55 .collect::<rusqlite::Result<_>>()?;
56 for child in &children {
57 println!(" {child}");
58 }
59 }
60 }
61
62 Ok(())
63}