Skip to main content

wsb_github/
targets.rs

1//! What the machine's mirror holds: everything every project in the workshop
2//! has on GitHub.
3//!
4//! Read from the projects rather than remembered, every time: a worktree
5//! added this morning has commits CI reports on, a submodule removed should
6//! stop being asked about, and a board a project stopped referencing should
7//! stop costing requests.
8
9use std::collections::{BTreeMap, BTreeSet};
10use std::path::Path;
11
12use anyhow::Result;
13
14use crate::boards::{self, BoardRef};
15use crate::checkout;
16
17/// Everything to mirror, and what could not be read while finding it out.
18#[derive(Debug, Clone, Default, PartialEq, Eq)]
19pub struct Targets {
20    /// `owner/repo`, and every commit some worktree has it checked out at.
21    pub repositories: BTreeMap<String, BTreeSet<String>>,
22    /// Every board some worktree's project settings name.
23    pub boards: BTreeSet<BoardRef>,
24    /// What could not be read, by where it was: `project/worktree: why`.
25    ///
26    /// While there are any, what the mirror holds is never forgotten: a
27    /// worktree that could not be read this minute is not a worktree that
28    /// stopped having repositories, and treating it as one would delete what
29    /// it holds on every transient failure.
30    pub problems: Vec<String>,
31}
32
33impl Targets {
34    /// What every project in the workshop has on GitHub: each worktree's
35    /// repositories and commits, and the boards its checkout's settings name.
36    pub fn of(workshop: &wsb_prj::Workshop) -> Result<Self> {
37        let mut targets = Self::default();
38        for workspace in workshop.workspaces()? {
39            let worktrees = match workspace.worktrees() {
40                Ok(worktrees) => worktrees,
41                Err(error) => {
42                    targets.problems.push(format!("{}: {error}", workspace.name()));
43                    continue;
44                }
45            };
46            for worktree in worktrees {
47                let place = wsb_prj::Location {
48                    workspace: workspace.clone(),
49                    worktree: Some(worktree.name.clone()),
50                };
51                match boards::read(&worktree.path) {
52                    Ok(named) => targets.boards.extend(named),
53                    Err(error) => targets.problems.push(format!("{place}: {error:#}")),
54                }
55                if let Err(error) = targets.add_checkout(&worktree.path) {
56                    targets.problems.push(format!("{place}: {error:#}"));
57                }
58            }
59        }
60        Ok(targets)
61    }
62
63    /// Add what one checkout holds.
64    pub fn add_checkout(&mut self, path: &Path) -> Result<()> {
65        for repository in checkout::repositories(path)? {
66            let commits = self.repositories.entry(repository.name).or_default();
67            if let Some(head) = repository.head {
68                commits.insert(head);
69            }
70        }
71        Ok(())
72    }
73
74    /// The owners whose board lists are needed to find the referenced boards.
75    pub fn board_owners(&self) -> BTreeSet<String> {
76        self.boards.iter().map(|board| board.owner.clone()).collect()
77    }
78}