use std::collections::HashMap;
use std::path::PathBuf;
pub(crate) fn toposort(adjacency: &HashMap<u32, Vec<u32>>) -> Result<Vec<u32>, Vec<u32>> {
use safegraph::algo::toposort::toposort as safegraph_toposort;
use safegraph::graph::Graph;
use safegraph::BTreeGraph;
let mut g = BTreeGraph::<u32, u32>::default();
for &id in adjacency.keys() {
g.insert_node(id).unwrap();
}
let mut edge_id = 0u32;
for (&from, deps) in adjacency {
for &to in deps {
g.push_edge(edge_id, [to, from]).unwrap();
edge_id += 1;
}
}
match safegraph_toposort(&g) {
Ok(order) => Ok(order),
Err(err) => Err(find_cycle(adjacency, err.node)),
}
}
pub(crate) fn header_order_toposort(
adjacency: &HashMap<u32, Vec<u32>>,
entry_id: u32,
) -> Result<Vec<u32>, Vec<u32>> {
#[derive(Clone, Copy, PartialEq)]
enum State {
InProgress,
Done,
}
let mut state: HashMap<u32, State> = HashMap::new();
let mut order: Vec<u32> = Vec::new();
let mut roots: Vec<u32> = vec![entry_id];
roots.extend(adjacency.keys().copied().filter(|&k| k != entry_id));
for root in roots {
if state.contains_key(&root) {
continue;
}
let mut stack: Vec<(u32, usize)> = vec![(root, 0)];
state.insert(root, State::InProgress);
while let Some(&(node, child_idx)) = stack.last() {
let children = adjacency.get(&node).map(Vec::as_slice).unwrap_or(&[]);
if child_idx < children.len() {
stack.last_mut().unwrap().1 += 1;
let child = children[child_idx];
match state.get(&child).copied() {
Some(State::Done) => {}
Some(State::InProgress) => {
return Err(find_cycle(adjacency, child));
}
None => {
state.insert(child, State::InProgress);
stack.push((child, 0));
}
}
} else {
order.push(node);
state.insert(node, State::Done);
stack.pop();
}
}
}
Ok(order)
}
fn find_cycle(adjacency: &HashMap<u32, Vec<u32>>, start: u32) -> Vec<u32> {
#[derive(Clone, Copy, PartialEq)]
enum State {
Unvisited,
InProgress,
Done,
}
fn dfs(
node: u32,
adjacency: &HashMap<u32, Vec<u32>>,
state: &mut HashMap<u32, State>,
stack: &mut Vec<u32>,
) -> Option<Vec<u32>> {
state.insert(node, State::InProgress);
stack.push(node);
if let Some(children) = adjacency.get(&node) {
for &child in children {
match state.get(&child).copied().unwrap_or(State::Unvisited) {
State::Unvisited => {
if let Some(cycle) = dfs(child, adjacency, state, stack) {
return Some(cycle);
}
}
State::InProgress => {
let pos = stack.iter().position(|&n| n == child).unwrap();
let mut cycle: Vec<u32> = stack[pos..].to_vec();
cycle.push(child);
return Some(cycle);
}
State::Done => {}
}
}
}
stack.pop();
state.insert(node, State::Done);
None
}
let mut state = HashMap::new();
let mut stack = Vec::new();
dfs(start, adjacency, &mut state, &mut stack).unwrap_or_else(|| vec![start])
}
pub(crate) fn chain_to_paths(chain: &[u32], path_of: &HashMap<u32, PathBuf>) -> Vec<PathBuf> {
chain.iter().map(|id| path_of[id].clone()).collect()
}