codemem_engine/enrichment/
hot_complex.rs1use super::EnrichResult;
4use crate::CodememEngine;
5use codemem_core::{CodememError, NodeKind};
6use serde_json::json;
7use std::collections::HashMap;
8
9impl CodememEngine {
10 pub fn enrich_hot_complex(
15 &self,
16 namespace: Option<&str>,
17 ) -> Result<EnrichResult, CodememError> {
18 let all_nodes = {
19 let graph = self.lock_graph()?;
20 graph.get_all_nodes()
21 };
22
23 let mut file_churn: HashMap<String, f64> = HashMap::new();
25 for node in &all_nodes {
26 if node.kind != NodeKind::File {
27 continue;
28 }
29 if let Some(churn) = node.payload.get("git_churn_rate").and_then(|v| v.as_f64()) {
30 if churn > 0.0 {
31 file_churn.insert(node.label.clone(), churn);
32 }
33 }
34 }
35
36 let mut file_max_complexity: HashMap<String, (usize, String)> = HashMap::new();
38 for node in &all_nodes {
39 if !matches!(node.kind, NodeKind::Function | NodeKind::Method) {
40 continue;
41 }
42 let cyclomatic = node
43 .payload
44 .get("cyclomatic_complexity")
45 .and_then(|v| v.as_u64())
46 .unwrap_or(0) as usize;
47 if cyclomatic <= 5 {
48 continue;
49 }
50 let file_path = match node.payload.get("file_path").and_then(|v| v.as_str()) {
51 Some(fp) => fp.to_string(),
52 None => continue,
53 };
54 let entry = file_max_complexity
55 .entry(file_path)
56 .or_insert((0, String::new()));
57 if cyclomatic > entry.0 {
58 *entry = (cyclomatic, node.label.clone());
59 }
60 }
61
62 let mut insights_stored = 0;
63 let mut hot_complex_files: Vec<serde_json::Value> = Vec::new();
64
65 for (file_path, churn) in &file_churn {
66 if let Some((complexity, fn_name)) = file_max_complexity.get(file_path) {
67 hot_complex_files.push(json!({
69 "file": file_path,
70 "churn_rate": churn,
71 "max_complexity": complexity,
72 "complex_function": fn_name,
73 }));
74
75 let content = format!(
76 "High-risk file: {} — churn rate {:.1} + max cyclomatic complexity {} (in {}). \
77 Prioritize refactoring",
78 file_path, churn, complexity, fn_name
79 );
80 if self
81 .store_insight(
82 &content,
83 "risk",
84 &["hot-complex"],
85 0.9,
86 namespace,
87 &[format!("file:{file_path}")],
88 )
89 .is_some()
90 {
91 insights_stored += 1;
92 }
93 }
94 }
95
96 self.save_index();
97
98 Ok(EnrichResult {
99 insights_stored,
100 details: json!({
101 "hot_complex_files": hot_complex_files.len(),
102 "files_with_churn": file_churn.len(),
103 "files_with_complexity": file_max_complexity.len(),
104 "insights_stored": insights_stored,
105 }),
106 })
107 }
108}