1use petgraph::graph::{DiGraph, NodeIndex};
6use petgraph::visit::EdgeRef;
7use std::collections::HashMap;
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
12pub struct DependencyGraph {
13 graph: DiGraph<Arc<str>, EdgeKind>,
14 node_map: HashMap<Arc<str>, NodeIndex>,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum EdgeKind {
19 Calls,
20 Inherits,
21 Uses,
22 Implements,
23}
24
25impl DependencyGraph {
26 pub fn new() -> Self {
27 Self {
28 graph: DiGraph::new(),
29 node_map: HashMap::new(),
30 }
31 }
32
33 pub fn add_node(&mut self, name: Arc<str>) -> NodeIndex {
35 if let Some(&idx) = self.node_map.get(&name) {
36 return idx;
37 }
38
39 let idx = self.graph.add_node(name.clone());
40 self.node_map.insert(name, idx);
41 idx
42 }
43
44 pub fn add_edge(&mut self, from: Arc<str>, to: Arc<str>, kind: EdgeKind) {
46 let from_idx = self.add_node(from);
47 let to_idx = self.add_node(to);
48 self.graph.add_edge(from_idx, to_idx, kind);
49 }
50
51 pub fn coupling_metrics(&self, node: &Arc<str>) -> Option<CouplingMetrics> {
53 let idx = *self.node_map.get(node)?;
54
55 let incoming = self
56 .graph
57 .edges_directed(idx, petgraph::Direction::Incoming)
58 .count();
59
60 let outgoing = self
61 .graph
62 .edges_directed(idx, petgraph::Direction::Outgoing)
63 .count();
64
65 Some(CouplingMetrics {
66 afferent: incoming,
67 efferent: outgoing,
68 instability: if incoming + outgoing > 0 {
69 outgoing as f64 / (incoming + outgoing) as f64
70 } else {
71 0.0
72 },
73 })
74 }
75
76 pub fn find_cycles(&self) -> Vec<Vec<Arc<str>>> {
78 let sccs = petgraph::algo::kosaraju_scc(&self.graph);
79
80 sccs.into_iter()
81 .filter(|scc| scc.len() > 1)
82 .map(|scc| {
83 scc.into_iter()
84 .map(|idx| self.graph[idx].clone())
85 .collect()
86 })
87 .collect()
88 }
89
90 pub fn density(&self) -> f64 {
92 let n = self.graph.node_count();
93 let e = self.graph.edge_count();
94
95 if n <= 1 {
96 0.0
97 } else {
98 e as f64 / (n * (n - 1)) as f64
99 }
100 }
101}
102
103impl Default for DependencyGraph {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct CouplingMetrics {
112 pub afferent: usize, pub efferent: usize, pub instability: f64, }
116