use crate::metadata::EntityTypeMeta;
use std::any::TypeId;
use std::collections::{HashMap, VecDeque};
pub struct DependencyGraph {
edges: HashMap<TypeId, Vec<TypeId>>,
nodes: Vec<TypeId>,
}
impl DependencyGraph {
pub fn build(metas: &HashMap<TypeId, EntityTypeMeta>) -> Self {
let mut edges: HashMap<TypeId, Vec<TypeId>> = HashMap::new();
let mut nodes: Vec<TypeId> = Vec::new();
for (type_id, meta) in metas {
nodes.push(*type_id);
for nav in &meta.navigations {
if matches!(
nav.kind,
crate::metadata::NavigationKind::HasMany
| crate::metadata::NavigationKind::ManyToMany
) {
edges.entry(nav.related_type_id).or_default().push(*type_id);
}
}
}
Self { edges, nodes }
}
pub fn topological_sort(&self) -> Vec<TypeId> {
let mut in_degree: HashMap<TypeId, usize> = HashMap::new();
for node in &self.nodes {
in_degree.entry(*node).or_insert(0);
}
for (child, parents) in &self.edges {
let count = parents.iter().filter(|p| **p != *child).count();
*in_degree.entry(*child).or_insert(0) += count;
}
let mut queue: VecDeque<TypeId> = in_degree
.iter()
.filter(|(_, °)| deg == 0)
.map(|(&k, _)| k)
.collect();
let mut result: Vec<TypeId> = Vec::new();
while let Some(node) = queue.pop_front() {
result.push(node);
for (child, parents) in &self.edges {
if parents.iter().any(|p| *p == node && *p != *child) {
let deg = in_degree.entry(*child).or_insert(0);
if *deg > 0 {
*deg -= 1;
}
if *deg == 0 && !result.contains(child) && !queue.contains(child) {
queue.push_back(*child);
}
}
}
}
for node in &self.nodes {
if !result.contains(node) {
result.push(*node);
}
}
result
}
pub fn deletion_order(&self) -> Vec<TypeId> {
let mut order = self.topological_sort();
order.reverse();
order
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph_returns_empty() {
let metas: HashMap<TypeId, EntityTypeMeta> = HashMap::new();
let graph = DependencyGraph::build(&metas);
assert!(graph.topological_sort().is_empty());
}
#[test]
fn deletion_order_is_reverse_of_insert() {
let metas: HashMap<TypeId, EntityTypeMeta> = HashMap::new();
let graph = DependencyGraph::build(&metas);
let insert = graph.topological_sort();
let delete = graph.deletion_order();
assert_eq!(delete, insert.into_iter().rev().collect::<Vec<_>>());
}
}