1use anyhow::Result;
2use comfy_table::presets::NOTHING;
3use comfy_table::Table;
4use std::path::Path;
5
6use crate::db;
7
8pub fn run(root: &Path, json: bool) -> Result<()> {
9 let conn = db::open(root)?;
10
11 let mut stmt = conn.prepare(
12 "SELECT id, title, description, type, priority, status, effort, parent, created, updated
13 FROM tasks
14 WHERE status = 'open'
15 AND id NOT IN (
16 SELECT b.task_id FROM blockers b
17 JOIN tasks t ON b.blocker_id = t.id
18 WHERE t.status != 'closed'
19 )
20 ORDER BY priority, created",
21 )?;
22
23 let tasks: Vec<db::Task> = stmt
24 .query_map([], db::row_to_task)?
25 .collect::<rusqlite::Result<_>>()?;
26
27 if json {
28 let summary: Vec<serde_json::Value> = tasks
29 .iter()
30 .map(|t| {
31 serde_json::json!({
32 "id": t.id,
33 "title": t.title,
34 "priority": db::priority_label(t.priority),
35 "effort": db::effort_label(t.effort),
36 })
37 })
38 .collect();
39 println!("{}", serde_json::to_string(&summary)?);
40 } else {
41 let c = crate::color::stdout_theme();
42 let mut table = Table::new();
43 table.load_preset(NOTHING);
44 table.set_header(vec!["ID", "PRIORITY", "EFFORT", "TITLE"]);
45 for t in &tasks {
46 table.add_row(vec![
47 format!("{}{}{}", c.green, t.id, c.reset),
48 format!("{}{}{}", c.red, db::priority_label(t.priority), c.reset),
49 format!("{}{}{}", c.blue, db::effort_label(t.effort), c.reset),
50 t.title.clone(),
51 ]);
52 }
53 if !tasks.is_empty() {
54 println!("{table}");
55 }
56 }
57
58 Ok(())
59}