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<db::Priority>,
13 effort: Option<db::Effort>,
14 label: Option<&str>,
15 json: bool,
16) -> Result<()> {
17 let store = db::open(root)?;
18 let mut tasks = store.list_tasks()?;
19
20 if let Some(s) = status {
21 let parsed = db::parse_status(s)?;
22 tasks.retain(|t| t.status == parsed);
23 }
24 if let Some(p) = priority {
25 tasks.retain(|t| t.priority == p);
26 }
27 if let Some(e) = effort {
28 tasks.retain(|t| t.effort == e);
29 }
30 if let Some(l) = label {
31 tasks.retain(|t| t.labels.iter().any(|x| x == l));
32 }
33
34 tasks.sort_by_key(|t| (t.priority.score(), t.created_at.clone()));
35
36 if json {
37 // Keep list JSON lean: include scheduling fields but not full work-log history.
38 let mut value = serde_json::to_value(&tasks)?;
39 if let Some(items) = value.as_array_mut() {
40 for item in items {
41 if let Some(obj) = item.as_object_mut() {
42 obj.remove("logs");
43 }
44 }
45 }
46 println!("{}", serde_json::to_string(&value)?);
47 } else {
48 let use_color = stdout_use_color();
49 let mut table = Table::new();
50 table.load_preset(NOTHING);
51 table.set_header(vec!["ID", "STATUS", "PRIORITY", "EFFORT", "TITLE"]);
52 for t in &tasks {
53 table.add_row(vec![
54 cell_bold(&t.id, use_color),
55 cell_fg(
56 format!("[{}]", db::status_label(t.status)),
57 Color::Yellow,
58 use_color,
59 ),
60 cell_fg(db::priority_label(t.priority), Color::Red, use_color),
61 cell_fg(db::effort_label(t.effort), Color::Blue, use_color),
62 Cell::new(&t.title),
63 ]);
64 }
65 if !tasks.is_empty() {
66 println!("{table}");
67 }
68 }
69
70 Ok(())
71}