impl EnhancedReportingService {
fn create_visualizations(
&self,
results: &AnalysisResults,
sections: &[ReportSection],
) -> Result<Vec<Visualization>> {
let mut visualizations = Vec::new();
if let Some(complexity) = &results.complexity_analysis {
visualizations.push(self.create_complexity_distribution_chart(complexity)?);
}
let health_score = self.calculate_health_score(results);
visualizations.push(self.create_health_score_gauge(health_score)?);
visualizations.push(self.create_issue_distribution_chart(sections)?);
Ok(visualizations)
}
fn create_complexity_distribution_chart(
&self,
complexity: &ComplexityAnalysis,
) -> Result<Visualization> {
let data = serde_json::json!({
"labels": ["0-5", "6-10", "11-15", "16-20", "20+"],
"datasets": [{
"label": "Function Count",
"data": complexity.distribution,
"backgroundColor": ["#4CAF50", "#8BC34A", "#FFC107", "#FF9800", "#F44336"]
}]
});
Ok(Visualization {
title: "Complexity Distribution".to_string(),
viz_type: VisualizationType::BarChart,
data,
config: HashMap::new(),
})
}
fn create_health_score_gauge(&self, score: f64) -> Result<Visualization> {
let data = serde_json::json!({
"value": score,
"min": 0,
"max": 100,
"thresholds": {
"critical": 40,
"high": 60,
"medium": 80
}
});
Ok(Visualization {
title: "Overall Health Score".to_string(),
viz_type: VisualizationType::LineChart, data,
config: HashMap::new(),
})
}
fn create_issue_distribution_chart(&self, sections: &[ReportSection]) -> Result<Visualization> {
let mut issue_counts = HashMap::new();
for section in sections {
let count = section.findings.len();
if count > 0 {
issue_counts.insert(format!("{:?}", section.section_type), count);
}
}
let data = serde_json::json!({
"labels": issue_counts.keys().collect::<Vec<_>>(),
"datasets": [{
"data": issue_counts.values().collect::<Vec<_>>()
}]
});
Ok(Visualization {
title: "Issue Distribution by Category".to_string(),
viz_type: VisualizationType::PieChart,
data,
config: HashMap::new(),
})
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn format_report(
&self,
report: &UnifiedAnalysisReport,
format: ReportFormat,
) -> Result<String> {
match format {
ReportFormat::Json => self.format_as_json(report),
ReportFormat::Markdown => self.format_as_markdown(report),
ReportFormat::Html => self.format_as_html(report).await,
ReportFormat::Pdf => self.format_as_pdf(report).await,
ReportFormat::Dashboard => self.format_as_dashboard(report).await,
}
}
fn format_as_json(&self, report: &UnifiedAnalysisReport) -> Result<String> {
Ok(serde_json::to_string_pretty(report)?)
}
fn format_as_markdown(&self, report: &UnifiedAnalysisReport) -> Result<String> {
let mut md = String::with_capacity(8192);
md.push_str(&format!(
"# {} Analysis Report\n\n",
report.metadata.project_name
));
md.push_str("## Metadata\n\n");
md.push_str(&format!("- **Date**: {}\n", report.metadata.report_date));
md.push_str(&format!(
"- **Tool Version**: {}\n",
report.metadata.tool_version
));
md.push_str(&format!(
"- **Files Analyzed**: {}\n",
report.metadata.analyzed_files
));
md.push_str(&format!(
"- **Total Lines**: {}\n\n",
report.metadata.total_lines
));
md.push_str("## Executive Summary\n\n");
md.push_str(&format!(
"**Overall Health Score**: {:.1}/100\n\n",
report.executive_summary.overall_health_score
));
md.push_str(&format!(
"**Risk Level**: {:?}\n\n",
report.executive_summary.risk_assessment
));
if !report.executive_summary.key_findings.is_empty() {
md.push_str("### Key Findings\n\n");
for finding in &report.executive_summary.key_findings {
md.push_str(&format!("- {finding}\n"));
}
md.push('\n');
}
for section in &report.sections {
md.push_str(&format!("## {}\n\n", section.title));
if !section.metrics.is_empty() {
md.push_str("| Metric | Value | Threshold | Trend |\n");
md.push_str("|--------|-------|-----------|-------|\n");
for (name, metric) in §ion.metrics {
let threshold = metric
.threshold
.map_or_else(|| "N/A".to_string(), |t| format!("{t:.1}"));
md.push_str(&format!(
"| {} | {:.1} {} | {} | {:?} |\n",
name, metric.value, metric.unit, threshold, metric.trend
));
}
md.push('\n');
}
if !section.findings.is_empty() {
md.push_str("### Findings\n\n");
for finding in §ion.findings {
md.push_str(&format!(
"- **{:?}**: {}\n",
finding.severity, finding.description
));
}
md.push('\n');
}
}
if !report.recommendations.is_empty() {
md.push_str("## Recommendations\n\n");
for rec in &report.recommendations {
md.push_str(&format!(
"### {} - {}\n\n",
match rec.priority {
Priority::Critical => "🔴 CRITICAL",
Priority::High => "🟠HIGH",
Priority::Medium => "🟡 MEDIUM",
Priority::Low => "🟢 LOW",
},
rec.title
));
md.push_str(&format!("{}\n\n", rec.description));
md.push_str(&format!("**Expected Impact**: {}\n", rec.expected_impact));
md.push_str(&format!("**Effort**: {:?}\n\n", rec.effort));
}
}
Ok(md)
}
async fn format_as_html(&self, report: &UnifiedAnalysisReport) -> Result<String> {
let mut html = String::with_capacity(16384);
html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
html.push_str("<title>Analysis Report</title>\n");
html.push_str("<style>\n");
html.push_str("body { font-family: Arial, sans-serif; margin: 40px; }\n");
html.push_str("h1 { color: #333; }\n");
html.push_str(".metric { background: #f0f0f0; padding: 10px; margin: 10px 0; }\n");
html.push_str(".health-score { font-size: 48px; font-weight: bold; }\n");
html.push_str(".critical { color: #d32f2f; }\n");
html.push_str(".high { color: #f57c00; }\n");
html.push_str(".medium { color: #fbc02d; }\n");
html.push_str(".low { color: #388e3c; }\n");
html.push_str("</style>\n");
html.push_str("</head>\n<body>\n");
html.push_str(&format!(
"<h1>{} Analysis Report</h1>\n",
report.metadata.project_name
));
html.push_str("<div class='executive-summary'>\n");
html.push_str("<h2>Executive Summary</h2>\n");
html.push_str(&format!(
"<div class='health-score'>{:.1}/100</div>\n",
report.executive_summary.overall_health_score
));
html.push_str("</div>\n");
html.push_str("</body>\n</html>");
Ok(html)
}
async fn format_as_pdf(&self, _report: &UnifiedAnalysisReport) -> Result<String> {
Ok("[PDF Report Generated]".to_string())
}
async fn format_as_dashboard(&self, _report: &UnifiedAnalysisReport) -> Result<String> {
Ok("<html><body><h1>Analysis Dashboard</h1></body></html>".to_string())
}
}