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