fn qg_verdict_word(passed: bool) -> String {
use crate::cli::colors as c;
if passed {
c::colored(c::GREEN, "PASSED")
} else {
c::colored(c::RED, "FAILED")
}
}
fn qg_severity_word(severity: &str) -> String {
use crate::cli::colors as c;
match severity {
"error" => c::colored(c::RED, severity),
"warning" => c::colored(c::YELLOW, severity),
ADVISORY_SEVERITY => c::dim(severity),
other => c::colored(c::RED, other),
}
}
fn format_qg_as_summary(
results: &QualityGateResults,
violations: &[QualityViolation],
) -> Result<String> {
use crate::cli::colors as c;
use std::fmt::Write;
let mut output = String::new();
writeln!(
&mut output,
"{} {}",
c::label("Quality Gate:"),
qg_verdict_word(results.passed)
)?;
writeln!(
&mut output,
"{} {}",
c::label("Total violations:"),
c::number(&results.total_violations.to_string())
)?;
writeln!(
&mut output,
"{} {}",
c::label("Blocking violations:"),
c::number(&results.blocking_violations.to_string())
)?;
if !violations.is_empty() {
writeln!(&mut output)?;
write_qg_violations_summary(&mut output, violations)?;
}
Ok(output)
}
fn write_qg_violations_summary(output: &mut String, violations: &[QualityViolation]) -> Result<()> {
use crate::cli::colors as c;
use std::collections::BTreeMap;
use std::fmt::Write;
let mut by_type: BTreeMap<&str, Vec<&QualityViolation>> = BTreeMap::new();
for v in violations {
by_type.entry(&v.check_type).or_default().push(v);
}
for (check_type, type_violations) in by_type {
writeln!(
output,
"{}",
c::subheader(&format!(
"## {} ({} violations)",
check_type,
type_violations.len()
))
)?;
for v in type_violations.iter() {
if let Some(line) = v.line {
writeln!(output, " - {}:{} - {}", c::path(&v.file), line, v.message)?;
} else {
writeln!(output, " - {} - {}", c::path(&v.file), v.message)?;
}
write_violation_details(output, v)?;
}
}
Ok(())
}
fn format_qg_as_detailed(
results: &QualityGateResults,
violations: &[QualityViolation],
) -> Result<String> {
let mut output = String::new();
write_qg_detailed_header(&mut output, results)?;
write_qg_detailed_summary(&mut output, results)?;
if !violations.is_empty() {
write_qg_detailed_violations(&mut output, violations)?;
}
Ok(output)
}
fn write_qg_detailed_header(output: &mut String, results: &QualityGateResults) -> Result<()> {
use crate::cli::colors as c;
use std::fmt::Write;
writeln!(output, "{}\n", c::header("# Quality Gate Detailed Report"))?;
writeln!(
output,
"{} {} {}",
c::label("Status:"),
if results.passed {
"\u{2705}"
} else {
"\u{274c}"
},
qg_verdict_word(results.passed)
)?;
writeln!(
output,
"{} {}",
c::label("Total violations:"),
c::number(&results.total_violations.to_string())
)?;
writeln!(
output,
"{} {}\n",
c::label("Blocking violations:"),
c::number(&results.blocking_violations.to_string())
)?;
Ok(())
}
fn write_qg_detailed_summary(output: &mut String, results: &QualityGateResults) -> Result<()> {
use std::fmt::Write;
writeln!(output, "## Violations by Type\n")?;
let items = [
("Complexity", results.complexity_violations),
("Dead code", results.dead_code_violations),
("SATD", results.satd_violations),
("Entropy", results.entropy_violations),
("Security", results.security_violations),
("Duplicates", results.duplicate_violations),
("Coverage", results.coverage_violations),
("Sections", results.section_violations),
("Provability", results.provability_violations),
];
for (name, count) in items {
writeln!(output, "- {name}: {count}")?;
}
Ok(())
}
fn write_qg_detailed_violations(
output: &mut String,
violations: &[QualityViolation],
) -> Result<()> {
use crate::cli::colors as c;
use std::fmt::Write;
writeln!(output, "\n{}\n", c::subheader("## All Violations"))?;
for (i, v) in violations.iter().enumerate() {
writeln!(
output,
"{}. [{}] {}: {}",
i + 1,
qg_severity_word(&v.severity),
v.check_type,
v.message
)?;
if let Some(line) = v.line {
writeln!(output, " File: {}:{}", c::path(&v.file), line)?;
} else {
writeln!(output, " File: {}", c::path(&v.file))?;
}
}
Ok(())
}
fn format_qg_as_markdown(
results: &QualityGateResults,
violations: &[QualityViolation],
) -> Result<String> {
let mut output = String::new();
write_qg_markdown_header(&mut output, results)?;
write_qg_markdown_summary_table(&mut output, results)?;
if !violations.is_empty() {
write_qg_markdown_violations(&mut output, violations)?;
}
Ok(output)
}
fn write_qg_markdown_violations(
output: &mut String,
violations: &[QualityViolation],
) -> Result<()> {
use std::collections::BTreeMap;
use std::fmt::Write;
writeln!(output, "\n## Violations\n")?;
let mut by_type: BTreeMap<&str, Vec<&QualityViolation>> = BTreeMap::new();
for v in violations {
by_type.entry(&v.check_type).or_default().push(v);
}
for (check_type, type_violations) in by_type {
writeln!(
output,
"### {} ({} issues)\n",
check_type,
type_violations.len()
)?;
writeln!(output, "| Severity | File | Line | Message |")?;
writeln!(output, "|----------|------|------|---------|")?;
for v in &type_violations {
let line_str = v.line.map_or(String::from("-"), |l| l.to_string());
let escaped_msg = v.message.replace('|', "\\|");
writeln!(
output,
"| {} | {} | {} | {} |",
v.severity, v.file, line_str, escaped_msg
)?;
}
writeln!(output)?;
}
Ok(())
}
fn write_qg_markdown_header(output: &mut String, results: &QualityGateResults) -> Result<()> {
use std::fmt::Write;
writeln!(output, "# Quality Gate Report\n")?;
writeln!(
output,
"**Status**: {}\n",
format_qg_status_badge(results.passed)
)?;
writeln!(
output,
"**Total violations**: {}\n",
results.total_violations
)?;
Ok(())
}
fn format_qg_status_badge(passed: bool) -> &'static str {
if passed {
"\u{2705} PASSED"
} else {
"\u{274c} FAILED"
}
}
fn write_qg_markdown_summary_table(
output: &mut String,
results: &QualityGateResults,
) -> Result<()> {
use std::fmt::Write;
writeln!(output, "## Summary\n")?;
write_qg_markdown_table_headers(output)?;
write_qg_markdown_table_rows(output, results)?;
Ok(())
}
fn write_qg_markdown_table_headers(output: &mut String) -> Result<()> {
use std::fmt::Write;
writeln!(output, "| Check Type | Violations |")?;
writeln!(output, "|------------|------------|")?;
Ok(())
}
fn write_qg_markdown_table_rows(output: &mut String, results: &QualityGateResults) -> Result<()> {
use std::fmt::Write;
let rows = get_qg_violation_summary_rows(results);
for (name, count) in rows {
writeln!(output, "| {name} | {count} |")?;
}
Ok(())
}