magellan 4.8.0

Deterministic codebase mapping tool for local development
Documentation
use crate::temporal::scc::SnapshotScc;
use std::collections::BTreeMap;

#[derive(Debug, Clone, serde::Serialize)]
pub struct TemporalSccPoint {
    pub snapshot_id: i64,
    pub commit_oid: String,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct TemporalSccBarcode {
    pub lineage_key: String,
    pub member_count: usize,
    pub members: Vec<String>,
    pub snapshot_count: usize,
    pub born_commit_oid: Option<String>,
    pub died_commit_oid: Option<String>,
    pub lifetime_length: usize,
    pub churn_count: usize,
    pub snapshots: Vec<TemporalSccPoint>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct TemporalSccBarcodeReport {
    pub count: usize,
    pub sccs: Vec<TemporalSccBarcode>,
}

#[derive(Debug, Default)]
struct LineageAccumulator {
    members: Vec<String>,
    snapshots: Vec<TemporalSccPoint>,
    segment_count: usize,
    last_snapshot_id: Option<i64>,
}

pub fn lineage_key(members: &[String]) -> String {
    members.join("|")
}

pub fn build_scc_lineages(snapshot_sccs: &[SnapshotScc]) -> TemporalSccBarcodeReport {
    let mut accumulators: BTreeMap<String, LineageAccumulator> = BTreeMap::new();

    for scc in snapshot_sccs {
        let key = lineage_key(&scc.members);
        let entry = accumulators
            .entry(key)
            .or_insert_with(|| LineageAccumulator {
                members: scc.members.clone(),
                ..LineageAccumulator::default()
            });

        let starts_new_segment = entry
            .last_snapshot_id
            .map(|last_snapshot_id| last_snapshot_id + 1 != scc.snapshot_id)
            .unwrap_or(true);
        if starts_new_segment {
            entry.segment_count += 1;
        }

        entry.snapshots.push(TemporalSccPoint {
            snapshot_id: scc.snapshot_id,
            commit_oid: scc.commit_oid.clone(),
        });
        entry.last_snapshot_id = Some(scc.snapshot_id);
    }

    let mut sccs: Vec<TemporalSccBarcode> = accumulators
        .into_iter()
        .map(|(key, entry)| TemporalSccBarcode {
            lineage_key: key,
            member_count: entry.members.len(),
            members: entry.members,
            snapshot_count: entry.snapshots.len(),
            born_commit_oid: entry
                .snapshots
                .first()
                .map(|point| point.commit_oid.clone()),
            died_commit_oid: entry.snapshots.last().map(|point| point.commit_oid.clone()),
            lifetime_length: entry.snapshots.len(),
            churn_count: entry.segment_count.saturating_sub(1),
            snapshots: entry.snapshots,
        })
        .collect();
    sccs.sort_by(|a, b| a.lineage_key.cmp(&b.lineage_key));

    TemporalSccBarcodeReport {
        count: sccs.len(),
        sccs,
    }
}