#![cfg_attr(coverage_nightly, coverage(off))]
use super::super::types::QueryResult;
use std::collections::HashMap;
fn find_coverage_for_file<'a>(
file_path: &str,
file_coverage: &'a HashMap<String, HashMap<usize, u64>>,
) -> Option<&'a HashMap<usize, u64>> {
if let Some(hits) = file_coverage.get(file_path) {
return Some(hits);
}
file_coverage
.iter()
.find(|(k, _)| k.ends_with(file_path) || file_path.ends_with(k.as_str()))
.map(|(_, v)| v)
}
fn determine_coverage_status(total: u32, covered: u32) -> &'static str {
if total == 0 {
"no_data"
} else if covered == 0 {
"uncovered"
} else if covered == total {
"full"
} else {
"partial"
}
}
fn annotate_coverage_faults(result: &mut QueryResult) {
match result.coverage_status.as_str() {
"uncovered" => {
result.fault_annotations.push(format!(
"NO_COVERAGE: 0/{} lines covered — untested code path",
result.lines_total
));
}
"partial" if result.line_coverage_pct < 50.0 => {
result.fault_annotations.push(format!(
"LOW_COVERAGE: {:.0}% ({}/{} lines) — {} missed lines need tests",
result.line_coverage_pct,
result.lines_covered,
result.lines_total,
result.missed_lines
));
}
_ => {}
}
if result.impact_score > 5.0 {
result.fault_annotations.push(format!(
"COVERAGE_RISK: impact {:.1} — high-ROI test target (missed:{} x pagerank)",
result.impact_score, result.missed_lines
));
}
}
pub fn enrich_with_coverage(
results: &mut [QueryResult],
file_coverage: &HashMap<String, HashMap<usize, u64>>,
) {
for result in results.iter_mut() {
let line_hits = match find_coverage_for_file(&result.file_path, file_coverage) {
Some(hits) => hits,
None => {
result.coverage_status = "no_data".to_string();
continue;
}
};
let mut covered = 0u32;
let mut total = 0u32;
for line in result.start_line..=result.end_line {
if let Some(&count) = line_hits.get(&line) {
total += 1;
if count > 0 {
covered += 1;
}
}
}
result.lines_covered = covered;
result.lines_total = total;
result.missed_lines = total.saturating_sub(covered);
result.line_coverage_pct = if total > 0 {
(covered as f32 / total as f32) * 100.0
} else {
0.0
};
result.impact_score =
compute_impact_score(result.missed_lines, result.pagerank, result.complexity);
result.coverage_status = determine_coverage_status(total, covered).to_string();
annotate_coverage_faults(result);
}
}
pub fn enrich_with_coverage_diff(
results: &mut [QueryResult],
baseline_coverage: &HashMap<String, HashMap<usize, u64>>,
) {
for result in results.iter_mut() {
let baseline_hits = match baseline_coverage.get(&result.file_path) {
Some(hits) => hits,
None => continue, };
let mut base_covered = 0u32;
let mut base_total = 0u32;
for line in result.start_line..=result.end_line {
if let Some(&count) = baseline_hits.get(&line) {
base_total += 1;
if count > 0 {
base_covered += 1;
}
}
}
let base_pct = if base_total > 0 {
base_covered as f32 / base_total as f32 * 100.0
} else {
0.0
};
result.coverage_diff = result.line_coverage_pct - base_pct;
}
}
pub fn format_coverage_summary(results: &[QueryResult]) -> Option<String> {
let with_data: Vec<_> = results
.iter()
.filter(|r| r.coverage_status != "no_data" && !r.coverage_status.is_empty())
.collect();
if with_data.is_empty() {
return None;
}
let total_covered: u32 = with_data.iter().map(|r| r.lines_covered).sum();
let total_lines: u32 = with_data.iter().map(|r| r.lines_total).sum();
let total_pct = if total_lines > 0 {
total_covered as f64 / total_lines as f64 * 100.0
} else {
0.0
};
let uncovered_count = with_data
.iter()
.filter(|r| r.coverage_status == "uncovered")
.count();
let partial_count = with_data
.iter()
.filter(|r| r.coverage_status == "partial")
.count();
let top_impact = with_data.iter().max_by(|a, b| {
a.impact_score
.partial_cmp(&b.impact_score)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut summary = format_coverage_header(total_covered, total_lines, total_pct, &with_data);
append_coverage_counters(&mut summary, uncovered_count, partial_count);
if let Some(top) = top_impact {
if top.impact_score > 0.0 {
summary.push_str(&format!(
" | \x1b[1;33mTop impact: {} ({:.1})\x1b[0m",
top.function_name, top.impact_score
));
}
}
Some(summary)
}
fn format_coverage_header(
total_covered: u32,
total_lines: u32,
total_pct: f64,
with_data: &[&QueryResult],
) -> String {
let pct_color = if total_pct >= 80.0 {
"\x1b[32m"
} else if total_pct >= 50.0 {
"\x1b[33m"
} else {
"\x1b[1;31m"
};
format!(
"Coverage: {}/{} lines ({}{:.1}%\x1b[0m) across {} functions",
total_covered,
total_lines,
pct_color,
total_pct,
with_data.len()
)
}
fn append_coverage_counters(summary: &mut String, uncovered_count: usize, partial_count: usize) {
if uncovered_count > 0 {
summary.push_str(&format!(
" | \x1b[1;31m{} uncovered\x1b[0m",
uncovered_count
));
}
if partial_count > 0 {
summary.push_str(&format!(" | \x1b[33m{} partial\x1b[0m", partial_count));
}
}
pub fn compute_impact_score(missed_lines: u32, pagerank: f32, complexity: u32) -> f32 {
if missed_lines == 0 {
return 0.0;
}
let pr_factor = (pagerank * 10000.0).max(0.1);
let complexity_factor = (complexity as f32).max(1.0);
missed_lines as f32 * pr_factor / complexity_factor
}