use super::BoardRow;
use crate::app::Snapshot;
use crate::git_tree::GitTree;
use crate::projects::balls::{Ball, ladder};
use crate::projects::join::JoinRow;
use crate::spend::Prices;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Gate {
pub id: String,
pub title: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Drone {
pub root_id: String,
pub name: String,
}
pub(super) fn row(
snap: &Snapshot,
prices: &Prices,
join: &JoinRow,
ball: &Ball,
live: &HashSet<&str>,
by_id: &HashMap<&str, &Ball>,
) -> BoardRow {
let gates = gates(ball, by_id);
let ws = join.workspace.clone();
let path = ws.as_deref().and_then(|name| snap.ws_path(name).ok());
let roots = path
.as_deref()
.map(|ws| stamped_roots(&snap.trees, ws, &ball.id))
.unwrap_or_default();
BoardRow {
project: join.project.clone(),
id: ball.id.clone(),
title: ball.title.clone(),
priority: ball.priority,
column: super::column(ladder(ball, live), !gates.is_empty()),
state: join.state,
drones: path
.as_deref()
.map(|ws| drones(&snap.trees, ws, &roots))
.unwrap_or_default(),
spend: path
.as_deref()
.map(|ws| crate::spend::of_ball(&bills_of(snap, ws), &roots, prices)),
rollup: super::rollup::of(snap, prices, &ball.id, by_id),
workspace: ws,
claimant: ball.claimant.clone(),
parent: ball.parent.clone(),
gates,
}
}
fn gates(ball: &Ball, by_id: &HashMap<&str, &Ball>) -> Vec<Gate> {
ball.blockers
.iter()
.filter(|b| b.on == "close")
.filter_map(|b| {
by_id.get(b.id.as_str()).map(|g| Gate {
id: b.id.clone(),
title: g.title.clone(),
})
})
.collect()
}
pub fn stamped_roots(trees: &HashMap<PathBuf, GitTree>, ws: &Path, ball_id: &str) -> Vec<String> {
let Some(tree) = trees.get(ws) else {
return Vec::new();
};
tree.agents
.iter()
.filter(|a| a.goal_ball.as_deref() == Some(ball_id))
.filter_map(|a| crate::nav::convs::root_of(&tree.agents, &a.agent_id))
.collect::<BTreeSet<_>>()
.into_iter()
.collect()
}
fn drones(trees: &HashMap<PathBuf, GitTree>, ws: &Path, roots: &[String]) -> Vec<Drone> {
let Some(tree) = trees.get(ws) else {
return Vec::new();
};
roots
.iter()
.map(|root| Drone {
root_id: root.clone(),
name: crate::nav::convs::display_name_of(&tree.agents, root),
})
.collect()
}
pub(super) fn bills_of(snap: &Snapshot, ws: &Path) -> Vec<crate::budgets::StepBill> {
snap.bills.get(ws).cloned().unwrap_or_default()
}