use std::collections::{HashMap, HashSet};
use super::types::*;
#[derive(Debug, Clone)]
pub struct LayeredGraph {
pub layers: Vec<Vec<String>>,
}
pub fn assign_layers(diagram: &MermaidDiagram) -> LayeredGraph {
if diagram.nodes.is_empty() {
return LayeredGraph { layers: Vec::new() };
}
let mut in_degree: HashMap<&str, usize> = HashMap::new();
let mut adj: HashMap<&str, Vec<&str>> = HashMap::new();
let mut layer_map: HashMap<&str, usize> = HashMap::new();
for node in &diagram.nodes {
in_degree.insert(&node.id, 0);
adj.insert(&node.id, Vec::new());
layer_map.insert(&node.id, 0);
}
let mut seen_edges: HashSet<(&str, &str)> = HashSet::new();
for edge in &diagram.edges {
if seen_edges.contains(&(&edge.source, &edge.target)) {
continue;
}
seen_edges.insert((&edge.source, &edge.target));
if let Some(deg) = in_degree.get_mut(edge.target.as_str()) {
*deg += 1;
}
if let Some(neighbors) = adj.get_mut(edge.source.as_str()) {
neighbors.push(&edge.target);
}
}
let mut queue: Vec<&str> = in_degree
.iter()
.filter(|(_, &d)| d == 0)
.map(|(&id, _)| id)
.collect();
let mut processed = 0usize;
while let Some(id) = queue.pop() {
processed += 1;
if let Some(neighbors) = adj.get(id) {
for &target in neighbors {
let parent_layer = *layer_map.get(id).unwrap_or(&0);
let current = *layer_map.get(target).unwrap_or(&0);
if parent_layer + 1 > current {
layer_map.insert(target, parent_layer + 1);
}
if let Some(deg) = in_degree.get_mut(target) {
*deg = deg.saturating_sub(1);
if *deg == 0 {
queue.push(target);
}
}
}
}
}
if processed < diagram.nodes.len() {
for node in &diagram.nodes {
if !layer_map.contains_key(node.id.as_str()) {
layer_map.insert(&node.id, 0);
}
}
}
let max_layer = layer_map.values().copied().max().unwrap_or(0);
let mut layers: Vec<Vec<String>> = vec![Vec::new(); max_layer + 1];
for node in &diagram.nodes {
let l = *layer_map.get(&node.id[..]).unwrap_or(&0);
layers[l].push(node.id.clone());
}
for layer in &mut layers {
layer.sort();
}
while layers.last().is_some_and(|l| l.is_empty()) {
layers.pop();
}
LayeredGraph { layers }
}