use crate::dag::graph::{DagError, PatchDag};
use crate::patch::types::PatchId;
use suture_common::BranchName;
impl PatchDag {
pub fn branch_exists(&self, name: &BranchName) -> bool {
self.branches.contains_key(name.as_str())
}
pub fn head(&self) -> Option<(String, PatchId)> {
self.branches
.get("main")
.map(|id| ("main".to_string(), *id))
.or_else(|| {
self.branches
.iter()
.next()
.map(|(name, id)| (name.clone(), *id))
})
}
pub fn branch_divergence(
&self,
branch_a: &BranchName,
branch_b: &BranchName,
) -> Result<(usize, usize), DagError> {
let target_a = self
.get_branch(branch_a)
.ok_or_else(|| DagError::BranchNotFound(branch_a.as_str().to_string()))?;
let target_b = self
.get_branch(branch_b)
.ok_or_else(|| DagError::BranchNotFound(branch_b.as_str().to_string()))?;
let lca_id = self
.lca(&target_a, &target_b)
.ok_or_else(|| DagError::Custom("no common ancestor found".to_string()))?;
let chain_a = self.patch_chain(&target_a);
let chain_b = self.patch_chain(&target_b);
let ahead = match chain_a.iter().position(|id| *id == lca_id) {
Some(pos) => pos,
None => chain_a.len(),
};
let behind = match chain_b.iter().position(|id| *id == lca_id) {
Some(pos) => pos,
None => chain_b.len(),
};
Ok((ahead, behind))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dag::graph::PatchDag;
use crate::patch::types::{OperationType, Patch, TouchSet};
fn make_patch(addr: &str) -> Patch {
Patch::new(
OperationType::Modify,
TouchSet::single(addr),
None,
vec![],
vec![],
"test".to_string(),
format!("edit {}", addr),
)
}
#[test]
fn test_branch_exists() {
let mut dag = PatchDag::new();
let root = make_patch("root");
let root_id = dag.add_patch(root, vec![]).unwrap();
let main = BranchName::new("main").unwrap();
dag.create_branch(main.clone(), root_id).unwrap();
assert!(dag.branch_exists(&main));
assert!(!dag.branch_exists(&BranchName::new("nonexistent").unwrap()));
}
#[test]
fn test_head() {
let mut dag = PatchDag::new();
let root = make_patch("root");
let root_id = dag.add_patch(root, vec![]).unwrap();
assert!(dag.head().is_none());
let main = BranchName::new("main").unwrap();
dag.create_branch(main.clone(), root_id).unwrap();
let head = dag.head().unwrap();
assert_eq!(head.0, "main");
assert_eq!(head.1, root_id);
}
#[test]
fn test_branch_divergence() {
let mut dag = PatchDag::new();
let root = make_patch("root");
let root_id = dag.add_patch(root, vec![]).unwrap();
let main = BranchName::new("main").unwrap();
dag.create_branch(main.clone(), root_id).unwrap();
let mc = make_patch("main_commit");
let mc_id = dag.add_patch(mc, vec![root_id]).unwrap();
dag.update_branch(&main, mc_id).unwrap();
let feat = BranchName::new("feature").unwrap();
dag.create_branch(feat.clone(), root_id).unwrap();
let fc = make_patch("feat_commit");
let fc_id = dag.add_patch(fc, vec![root_id]).unwrap();
dag.update_branch(&feat, fc_id).unwrap();
let (ahead, behind) = dag.branch_divergence(&main, &feat).unwrap();
assert_eq!(ahead, 1, "main should be 1 ahead of feature");
assert_eq!(behind, 1, "main should be 1 behind feature");
}
}