1use icb_common::NodeKind;
2use icb_graph::analysis;
3use icb_graph::graph::{CodePropertyGraph, GraphData};
4use serde_json::json;
5use std::collections::HashSet;
6
7pub fn generate_report(
18 cpg: &CodePropertyGraph,
19 project_name: &str,
20) -> Result<String, anyhow::Error> {
21 let total_functions = cpg
22 .graph
23 .node_weights()
24 .filter(|n| n.kind == NodeKind::Function)
25 .count();
26 let total_classes = cpg
27 .graph
28 .node_weights()
29 .filter(|n| n.kind == NodeKind::Class)
30 .count();
31 let total_calls = cpg
32 .graph
33 .edge_weights()
34 .filter(|e| matches!(e, icb_graph::graph::Edge::Call))
35 .count();
36
37 let cycles = analysis::detect_call_cycles(cpg);
38 let dead = analysis::detect_dead_code(cpg, &["main".to_string()]);
39 let complex = analysis::detect_complex_functions(cpg, 20);
40
41 let mut graph_data = GraphData::from(cpg);
42 for node in &mut graph_data.nodes {
43 let name = node.name.as_deref().unwrap_or("");
44 let is_cycle = cycles
45 .iter()
46 .any(|c| c.functions.contains(&name.to_string()));
47 let is_dead = dead.iter().any(|n| n.name.as_deref() == Some(name));
48 let complexity = complex
49 .iter()
50 .find(|r| r.function_name == name)
51 .map(|r| r.ast_node_count);
52 node.usr = Some(
53 json!({
54 "is_cycle": is_cycle,
55 "is_dead": is_dead,
56 "complexity": complexity,
57 })
58 .to_string(),
59 );
60 }
61
62 let mut seen = HashSet::new();
64 graph_data.edges.retain(|(src, tgt, _)| {
65 let key = (*src, *tgt);
66 if seen.contains(&key) {
67 false
68 } else {
69 seen.insert(key);
70 true
71 }
72 });
73
74 let json_graph = serde_json::to_string(&graph_data)?;
75 let json_stats = json!({
76 "project": project_name,
77 "total_functions": total_functions,
78 "total_classes": total_classes,
79 "total_calls": total_calls,
80 "cycle_count": cycles.len(),
81 "dead_count": dead.len(),
82 "complex_count": complex.len(),
83 })
84 .to_string();
85
86 let html = include_str!("template_report.html")
87 .replace("__GRAPH_DATA__", &json_graph)
88 .replace("__STATS_DATA__", &json_stats)
89 .replace("__PROJECT_NAME__", project_name);
90
91 Ok(html)
92}