Skip to main content

things3_cloud/commands/
area.rs

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