use std::collections::BTreeMap;
use vcs_graph::tsort::TopoSorter;
pub type Key = Vec<Vec<u8>>;
pub fn sort_gc_optimal(parent_map: Vec<(Key, Vec<Key>)>) -> Vec<Key> {
let mut per_prefix: BTreeMap<Vec<u8>, Vec<(Key, Vec<Key>)>> = BTreeMap::new();
for (key, parents) in parent_map {
let prefix = if key.len() <= 1 {
Vec::new()
} else {
key[0].clone()
};
per_prefix.entry(prefix).or_default().push((key, parents));
}
let mut out = Vec::new();
for (_prefix, bucket) in per_prefix {
let mut sorter = TopoSorter::new(bucket.into_iter());
let sorted = sorter
.sorted()
.expect("groupcompress parent_map should not contain cycles");
out.extend(sorted.into_iter().rev());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn key(parts: &[&[u8]]) -> Key {
parts.iter().map(|p| p.to_vec()).collect()
}
#[test]
fn empty() {
assert!(sort_gc_optimal(vec![]).is_empty());
}
#[test]
fn single_prefix_reverse_topo() {
let a = key(&[b"f1", b"a"]);
let b = key(&[b"f1", b"b"]);
let c = key(&[b"f1", b"c"]);
let parent_map = vec![
(a.clone(), vec![]),
(b.clone(), vec![a.clone()]),
(c.clone(), vec![b.clone()]),
];
let out = sort_gc_optimal(parent_map);
assert_eq!(out, vec![c, b, a]);
}
#[test]
fn multi_prefix_grouped_by_first_segment() {
let f1a = key(&[b"f1", b"a"]);
let f2a = key(&[b"f2", b"a"]);
let parent_map = vec![(f2a.clone(), vec![]), (f1a.clone(), vec![])];
let out = sort_gc_optimal(parent_map);
assert_eq!(out, vec![f1a, f2a]);
}
#[test]
fn single_element_keys_share_empty_prefix() {
let a = key(&[b"a"]);
let b = key(&[b"b"]);
let parent_map = vec![(a.clone(), vec![]), (b.clone(), vec![a.clone()])];
let out = sort_gc_optimal(parent_map);
assert_eq!(out, vec![b, a]);
}
}