lean_ctx/core/graph_analysis/
mod.rs1mod centrality;
9mod cycles;
10mod god_nodes;
11mod surprising;
12
13pub use centrality::{
14 compute_bridge_centrality, compute_bridge_nodes, BridgeCentrality, BridgeNode,
15};
16pub use cycles::{find_import_cycles, ImportCycle};
17pub use god_nodes::{compute_god_nodes, GodNode};
18pub use surprising::{find_surprising_connections, SurprisingConnection};
19
20use crate::core::graph_provider::EdgeInfo;
21
22pub const DEP_EDGE_KINDS: [&str; 2] = ["import", "reexport"];
25
26pub fn is_dependency_kind(kind: &str) -> bool {
28 DEP_EDGE_KINDS.contains(&kind)
29}
30
31pub fn dependency_edges(edges: &[EdgeInfo]) -> Vec<(&str, &str)> {
34 edges
35 .iter()
36 .filter(|e| is_dependency_kind(&e.kind))
37 .filter(|e| e.from != e.to)
38 .map(|e| (e.from.as_str(), e.to.as_str()))
39 .collect()
40}
41
42pub fn edge_confidence(kind: &str, weight: f64) -> f64 {
50 match kind {
51 "import" => 1.0,
52 "reexport" => 0.95,
53 "module" => 0.6,
54 "cochange" => (0.30 + weight.max(0.0).ln_1p() * 0.18).clamp(0.30, 0.85),
56 "co_access" => (0.35 + weight.max(0.0).ln_1p() * 0.16).clamp(0.35, 0.80),
59 "sibling" => 0.25,
60 _ => 0.5,
61 }
62}
63
64#[cfg(test)]
65mod confidence_tests {
66 use super::edge_confidence;
67
68 #[test]
69 fn explicit_refs_rank_above_heuristics() {
70 let import = edge_confidence("import", 0.0);
71 let reexport = edge_confidence("reexport", 0.0);
72 let module = edge_confidence("module", 0.0);
73 let sibling = edge_confidence("sibling", 0.0);
74 assert!(import >= reexport);
75 assert!(reexport > module);
76 assert!(module > sibling);
77 assert!((0.0..=1.0).contains(&sibling));
78 }
79
80 #[test]
81 fn cochange_scales_with_weight_and_is_bounded() {
82 let low = edge_confidence("cochange", 1.0);
83 let high = edge_confidence("cochange", 50.0);
84 assert!(high > low, "more co-changes should raise confidence");
85 assert!((0.30..=0.85).contains(&low));
86 assert!((0.30..=0.85).contains(&high));
87 }
88
89 #[test]
90 fn unknown_kind_is_neutral() {
91 assert_eq!(edge_confidence("mystery", 0.0), 0.5);
92 }
93
94 #[test]
95 fn co_access_scales_with_weight_and_is_bounded() {
96 let low = edge_confidence("co_access", 1.0);
97 let high = edge_confidence("co_access", 50.0);
98 assert!(high > low, "more reinforcement should raise confidence");
99 assert!((0.35..=0.80).contains(&low));
100 assert!((0.35..=0.80).contains(&high));
101 assert!(low > edge_confidence("sibling", 0.0));
104 assert!(high < edge_confidence("import", 0.0));
105 }
106}