mod rollup;
mod rows;
#[cfg(test)]
mod tests;
pub use rollup::descendants;
pub use rows::{Drone, Gate, stamped_roots};
use crate::app::Snapshot;
use crate::projects::balls::{Ball, Status};
use crate::projects::join::JoinState;
use crate::spend::{Figure, Prices};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Column {
Ready,
Gated,
Claimed,
Blocked,
}
impl Column {
pub const ALL: [Column; 4] = [
Column::Ready,
Column::Gated,
Column::Claimed,
Column::Blocked,
];
pub(crate) fn word(self) -> &'static str {
match self {
Column::Ready => "ready",
Column::Gated => "gated",
Column::Claimed => "claimed",
Column::Blocked => "blocked",
}
}
}
pub fn column(status: Status, gated: bool) -> Column {
match (status, gated) {
(Status::Claimed, _) => Column::Claimed,
(Status::Blocked, _) => Column::Blocked,
(Status::Ready, true) => Column::Gated,
(Status::Ready, false) => Column::Ready,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BoardRow {
pub project: String,
pub id: String,
pub title: String,
pub priority: i64,
pub column: Column,
pub state: JoinState,
pub workspace: Option<String>,
pub claimant: Option<String>,
pub parent: Option<String>,
pub gates: Vec<Gate>,
pub drones: Vec<Drone>,
pub spend: Option<Figure>,
pub rollup: Option<Figure>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Board {
pub rows: Vec<BoardRow>,
pub fleet: Vec<crate::fleet::Facts>,
}
impl Board {
pub fn in_column(&self, column: Column) -> Vec<BoardRow> {
self.rows
.iter()
.filter(|r| r.column == column)
.cloned()
.collect()
}
pub fn count(&self, column: Column) -> usize {
self.rows.iter().filter(|r| r.column == column).count()
}
}
pub fn build(snap: &Snapshot, ui: &crate::ui_state::UiState, now_unix: i64) -> Board {
let prices = ui.prices();
let rows = rows_of(snap, &prices);
let fleet = crate::fleet::facts::of(snap, &prices, ui.ceiling(), &rows, now_unix);
Board { rows, fleet }
}
fn rows_of(snap: &Snapshot, prices: &Prices) -> Vec<BoardRow> {
let index: HashMap<String, (HashSet<&str>, HashMap<&str, &Ball>)> = snap
.balls_by_project
.iter()
.map(|(project, balls)| {
let by_id: HashMap<&str, &Ball> = balls.iter().map(|b| (b.id.as_str(), b)).collect();
(
snap.project_name(project),
(by_id.keys().copied().collect(), by_id),
)
})
.collect();
let mut rows = Vec::new();
for join in &snap.join_rows {
let Some((live, by_id)) = index.get(&join.project) else {
continue;
};
let Some(ball) = by_id.get(join.ball_id.as_str()) else {
continue;
};
rows.push(rows::row(snap, prices, join, ball, live, by_id));
}
rows.sort_by(|a, b| {
(a.priority, a.id.as_str())
.cmp(&(b.priority, b.id.as_str()))
.then_with(|| a.project.cmp(&b.project))
});
rows
}