1use anyhow::Result;
2use comfy_table::presets::NOTHING;
3use comfy_table::{Cell, Table};
4use std::path::Path;
5
6use crate::color::{cell_bold, cell_effort, cell_priority, cell_status, stdout_use_color};
7use crate::db;
8use crate::model::{Effort, Priority, Status};
9
10pub struct Opts<'a> {
11 pub status: Option<&'a str>,
12 pub priority: Option<Priority>,
13 pub effort: Option<Effort>,
14 pub label: Option<&'a str>,
15 pub task_type: Option<&'a str>,
16 pub all: bool,
17 pub json: bool,
18}
19
20pub fn run(root: &Path, opts: Opts) -> Result<()> {
21 let store = db::open(root)?;
22 let mut tasks = store.list_tasks()?;
23
24 if let Some(s) = opts.status {
25 let parsed = Status::parse(s)?;
26 tasks.retain(|t| t.status == parsed);
27 } else if !opts.all {
28 // By default, show open and in-progress tasks but not closed.
29 tasks.retain(|t| t.status == Status::Open || t.status == Status::InProgress);
30 }
31 if let Some(p) = opts.priority {
32 tasks.retain(|t| t.priority == p);
33 }
34 if let Some(e) = opts.effort {
35 tasks.retain(|t| t.effort == e);
36 }
37 if let Some(l) = opts.label {
38 tasks.retain(|t| t.labels.iter().any(|x| x == l));
39 }
40 if let Some(tt) = opts.task_type {
41 tasks.retain(|t| t.task_type == tt);
42 }
43
44 tasks.sort_by_key(|t| (t.priority.score(), t.created_at.clone()));
45
46 if opts.json {
47 // Keep list JSON lean: include scheduling fields but not full work-log history.
48 let mut value = serde_json::to_value(&tasks)?;
49 if let Some(items) = value.as_array_mut() {
50 for item in items {
51 if let Some(obj) = item.as_object_mut() {
52 obj.remove("logs");
53 }
54 }
55 }
56 println!("{}", serde_json::to_string(&value)?);
57 } else {
58 let use_color = stdout_use_color();
59 let mut table = Table::new();
60 table.load_preset(NOTHING);
61 table.set_header(vec![
62 "ID", "STATUS", "TYPE", "PRIORITY", "EFFORT", "LABELS", "TITLE",
63 ]);
64 for t in &tasks {
65 table.add_row(vec![
66 cell_bold(&t.id, use_color),
67 cell_status(format!("[{}]", t.status.as_str()), t.status, use_color),
68 Cell::new(&t.task_type),
69 cell_priority(t.priority.as_str(), t.priority, use_color),
70 cell_effort(t.effort.as_str(), t.effort, use_color),
71 Cell::new(t.labels.join(", ")),
72 Cell::new(&t.title),
73 ]);
74 }
75 if !tasks.is_empty() {
76 println!("{table}");
77 }
78 }
79
80 Ok(())
81}