list.rs

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