use std::collections::{BTreeMap, HashMap, HashSet};
use std::path::{Path, PathBuf};
use super::super::balls::{Ball, ladder};
use super::{JoinRow, JoinState, JoinStatus, classify, live_status};
use crate::binding::{Workspace, WorkspaceKind};
pub(super) fn named_map(workspaces: &[Workspace]) -> BTreeMap<&str, &Path> {
workspaces
.iter()
.filter_map(|w| match &w.kind {
WorkspaceKind::Named { name } => Some((name.as_str(), w.path.as_path())),
_ => None,
})
.collect()
}
pub fn join(
projects: &[PathBuf],
cloned: &[PathBuf],
live_by_project: &HashMap<PathBuf, Vec<Ball>>,
closed_by_project: &HashMap<PathBuf, Vec<Ball>>,
workspaces: &[Workspace],
) -> Vec<JoinRow> {
let names = named_map(workspaces);
let mut rows = Vec::new();
let mut engaged: HashSet<String> = HashSet::new();
for project in cloned {
let name = crate::naming::name_of(projects, project);
let Some(live) = live_by_project.get(project) else {
rows.push(orphaned_row(name));
continue;
};
let live_ids: HashSet<&str> = live.iter().map(|b| b.id.as_str()).collect();
for ball in live {
let bound = bind(&names, ball, &mut engaged);
let js = live_status(ladder(ball, &live_ids));
rows.push(JoinRow {
project: name.clone(),
ball_id: ball.id.clone(),
state: classify(js, bound.is_some()),
workspace: bound,
claimant: ball.claimant.clone(),
title: Some(ball.title.clone()),
});
}
for ball in closed_by_project.get(project).into_iter().flatten() {
if live_ids.contains(ball.id.as_str()) {
continue;
}
if let Some(bound) = bind(&names, ball, &mut engaged) {
rows.push(JoinRow {
project: name.clone(),
ball_id: ball.id.clone(),
state: classify(JoinStatus::Closed, true),
workspace: Some(bound),
claimant: ball.claimant.clone(),
title: None,
});
}
}
}
for name in names.keys() {
if !engaged.contains(*name) {
rows.push(JoinRow {
project: String::new(),
ball_id: String::new(),
state: JoinState::UnassignedWorkspace,
workspace: Some((*name).to_owned()),
claimant: None,
title: None,
});
}
}
rows
}
fn bind(
names: &BTreeMap<&str, &Path>,
ball: &Ball,
engaged: &mut HashSet<String>,
) -> Option<String> {
let claimant = ball.claimant.as_deref()?;
names.get(claimant)?;
engaged.insert(claimant.to_owned());
Some(claimant.to_owned())
}
fn orphaned_row(project: String) -> JoinRow {
JoinRow {
project,
ball_id: String::new(),
state: JoinState::OrphanedProject,
workspace: None,
claimant: None,
title: None,
}
}