Skip to main content

doist/projects/
list.rs

1use std::collections::HashMap;
2
3use crate::api::rest::{Gateway, Project, Task};
4use color_eyre::{Result, eyre::eyre};
5
6#[derive(clap::Parser, Debug)]
7pub struct Params {
8    /// If specified, will only show projects whose tasks are passing this filter.
9    #[arg(short = 'f', long = "filter")]
10    pub filter: Option<String>,
11}
12
13/// Lists available projects.
14pub async fn list(params: Params, gw: &Gateway) -> Result<()> {
15    let projects = gw.projects().await?;
16    if let Some(filter) = params.filter {
17        let tasks = gw.tasks(Some(&filter)).await?;
18        if tasks.is_empty() {
19            return Err(eyre!("no tasks match the given filter"))?;
20        }
21        let projects = filtered_projects(&projects, &tasks)?;
22        for (project, tasks) in projects.iter() {
23            println!("{} (Tasks: {})", &project, tasks);
24        }
25        return Ok(());
26    }
27    for project in projects.iter() {
28        println!("{}", &project);
29    }
30    Ok(())
31}
32
33fn filtered_projects<'a>(
34    projects: &'a [Project],
35    tasks: &'_ [Task],
36) -> Result<Vec<(&'a Project, usize)>> {
37    let hm = tasks.iter().fold(HashMap::<_, usize>::new(), |mut hm, t| {
38        *hm.entry(&t.project_id).or_default() += 1;
39        hm
40    });
41    let projects = projects
42        .iter()
43        .filter_map(|p| hm.get(&p.id).map(|tasks| (p, *tasks)))
44        .collect();
45    Ok(projects)
46}
47
48#[cfg(test)]
49mod test {
50    use super::*;
51    use crate::api::rest::{Project, Task};
52
53    #[tokio::test]
54    async fn filter_projects() {
55        let ps = vec![
56            Project::new("1", "one"),
57            Project::new("2", "two"),
58            Project::new("3", "three"),
59        ];
60        let ts = vec![
61            create_task("1", "1", "one"),
62            create_task("2", "1", "two"),
63            create_task("3", "2", "three"),
64        ];
65        let projects = filtered_projects(&ps, &ts).unwrap();
66        assert_eq!(projects.len(), 2);
67        assert_eq!(projects[0].0.id, "1");
68        assert_eq!(projects[0].1, 2);
69        assert_eq!(projects[1].0.id, "2");
70        assert_eq!(projects[1].1, 1);
71    }
72
73    fn create_task(id: &str, project_id: &str, content: &str) -> Task {
74        let mut task = crate::api::rest::Task::new(id, content);
75        task.project_id = project_id.to_string();
76        task
77    }
78}