1use crate::app::Cli;
2use crate::commands::{Command, DetailedArgs};
3use crate::common::{
4 colored, fmt_project_with_note, fmt_task_line, fmt_task_with_note, BLUE, BOLD, DIM, ICONS,
5 YELLOW,
6};
7use crate::wire::task::TaskStatus;
8use anyhow::Result;
9use clap::Args;
10use std::io::Write;
11
12#[derive(Debug, Default, Args)]
13pub struct TodayArgs {
14 #[command(flatten)]
15 pub detailed: DetailedArgs,
16}
17
18impl Command for TodayArgs {
19 fn run_with_ctx(
20 &self,
21 cli: &Cli,
22 out: &mut dyn Write,
23 ctx: &mut dyn crate::cmd_ctx::CmdCtx,
24 ) -> Result<()> {
25 let store = cli.load_store()?;
26 let today = ctx.today();
27 let tasks = store.today(&today);
28 let mut today_items: Vec<_> = store
29 .tasks(Some(TaskStatus::Incomplete), Some(false), None)
30 .into_iter()
31 .filter(|t| {
32 !t.is_heading()
33 && !t.title.trim().is_empty()
34 && t.entity == "Task6"
35 && (t.is_today(&today) || t.evening)
36 })
37 .collect();
38
39 today_items.sort_by_key(|task| {
40 let tir = task.today_index_reference.unwrap_or(0);
41 (
42 std::cmp::Reverse(tir),
43 task.today_index,
44 std::cmp::Reverse(task.index),
45 )
46 });
47
48 if today_items.is_empty() {
49 writeln!(
50 out,
51 "{}",
52 colored("No tasks for today.", &[DIM], cli.no_color)
53 )?;
54 return Ok(());
55 }
56
57 let regular: Vec<_> = today_items.iter().filter(|t| !t.evening).cloned().collect();
58 let evening: Vec<_> = today_items.iter().filter(|t| t.evening).cloned().collect();
59 let project_count = today_items.iter().filter(|t| t.is_project()).count();
60 let id_prefix_len = store.unique_prefix_length(
61 &today_items
62 .iter()
63 .map(|i| i.uuid.clone())
64 .collect::<Vec<_>>(),
65 );
66
67 if project_count > 0 {
68 let label = if project_count == 1 {
69 "project"
70 } else {
71 "projects"
72 };
73 writeln!(
74 out,
75 "{}",
76 colored(
77 &format!(
78 "{} Today ({} tasks, {} {})",
79 ICONS.today,
80 tasks.len(),
81 project_count,
82 label
83 ),
84 &[BOLD, YELLOW],
85 cli.no_color,
86 )
87 )?;
88 } else {
89 writeln!(
90 out,
91 "{}",
92 colored(
93 &format!("{} Today ({} tasks)", ICONS.today, tasks.len()),
94 &[BOLD, YELLOW],
95 cli.no_color,
96 )
97 )?;
98 }
99
100 if !regular.is_empty() {
101 writeln!(out)?;
102 for item in regular {
103 if item.is_project() {
104 writeln!(
105 out,
106 "{}",
107 fmt_project_with_note(
108 &item,
109 &store,
110 " ",
111 Some(id_prefix_len),
112 false,
113 true,
114 self.detailed.detailed,
115 &today,
116 cli.no_color,
117 )
118 )?;
119 } else {
120 let line = fmt_task_line(
121 &item,
122 &store,
123 false,
124 false,
125 true,
126 Some(id_prefix_len),
127 &today,
128 cli.no_color,
129 );
130 writeln!(
131 out,
132 "{}",
133 fmt_task_with_note(
134 line,
135 &item,
136 " ",
137 Some(id_prefix_len),
138 self.detailed.detailed,
139 cli.no_color,
140 )
141 )?;
142 }
143 }
144 }
145
146 if !evening.is_empty() {
147 writeln!(out)?;
148 writeln!(
149 out,
150 "{}",
151 colored(
152 &format!("{} This Evening", ICONS.evening),
153 &[BOLD, BLUE],
154 cli.no_color,
155 )
156 )?;
157 writeln!(out)?;
158
159 for item in evening {
160 if item.is_project() {
161 writeln!(
162 out,
163 "{}",
164 fmt_project_with_note(
165 &item,
166 &store,
167 " ",
168 Some(id_prefix_len),
169 false,
170 true,
171 self.detailed.detailed,
172 &today,
173 cli.no_color,
174 )
175 )?;
176 } else {
177 let line = fmt_task_line(
178 &item,
179 &store,
180 false,
181 false,
182 true,
183 Some(id_prefix_len),
184 &today,
185 cli.no_color,
186 );
187 writeln!(
188 out,
189 "{}",
190 fmt_task_with_note(
191 line,
192 &item,
193 " ",
194 Some(id_prefix_len),
195 self.detailed.detailed,
196 cli.no_color,
197 )
198 )?;
199 }
200 }
201 }
202
203 Ok(())
204 }
205}