1use crate::{Complexity, Coupling, EdgeKind, Graph, NodeId, NodeKind, PluginGraphs};
2use std::collections::{HashMap, HashSet};
3
4pub fn annotate_hk(graphs: &mut PluginGraphs) {
5 annotate_graph_hk(&mut graphs.modules);
6 annotate_graph_hk(&mut graphs.files);
7 annotate_graph_hk(&mut graphs.functions);
8}
9
10fn annotate_graph_hk(graph: &mut Graph) {
11 let has_calls = graph.edges.iter().any(|e| e.kind == EdgeKind::Calls);
15
16 let mut fan_in: HashMap<NodeId, HashSet<NodeId>> = HashMap::new();
17 let mut fan_out: HashMap<NodeId, HashSet<NodeId>> = HashMap::new();
18
19 for edge in &graph.edges {
20 if edge.kind == EdgeKind::Contains {
21 continue;
22 }
23 fan_out
24 .entry(edge.from.clone())
25 .or_default()
26 .insert(edge.to.clone());
27 fan_in
28 .entry(edge.to.clone())
29 .or_default()
30 .insert(edge.from.clone());
31 }
32
33 for node in &mut graph.nodes {
34 if !has_calls && matches!(node.kind, NodeKind::Fn | NodeKind::Method) {
35 continue;
36 }
37 let fi = fan_in.get(&node.id).map(|s| s.len()).unwrap_or(0);
38 let fo = fan_out.get(&node.id).map(|s| s.len()).unwrap_or(0);
39 let loc = node.loc.unwrap_or(0) as f64;
40 let hk_term = ((fi * fo) as f64).powi(2);
41 let hk = if loc > 0.0 { loc * hk_term } else { hk_term };
42
43 let cx = node.complexity.get_or_insert_with(Complexity::default);
44 cx.coupling = Some(Coupling {
45 fan_in: fi as u32,
46 fan_out: fo as u32,
47 hk,
48 });
49 }
50}