#![allow(dead_code)]
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug, Default, Clone)]
pub struct DependencyResolverV2 {
nodes: HashSet<String>,
deps: HashMap<String, Vec<String>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResolveResult {
Order(Vec<String>),
Cycle(Vec<String>),
}
impl DependencyResolverV2 {
pub fn new() -> Self {
DependencyResolverV2 { nodes: HashSet::new(), deps: HashMap::new() }
}
pub fn add_node(&mut self, node: impl Into<String>) {
let n = node.into();
self.nodes.insert(n.clone());
self.deps.entry(n).or_default();
}
pub fn add_dep(&mut self, node: impl Into<String>, dep: impl Into<String>) {
let n = node.into();
let d = dep.into();
self.nodes.insert(n.clone());
self.nodes.insert(d.clone());
self.deps.entry(d.clone()).or_default();
self.deps.entry(n).or_default().push(d);
}
pub fn resolve(&self) -> ResolveResult {
let mut in_degree: HashMap<&str, usize> = self.nodes.iter().map(|n| (n.as_str(), 0)).collect();
let mut adj: HashMap<&str, Vec<&str>> = self.nodes.iter().map(|n| (n.as_str(), vec![])).collect();
for (node, deps) in &self.deps {
for dep in deps {
*in_degree.entry(node.as_str()).or_insert(0) += 1;
adj.entry(dep.as_str()).or_default().push(node.as_str());
}
}
let mut queue: VecDeque<&str> =
in_degree.iter().filter(|(_, &d)| d == 0).map(|(&n, _)| n).collect();
let mut order = Vec::new();
while let Some(n) = queue.pop_front() {
order.push(n.to_owned());
if let Some(dependents) = adj.get(n) {
for &dep in dependents {
let Some(cnt) = in_degree.get_mut(dep) else { continue };
*cnt = cnt.saturating_sub(1);
if *cnt == 0 {
queue.push_back(dep);
}
}
}
}
if order.len() == self.nodes.len() {
ResolveResult::Order(order)
} else {
let in_cycle: Vec<String> = self
.nodes
.iter()
.filter(|n| !order.contains(&n.to_string()))
.cloned()
.collect();
ResolveResult::Cycle(in_cycle)
}
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn edge_count(&self) -> usize {
self.deps.values().map(|v| v.len()).sum()
}
}
pub fn new_dependency_resolver_v2() -> DependencyResolverV2 {
DependencyResolverV2::new()
}
pub fn dr_add_node(r: &mut DependencyResolverV2, node: &str) {
r.add_node(node);
}
pub fn dr_add_dep(r: &mut DependencyResolverV2, node: &str, dep: &str) {
r.add_dep(node, dep);
}
pub fn dr_resolve(r: &DependencyResolverV2) -> ResolveResult {
r.resolve()
}
pub fn dr_node_count(r: &DependencyResolverV2) -> usize {
r.node_count()
}
pub fn dr_edge_count(r: &DependencyResolverV2) -> usize {
r.edge_count()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_node() {
let mut r = new_dependency_resolver_v2();
dr_add_node(&mut r, "A");
match dr_resolve(&r) {
ResolveResult::Order(o) => assert_eq!(o, vec!["A"] ),
_ => panic!("expected order"),
}
}
#[test]
fn test_chain() {
let mut r = new_dependency_resolver_v2();
dr_add_dep(&mut r, "B", "A");
dr_add_dep(&mut r, "C", "B");
match dr_resolve(&r) {
ResolveResult::Order(o) => {
assert!(o.iter().position(|x| x == "A") < o.iter().position(|x| x == "B") );
assert!(o.iter().position(|x| x == "B") < o.iter().position(|x| x == "C"));
}
_ => panic!("expected order"),
}
}
#[test]
fn test_cycle_detection() {
let mut r = new_dependency_resolver_v2();
dr_add_dep(&mut r, "A", "B");
dr_add_dep(&mut r, "B", "A");
match dr_resolve(&r) {
ResolveResult::Cycle(_) => { }
_ => panic!("expected cycle"),
}
}
#[test]
fn test_node_count() {
let mut r = new_dependency_resolver_v2();
dr_add_node(&mut r, "X");
dr_add_node(&mut r, "Y");
assert_eq!(dr_node_count(&r), 2 );
}
#[test]
fn test_edge_count() {
let mut r = new_dependency_resolver_v2();
dr_add_dep(&mut r, "B", "A");
assert_eq!(dr_edge_count(&r), 1 );
}
#[test]
fn test_diamond() {
let mut r = new_dependency_resolver_v2();
dr_add_dep(&mut r, "B", "A");
dr_add_dep(&mut r, "C", "A");
dr_add_dep(&mut r, "D", "B");
dr_add_dep(&mut r, "D", "C");
match dr_resolve(&r) {
ResolveResult::Order(o) => {
let ai = o.iter().position(|x| x == "A").expect("should succeed");
let di = o.iter().position(|x| x == "D").expect("should succeed");
assert!(ai < di );
}
_ => panic!("expected order"),
}
}
#[test]
fn test_empty_resolver() {
let r = new_dependency_resolver_v2();
match dr_resolve(&r) {
ResolveResult::Order(o) => assert!(o.is_empty() ),
_ => panic!("expected empty order"),
}
}
#[test]
fn test_no_deps() {
let mut r = new_dependency_resolver_v2();
dr_add_node(&mut r, "P");
dr_add_node(&mut r, "Q");
match dr_resolve(&r) {
ResolveResult::Order(o) => assert_eq!(o.len(), 2 ),
_ => panic!("expected order"),
}
}
#[test]
fn test_auto_register_deps() {
let mut r = new_dependency_resolver_v2();
dr_add_dep(&mut r, "B", "A");
assert_eq!(dr_node_count(&r), 2 );
}
}