Skip to main content

things3_cloud/commands/
logbook.rs

1use crate::app::Cli;
2use crate::commands::{Command, DetailedArgs};
3use crate::common::{
4    BOLD, DIM, GREEN, ICONS, colored, fmt_date_local, fmt_task_line, fmt_task_with_note, parse_day,
5};
6use anyhow::Result;
7use clap::Args;
8use std::io::Write;
9
10#[derive(Debug, Default, Args)]
11#[command(about = "Show the Logbook")]
12pub struct LogbookArgs {
13    #[command(flatten)]
14    pub detailed: DetailedArgs,
15    #[arg(long = "from", help = "Show items completed on/after this date (YYYY-MM-DD)")]
16    pub from_date: Option<String>,
17    #[arg(long = "to", help = "Show items completed on/before this date (YYYY-MM-DD)")]
18    pub to_date: Option<String>,
19}
20
21impl Command for LogbookArgs {
22    fn run_with_ctx(
23        &self,
24        cli: &Cli,
25        out: &mut dyn Write,
26        ctx: &mut dyn crate::cmd_ctx::CmdCtx,
27    ) -> Result<()> {
28        let store = cli.load_store()?;
29        let today = ctx.today();
30
31        let from_day =
32            parse_day(self.from_date.as_deref(), "--from").map_err(anyhow::Error::msg)?;
33        let to_day = parse_day(self.to_date.as_deref(), "--to").map_err(anyhow::Error::msg)?;
34
35        if let (Some(from), Some(to)) = (from_day, to_day)
36            && from > to
37        {
38            eprintln!("--from date must be before or equal to --to date");
39            return Ok(());
40        }
41
42        let tasks = store.logbook(from_day, to_day);
43        if tasks.is_empty() {
44            writeln!(
45                out,
46                "{}",
47                colored("Logbook is empty.", &[DIM], cli.no_color)
48            )?;
49            return Ok(());
50        }
51
52        let id_prefix_len =
53            store.unique_prefix_length(&tasks.iter().map(|t| t.uuid.clone()).collect::<Vec<_>>());
54
55        writeln!(
56            out,
57            "{}",
58            colored(
59                &format!("{} Logbook  ({} tasks)", ICONS.done, tasks.len()),
60                &[BOLD, GREEN],
61                cli.no_color,
62            )
63        )?;
64
65        let mut current_day = String::new();
66        for task in tasks {
67            let day = fmt_date_local(task.stop_date);
68            if day != current_day {
69                writeln!(out)?;
70                writeln!(
71                    out,
72                    "{}",
73                    colored(&format!("  {}", day), &[BOLD], cli.no_color)
74                )?;
75                current_day = day;
76            }
77            let line = fmt_task_line(
78                &task,
79                &store,
80                true,
81                false,
82                false,
83                Some(id_prefix_len),
84                &today,
85                cli.no_color,
86            );
87            writeln!(
88                out,
89                "{}",
90                fmt_task_with_note(
91                    line,
92                    &task,
93                    "    ",
94                    Some(id_prefix_len),
95                    self.detailed.detailed,
96                    cli.no_color,
97                )
98            )?;
99        }
100
101        Ok(())
102    }
103}