show.rs

 1use anyhow::{bail, Result};
 2use std::path::Path;
 3
 4use crate::db;
 5
 6pub fn run(root: &Path, id: &str, json: bool) -> Result<()> {
 7    let conn = db::open(root)?;
 8
 9    let exists: bool = conn.query_row("SELECT COUNT(*) FROM tasks WHERE id = ?1", [id], |r| {
10        r.get::<_, i64>(0).map(|n| n > 0)
11    })?;
12
13    if !exists {
14        bail!("task {id} not found");
15    }
16
17    let detail = db::load_task_detail(&conn, id)?;
18
19    if json {
20        println!("{}", serde_json::to_string(&detail)?);
21        return Ok(());
22    }
23
24    let c = crate::color::stdout_theme();
25    let t = &detail.task;
26
27    // Title as a heading with status tag
28    println!(
29        "{}# {}{} {}[{}]{}",
30        c.bold, t.title, c.reset, c.yellow, t.status, c.reset
31    );
32
33    // Description as body text, only when present
34    if !t.description.is_empty() {
35        println!();
36        println!("{}", t.description);
37    }
38
39    // Metadata line: id 路 type 路 priority 路 effort
40    println!();
41    println!(
42        "{}{}{}{}{}{}{} priority 路 {}{}{} effort",
43        c.bold,
44        t.id,
45        c.reset,
46        t.task_type,
47        c.red,
48        db::priority_label(t.priority),
49        c.reset,
50        c.blue,
51        db::effort_label(t.effort),
52        c.reset,
53    );
54
55    // Labels, only when present
56    if !detail.labels.is_empty() {
57        println!("labels: {}", detail.labels.join(", "));
58    }
59
60    // Blockers, only when present
61    let (open_blockers, closed_blockers) = db::load_blockers_partitioned(&conn, &t.id)?;
62    let total = open_blockers.len() + closed_blockers.len();
63    if total > 0 {
64        let label = if total == 1 { "blocker" } else { "blockers" };
65        let mut ids: Vec<String> = Vec::new();
66        for id in &open_blockers {
67            ids.push(id.clone());
68        }
69        for id in &closed_blockers {
70            ids.push(format!("{id} [closed]"));
71        }
72
73        let value = if open_blockers.is_empty() {
74            format!("[all closed] {}", ids.join(", "))
75        } else {
76            ids.join(", ")
77        };
78
79        println!("{label}: {value}");
80    }
81
82    // Timestamps at the bottom
83    println!("created {} 路 updated {}", t.created, t.updated);
84
85    Ok(())
86}