Skip to main content

things3_cloud/commands/
area.rs

1use std::sync::Arc;
2
3use anyhow::Result;
4use clap::Args;
5use iocraft::prelude::*;
6
7use crate::{
8    app::Cli,
9    commands::Command,
10    common::ICONS,
11    ui::{render_element_to_string, views::area::AreaView},
12    wire::task::TaskStatus,
13};
14
15#[derive(Debug, Args)]
16#[command(about = "Show projects and tasks in an area")]
17pub struct AreaArgs {
18    /// Area UUID (or unique UUID prefix)
19    pub area_id: String,
20    /// Show notes beneath each task/project
21    #[arg(long)]
22    pub detailed: bool,
23    /// Include completed and canceled items
24    #[arg(long)]
25    pub all: bool,
26}
27
28impl Command for AreaArgs {
29    fn run_with_ctx(
30        &self,
31        cli: &Cli,
32        out: &mut dyn std::io::Write,
33        ctx: &mut dyn crate::cmd_ctx::CmdCtx,
34    ) -> Result<()> {
35        let store = Arc::new(cli.load_store()?);
36        let today = ctx.today();
37        let (area_opt, err, ambiguous) = store.resolve_area_identifier(&self.area_id);
38        let Some(area) = area_opt else {
39            eprintln!("{err}");
40            for match_area in ambiguous {
41                eprintln!(
42                    "  {} {}  ({})",
43                    ICONS.area, match_area.title, match_area.uuid
44                );
45            }
46            return Ok(());
47        };
48
49        let status_filter = if self.all {
50            None
51        } else {
52            Some(TaskStatus::Incomplete)
53        };
54        let mut projects = store
55            .projects(status_filter)
56            .into_iter()
57            .filter(|p| p.area.as_ref() == Some(&area.uuid))
58            .collect::<Vec<_>>();
59        projects.sort_by_key(|p| p.index);
60
61        let mut loose_tasks = store
62            .tasks(status_filter, Some(false), None)
63            .into_iter()
64            .filter(|t| {
65                t.area.as_ref() == Some(&area.uuid)
66                    && !t.is_project()
67                    && store.effective_project_uuid(t).is_none()
68            })
69            .collect::<Vec<_>>();
70        loose_tasks.sort_by_key(|t| t.index);
71
72        let mut ui = element! {
73            ContextProvider(value: Context::owned(store.clone())) {
74                ContextProvider(value: Context::owned(today)) {
75                    AreaView(
76                        area: &area,
77                        tasks: loose_tasks.iter().collect::<Vec<_>>(),
78                        projects: projects.iter().collect::<Vec<_>>(),
79                        detailed: self.detailed,
80                    )
81                }
82            }
83        };
84        let rendered = render_element_to_string(&mut ui, cli.no_color);
85        writeln!(out, "{}", rendered)?;
86
87        Ok(())
88    }
89}