fn format_qg_as_json(
results: &QualityGateResults,
violations: &[QualityViolation],
) -> Result<String> {
Ok(serde_json::to_string_pretty(&serde_json::json!({
"results": results,
"violations": violations,
}))?)
}
fn format_qg_as_human(
results: &QualityGateResults,
violations: &[QualityViolation],
) -> Result<String> {
use std::fmt::Write;
let mut output = String::new();
write_qg_human_header(&mut output, results)?;
write_qg_violation_counts(&mut output, results)?;
if let Some(score) = results.provability_score {
writeln!(&mut output, "\nProvability score: {score:.2}")?;
}
if !violations.is_empty() {
write_qg_violations_list(&mut output, violations)?;
}
Ok(output)
}
fn write_qg_human_header(output: &mut String, results: &QualityGateResults) -> Result<()> {
use crate::cli::colors as c;
use std::fmt::Write;
writeln!(output, "{}\n", c::header("# Quality Gate Report"))?;
writeln!(
output,
"{} {} {}",
c::label("Status:"),
if results.passed {
"\u{2705}"
} else {
"\u{274c}"
},
qg_verdict_word(results.passed)
)?;
writeln!(
output,
"{} {}\n",
c::label("Total violations:"),
c::number(&results.total_violations.to_string())
)?;
Ok(())
}
fn write_qg_violation_counts(output: &mut String, results: &QualityGateResults) -> Result<()> {
use std::fmt::Write;
let counts = [
("Complexity", results.complexity_violations),
("Dead code", results.dead_code_violations),
("Technical debt", results.satd_violations),
("Entropy", results.entropy_violations),
("Security", results.security_violations),
("Duplicate code", results.duplicate_violations),
];
for (name, count) in counts {
if count > 0 {
writeln!(output, "## {name} violations: {count}")?;
}
}
Ok(())
}
fn write_qg_violations_list(output: &mut String, violations: &[QualityViolation]) -> Result<()> {
use crate::cli::colors as c;
use std::fmt::Write;
writeln!(output, "\n{}\n", c::subheader("## Violations:"))?;
for v in violations {
writeln!(
output,
"- [{}] {} - {}",
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))?;
}
write_violation_details(output, v)?;
}
Ok(())
}
fn write_violation_details(output: &mut String, v: &QualityViolation) -> Result<()> {
use std::fmt::Write;
let Some(details) = &v.details else {
return Ok(());
};
if !details.score_factors.is_empty() {
writeln!(output, " Factors: {}", details.score_factors.join(", "))?;
}
if let Some(code) = &details.example_code {
let trimmed = code.trim();
if !trimmed.is_empty() {
writeln!(output, " Example: {}", truncate_line(trimmed, 100))?;
}
}
if details.affected_files.len() > 1 {
writeln!(output, " Files: {}", details.affected_files.join(", "))?;
}
Ok(())
}
fn truncate_line(s: &str, max_len: usize) -> String {
if s.len() <= max_len {
s.to_string()
} else {
format!("{}...", &s[..max_len])
}
}
fn format_qg_as_junit(violations: &[QualityViolation]) -> Result<String> {
let mut output = String::new();
write_junit_header(&mut output)?;
write_junit_testsuite_start(&mut output, violations.len())?;
write_junit_testcases(&mut output, violations)?;
write_junit_footer(&mut output)?;
Ok(output)
}
fn write_junit_header(output: &mut String) -> Result<()> {
use std::fmt::Write;
writeln!(output, r#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
writeln!(output, r#"<testsuites name="Quality Gate">"#)?;
Ok(())
}
fn write_junit_testsuite_start(output: &mut String, count: usize) -> Result<()> {
use std::fmt::Write;
writeln!(
output,
r#" <testsuite name="Quality Checks" tests="{count}" failures="{count}">"#
)?;
Ok(())
}
fn write_junit_testcases(output: &mut String, violations: &[QualityViolation]) -> Result<()> {
for v in violations {
write_single_junit_testcase(output, v)?;
}
Ok(())
}
fn xml_attr_escape(text: &str) -> String {
text.chars()
.filter(|c| matches!(c, '\t' | '\n' | '\r') || !c.is_control())
.collect::<String>()
.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
fn write_single_junit_testcase(output: &mut String, v: &QualityViolation) -> Result<()> {
use std::fmt::Write;
writeln!(
output,
r#" <testcase name="{}" classname="{}">"#,
xml_attr_escape(&v.message),
xml_attr_escape(&v.check_type)
)?;
writeln!(
output,
r#" <failure message="{}" type="{}"/>"#,
xml_attr_escape(&v.message),
xml_attr_escape(&v.severity)
)?;
writeln!(output, r" </testcase>")?;
Ok(())
}
fn write_junit_footer(output: &mut String) -> Result<()> {
use std::fmt::Write;
writeln!(output, r" </testsuite>")?;
writeln!(output, r"</testsuites>")?;
Ok(())
}