use std::collections::BTreeMap;
use crate::model::types::{Bead, Edge, Status};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Related {
pub id: String,
pub edge: Edge,
pub status: Option<Status>,
pub title: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Relations {
pub parent: Option<Related>,
pub depends_on: Vec<Related>,
pub blocks: Vec<Related>,
}
pub fn relations(beads: &[Bead]) -> BTreeMap<String, Relations> {
let by_id: BTreeMap<&str, &Bead> = beads.iter().map(|b| (b.id.as_str(), b)).collect();
let related = |id: &str, edge: Edge| {
let row = by_id.get(id);
Related {
id: id.to_string(),
edge,
status: row.map(|bead| bead.status.clone()),
title: row.map(|bead| bead.title.clone()),
}
};
let mut relations: BTreeMap<String, Relations> = beads
.iter()
.map(|bead| {
let mut ties = Relations::default();
for dependency in &bead.dependencies {
let tied = related(&dependency.on, dependency.edge.clone());
match dependency.edge {
Edge::ParentChild => ties.parent = Some(tied),
Edge::Blocks | Edge::Other(_) => ties.depends_on.push(tied),
}
}
(bead.id.clone(), ties)
})
.collect();
for bead in by_id.values() {
for dependency in &bead.dependencies {
if dependency.edge != Edge::Blocks {
continue;
}
if let Some(blocker) = relations.get_mut(&dependency.on) {
blocker.blocks.push(related(&bead.id, Edge::Blocks));
}
}
}
relations
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collect::bd::parse_beads;
use pretty_assertions::assert_eq;
const BEADS: &str = r#"[
{"id":"dun-3","title":"the root","status":"in_progress"},
{"id":"dun-3.1","title":"the waiting one","status":"open",
"dependencies":[{"depends_on_id":"dun-3","type":"parent-child"},
{"depends_on_id":"dun-3.2","type":"blocks"}]},
{"id":"dun-3.2","title":"the one waited on","status":"closed",
"dependencies":[{"depends_on_id":"dun-3","type":"parent-child"},
{"depends_on_id":"dun-9","type":"blocks"}]}
]"#;
fn tied() -> BTreeMap<String, Relations> {
relations(&parse_beads(BEADS).expect("the rows parse"))
}
fn held(id: &str, edge: Edge, status: Status, title: &str) -> Related {
Related {
id: id.to_string(),
edge,
status: Some(status),
title: Some(title.to_string()),
}
}
#[test]
fn the_parent_is_read_off_the_parent_child_edge_with_its_status_and_title() {
assert_eq!(
tied()["dun-3.1"].parent,
Some(held(
"dun-3",
Edge::ParentChild,
Status::InProgress,
"the root"
))
);
assert_eq!(tied()["dun-3"].parent, None, "the root hangs under nothing");
}
#[test]
fn what_a_bead_depends_on_is_every_edge_but_the_parent() {
assert_eq!(
tied()["dun-3.1"].depends_on,
vec![held(
"dun-3.2",
Edge::Blocks,
Status::Closed,
"the one waited on"
)]
);
assert_eq!(tied()["dun-3"].depends_on, vec![]);
}
#[test]
fn a_dependency_the_answer_does_not_hold_keeps_its_id_and_nothing_else() {
assert_eq!(
tied()["dun-3.2"].depends_on,
vec![Related {
id: "dun-9".to_string(),
edge: Edge::Blocks,
status: None,
title: None,
}]
);
}
#[test]
fn what_a_bead_blocks_is_read_off_the_beads_that_wait_on_it() {
assert_eq!(
tied()["dun-3.2"].blocks,
vec![held(
"dun-3.1",
Edge::Blocks,
Status::Open,
"the waiting one"
)]
);
assert_eq!(
tied()["dun-3"].blocks,
vec![],
"a parent is waited on by nothing; its children hang under it"
);
}
#[test]
fn what_a_bead_blocks_is_in_id_order_however_the_answer_was_ordered() {
let rows = r#"[
{"id":"dun-4","title":"waited on","status":"open"},
{"id":"dun-4.9","title":"last","status":"open",
"dependencies":[{"depends_on_id":"dun-4","type":"blocks"}]},
{"id":"dun-4.1","title":"first","status":"open",
"dependencies":[{"depends_on_id":"dun-4","type":"blocks"}]}
]"#;
let tied = relations(&parse_beads(rows).expect("the rows parse"));
assert_eq!(
tied["dun-4"]
.blocks
.iter()
.map(|r| r.id.as_str())
.collect::<Vec<_>>(),
["dun-4.1", "dun-4.9"]
);
}
#[test]
fn an_edge_of_a_kind_bdi_does_not_know_is_kept_with_its_kind() {
let rows = r#"[
{"id":"dun-5","title":"one","status":"open"},
{"id":"dun-5.1","title":"two","status":"open",
"dependencies":[{"depends_on_id":"dun-5","type":"relates-to"}]}
]"#;
let tied = relations(&parse_beads(rows).expect("the rows parse"));
assert_eq!(
tied["dun-5.1"].depends_on[0].edge,
Edge::Other("relates-to".to_string())
);
assert_eq!(
tied["dun-5"].blocks,
vec![],
"only a blocks edge says the other bead is waited on"
);
}
}