use crate::error::MutationError;
use ankurah_proto::{Attested, Event, EventId};
use std::collections::{BTreeMap, BTreeSet, VecDeque};
pub(crate) fn topo_sort_events(events: Vec<Attested<Event>>) -> Result<Vec<Attested<Event>>, MutationError> {
let mut by_id: BTreeMap<EventId, Attested<Event>> = BTreeMap::new();
for event in events {
by_id.entry(event.payload.id()).or_insert(event);
}
let mut indegree: BTreeMap<EventId, usize> = BTreeMap::new();
let mut children: BTreeMap<EventId, Vec<EventId>> = BTreeMap::new();
for (id, event) in &by_id {
indegree.entry(id.clone()).or_insert(0);
let parents: BTreeSet<&EventId> = event.payload.parent.as_slice().iter().filter(|p| by_id.contains_key(*p)).collect();
for parent in parents {
*indegree.entry(id.clone()).or_insert(0) += 1;
children.entry(parent.clone()).or_default().push(id.clone());
}
}
let mut queue: VecDeque<EventId> = indegree.iter().filter(|(_, d)| **d == 0).map(|(id, _)| id.clone()).collect();
let mut sorted: Vec<Attested<Event>> = Vec::with_capacity(by_id.len());
while let Some(id) = queue.pop_front() {
if let Some(kids) = children.get(&id) {
for kid in kids {
let degree = indegree.get_mut(kid).expect("child in-degree was initialized");
*degree -= 1;
if *degree == 0 {
queue.push_back(kid.clone());
}
}
}
sorted.push(by_id.remove(&id).expect("queued id is present"));
}
if !by_id.is_empty() {
return Err(MutationError::InvalidUpdate("event batch contains a parent cycle"));
}
Ok(sorted)
}