list.rs

 1use anyhow::Result;
 2use comfy_table::presets::NOTHING;
 3use comfy_table::{Cell, Color, Table};
 4use std::path::Path;
 5
 6use crate::color::{cell_bold, cell_fg, stdout_use_color};
 7use crate::db;
 8
 9pub fn run(
10    root: &Path,
11    status: Option<&str>,
12    priority: Option<i32>,
13    effort: Option<i32>,
14    label: Option<&str>,
15    json: bool,
16) -> Result<()> {
17    let conn = db::open(root)?;
18
19    let mut sql = String::from(
20        "SELECT id, title, description, type, priority, status, effort, parent, created, updated
21         FROM tasks WHERE 1=1",
22    );
23    let mut params: Vec<Box<dyn rusqlite::types::ToSql>> = Vec::new();
24    let mut idx = 1;
25
26    if let Some(s) = status {
27        sql.push_str(&format!(" AND status = ?{idx}"));
28        params.push(Box::new(s.to_string()));
29        idx += 1;
30    }
31    if let Some(p) = priority {
32        sql.push_str(&format!(" AND priority = ?{idx}"));
33        params.push(Box::new(p));
34        idx += 1;
35    }
36    if let Some(e) = effort {
37        sql.push_str(&format!(" AND effort = ?{idx}"));
38        params.push(Box::new(e));
39        idx += 1;
40    }
41    if let Some(l) = label {
42        sql.push_str(&format!(
43            " AND id IN (SELECT task_id FROM labels WHERE label = ?{idx})"
44        ));
45        params.push(Box::new(l.to_string()));
46    }
47
48    sql.push_str(" ORDER BY priority, created");
49
50    let param_refs: Vec<&dyn rusqlite::types::ToSql> = params.iter().map(|p| p.as_ref()).collect();
51    let mut stmt = conn.prepare(&sql)?;
52    let tasks: Vec<db::Task> = stmt
53        .query_map(param_refs.as_slice(), db::row_to_task)?
54        .collect::<rusqlite::Result<_>>()?;
55
56    if json {
57        let details: Vec<db::TaskDetail> = tasks
58            .into_iter()
59            .map(|t| {
60                let labels = db::load_labels(&conn, &t.id)?;
61                let blockers = db::load_blockers(&conn, &t.id)?;
62                Ok(db::TaskDetail {
63                    task: t,
64                    labels,
65                    blockers,
66                })
67            })
68            .collect::<Result<_>>()?;
69        println!("{}", serde_json::to_string(&details)?);
70    } else {
71        let use_color = stdout_use_color();
72        let mut table = Table::new();
73        table.load_preset(NOTHING);
74        table.set_header(vec!["ID", "STATUS", "PRIORITY", "EFFORT", "TITLE"]);
75        for t in &tasks {
76            table.add_row(vec![
77                cell_bold(&t.id, use_color),
78                cell_fg(format!("[{}]", t.status), Color::Yellow, use_color),
79                cell_fg(db::priority_label(t.priority), Color::Red, use_color),
80                cell_fg(db::effort_label(t.effort), Color::Blue, use_color),
81                Cell::new(&t.title),
82            ]);
83        }
84        if !tasks.is_empty() {
85            println!("{table}");
86        }
87    }
88
89    Ok(())
90}