use crate::meta;
use anyhow::{bail, Result};
use std::collections::HashMap;
const MAX_DEPTH: usize = 1000;
pub struct Queue {
pub trunk: String,
parents: HashMap<String, String>,
}
impl Queue {
pub fn load() -> Result<Queue> {
let trunk = meta::trunk()?;
let mut parents = HashMap::new();
for b in meta::tracked_branches() {
if let Some(p) = meta::parent(&b) {
parents.insert(b, p);
}
}
Ok(Queue { trunk, parents })
}
pub fn is_tracked(&self, branch: &str) -> bool {
self.parents.contains_key(branch)
}
pub fn parent_of(&self, branch: &str) -> Option<&str> {
self.parents.get(branch).map(|s| s.as_str())
}
pub fn children(&self, branch: &str) -> Vec<String> {
let mut kids: Vec<String> = self
.parents
.iter()
.filter(|(_, p)| p.as_str() == branch)
.map(|(c, _)| c.clone())
.collect();
kids.sort();
kids
}
pub fn roots(&self) -> Vec<String> {
let mut roots: Vec<String> = self
.parents
.iter()
.filter(|(_, p)| !self.parents.contains_key(*p))
.map(|(b, _)| b.clone())
.collect();
roots.sort();
roots
}
pub fn leaves(&self) -> Vec<String> {
let mut leaves: Vec<String> = self
.parents
.keys()
.filter(|b| self.children(b).is_empty())
.cloned()
.collect();
leaves.sort();
leaves
}
pub fn bases(&self) -> Vec<String> {
let mut bases: Vec<String> = self
.parents
.values()
.filter(|p| !self.parents.contains_key(*p))
.cloned()
.collect();
bases.sort();
bases.dedup();
bases
}
pub fn chain_to_base(&self, branch: &str) -> Result<Vec<String>> {
let mut chain = vec![branch.to_string()];
let mut cur = branch.to_string();
for _ in 0..MAX_DEPTH {
match self.parents.get(&cur) {
Some(p) if !self.parents.contains_key(p) => {
chain.reverse();
return Ok(chain);
}
Some(p) => {
chain.push(p.clone());
cur = p.clone();
}
None => bail!("`{branch}` is not a tracked queue branch"),
}
}
bail!("parent chain for `{branch}` is too deep or cyclic");
}
pub fn line_through(&self, branch: &str) -> Result<Line> {
let mut branches = self.chain_to_base(branch)?;
let base = self
.parent_of(&branches[0])
.expect("bottom branch is tracked, so it has a parent")
.to_string();
let mut fork_at = None;
loop {
let top = branches.last().unwrap().clone();
let kids = self.children(&top);
match kids.len() {
0 => break,
1 => branches.push(kids.into_iter().next().unwrap()),
_ => {
fork_at = Some(top);
break;
}
}
}
Ok(Line {
branches,
base,
fork_at,
})
}
pub fn descendants_topo(&self, branch: &str) -> Vec<String> {
let mut out = Vec::new();
let mut frontier = self.children(branch);
while let Some(b) = frontier.pop() {
out.push(b.clone());
frontier.extend(self.children(&b));
}
out.sort_by_key(|b| (self.depth(b), b.clone()));
out.dedup();
out
}
pub fn leaves_under(&self, branch: &str) -> Vec<String> {
let mut leaves = Vec::new();
let mut frontier = vec![branch.to_string()];
while let Some(b) = frontier.pop() {
let kids = self.children(&b);
if kids.is_empty() {
leaves.push(b);
} else {
frontier.extend(kids);
}
}
leaves.sort();
leaves.dedup();
leaves
}
fn depth(&self, branch: &str) -> usize {
self.chain_to_base(branch)
.map(|c| c.len())
.unwrap_or(usize::MAX)
}
pub fn topo_order(&self) -> Vec<String> {
let mut branches: Vec<String> = self.parents.keys().cloned().collect();
branches.sort_by_key(|b| (self.depth(b), b.clone()));
branches
}
}
pub struct Line {
pub branches: Vec<String>,
pub base: String,
pub fork_at: Option<String>,
}