use std::collections::{HashMap, HashSet, VecDeque};
use dashmap::DashMap;
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct ModuleDependency {
pub name: String,
pub min_version: Option<String>,
pub optional: bool,
}
#[derive(Debug, Clone)]
pub struct DependencyNode {
pub name: String,
pub dependencies: Vec<ModuleDependency>,
pub dependents: HashSet<String>,
pub loaded: bool,
}
#[derive(Debug)]
pub struct DependencyGraph {
nodes: DashMap<String, DependencyNode>,
}
impl DependencyGraph {
pub fn new() -> Self {
Self {
nodes: DashMap::new(),
}
}
pub fn add_module(&self, name: &str, dependencies: Vec<ModuleDependency>) {
let node = DependencyNode {
name: name.to_string(),
dependencies,
dependents: HashSet::new(),
loaded: false,
};
self.nodes.insert(name.to_string(), node);
}
pub fn mark_loaded(&self, name: &str) {
if let Some(mut node) = self.nodes.get_mut(name) {
node.loaded = true;
}
}
pub fn mark_unloaded(&self, name: &str) {
if let Some(mut node) = self.nodes.get_mut(name) {
node.loaded = false;
}
}
pub fn dependencies_satisfied(&self, name: &str) -> bool {
let node = match self.nodes.get(name) {
Some(n) => n,
None => return false,
};
for dep in &node.dependencies {
if dep.optional {
continue;
}
let dep_node = match self.nodes.get(&dep.name) {
Some(n) => n,
None => return false, };
if !dep_node.loaded {
return false; }
}
true
}
pub fn load_order(&self) -> Result<Vec<String>> {
let mut in_degree: HashMap<String, usize> = HashMap::new();
let mut queue: VecDeque<String> = VecDeque::new();
let mut result: Vec<String> = Vec::new();
for entry in self.nodes.iter() {
let name = entry.key().clone();
let node = entry.value();
let required_deps = node.dependencies.iter().filter(|d| !d.optional).count();
in_degree.insert(name.clone(), required_deps);
if required_deps == 0 {
queue.push_back(name.clone());
}
}
while let Some(name) = queue.pop_front() {
result.push(name.clone());
for entry in self.nodes.iter() {
let node = entry.value();
if node.dependencies.iter().any(|d| d.name == name && !d.optional) {
let in_deg = in_degree.get_mut(entry.key()).unwrap();
*in_deg -= 1;
if *in_deg == 0 {
queue.push_back(entry.key().clone());
}
}
}
}
if result.len() != self.nodes.len() {
return Err(Error::ModuleCallFailed(-1)); }
Ok(result)
}
pub fn unload_order(&self) -> Result<Vec<String>> {
let mut order = self.load_order()?;
order.reverse();
Ok(order)
}
pub fn has_cycle(&self) -> bool {
self.load_order().is_err()
}
pub fn all_dependencies(&self, name: &str) -> HashSet<String> {
let mut result = HashSet::new();
let mut queue: VecDeque<String> = VecDeque::new();
if let Some(node) = self.nodes.get(name) {
for dep in &node.dependencies {
queue.push_back(dep.name.clone());
}
}
while let Some(dep_name) = queue.pop_front() {
if result.contains(&dep_name) {
continue;
}
result.insert(dep_name.clone());
if let Some(dep_node) = self.nodes.get(&dep_name) {
for sub_dep in &dep_node.dependencies {
queue.push_back(sub_dep.name.clone());
}
}
}
result
}
pub fn all_dependents(&self, name: &str) -> HashSet<String> {
let mut result = HashSet::new();
let mut queue: VecDeque<String> = VecDeque::new();
for entry in self.nodes.iter() {
let node = entry.value();
if node.dependencies.iter().any(|d| d.name == name) {
queue.push_back(entry.key().clone());
}
}
while let Some(dep_name) = queue.pop_front() {
if result.contains(&dep_name) {
continue;
}
result.insert(dep_name.clone());
for entry in self.nodes.iter() {
let node = entry.value();
if node.dependencies.iter().any(|d| d.name == dep_name) {
queue.push_back(entry.key().clone());
}
}
}
result
}
pub fn can_unload(&self, name: &str) -> bool {
let dependents = self.all_dependents(name);
for dep_name in &dependents {
if let Some(node) = self.nodes.get(dep_name) {
if node.loaded {
return false; }
}
}
true
}
pub fn module_count(&self) -> usize {
self.nodes.len()
}
pub fn all_modules(&self) -> Vec<String> {
self.nodes.iter().map(|e| e.key().clone()).collect()
}
pub fn get_module(&self, name: &str) -> Option<DependencyNode> {
self.nodes.get(name).map(|e| e.value().clone())
}
}
impl Default for DependencyGraph {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct CallContext {
pub caller: Option<String>,
pub target: String,
pub depth: usize,
pub max_depth: usize,
pub trace_id: Option<u128>,
}
impl CallContext {
pub fn new(target: String) -> Self {
Self {
caller: None,
target,
depth: 0,
max_depth: 10,
trace_id: None,
}
}
pub fn child(&self, target: String) -> Option<Self> {
if self.depth >= self.max_depth {
return None; }
Some(Self {
caller: Some(self.target.clone()),
target,
depth: self.depth + 1,
max_depth: self.max_depth,
trace_id: self.trace_id,
})
}
pub fn is_root(&self) -> bool {
self.caller.is_none()
}
pub fn is_nested(&self) -> bool {
self.caller.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dependency_graph_basic() {
let graph = DependencyGraph::new();
graph.add_module("A", vec![]);
graph.add_module("B", vec![
ModuleDependency { name: "A".to_string(), min_version: None, optional: false }
]);
graph.add_module("C", vec![
ModuleDependency { name: "B".to_string(), min_version: None, optional: false }
]);
let order = graph.load_order().unwrap();
assert_eq!(order, vec!["A", "B", "C"]);
let order = graph.unload_order().unwrap();
assert_eq!(order, vec!["C", "B", "A"]);
}
#[test]
fn test_dependency_graph_optional() {
let graph = DependencyGraph::new();
graph.add_module("A", vec![]);
graph.add_module("B", vec![
ModuleDependency { name: "A".to_string(), min_version: None, optional: true }
]);
graph.mark_loaded("B");
assert!(graph.dependencies_satisfied("B"));
}
#[test]
fn test_dependency_graph_cycle_detection() {
let graph = DependencyGraph::new();
graph.add_module("A", vec![
ModuleDependency { name: "B".to_string(), min_version: None, optional: false }
]);
graph.add_module("B", vec![
ModuleDependency { name: "A".to_string(), min_version: None, optional: false }
]);
assert!(graph.has_cycle());
assert!(graph.load_order().is_err());
}
#[test]
fn test_call_context_depth() {
let root = CallContext::new("A".to_string());
assert_eq!(root.depth, 0);
assert!(root.is_root());
let child1 = root.child("B".to_string()).unwrap();
assert_eq!(child1.depth, 1);
assert!(child1.is_nested());
let child2 = child1.child("C".to_string()).unwrap();
assert_eq!(child2.depth, 2);
let mut ctx = root;
for i in 0..10 {
if let Some(next) = ctx.child(format!("M{}", i)) {
ctx = next;
}
}
assert!(ctx.child("M10".to_string()).is_none());
}
#[test]
fn test_transitive_dependencies() {
let graph = DependencyGraph::new();
graph.add_module("A", vec![]);
graph.add_module("B", vec![
ModuleDependency { name: "A".to_string(), min_version: None, optional: false }
]);
graph.add_module("C", vec![
ModuleDependency { name: "B".to_string(), min_version: None, optional: false }
]);
let deps = graph.all_dependencies("C");
assert!(deps.contains("A"));
assert!(deps.contains("B"));
assert_eq!(deps.len(), 2);
}
#[test]
fn test_can_unload() {
let graph = DependencyGraph::new();
graph.add_module("A", vec![]);
graph.add_module("B", vec![
ModuleDependency { name: "A".to_string(), min_version: None, optional: false }
]);
graph.mark_loaded("A");
graph.mark_loaded("B");
assert!(!graph.can_unload("A"));
assert!(graph.can_unload("B"));
graph.mark_unloaded("B");
assert!(graph.can_unload("A"));
}
}