///|
fn markdown_escape(value : String) -> String {
value.replace_all(old="|", new="\\|").replace_all(old="\n", new=" ")
}
///|
fn severity_badge(severity : Severity) -> String {
match severity {
Error => "ERROR"
Warn => "WARN"
Info => "INFO"
}
}
///|
fn append_heading(builder : StringBuilder, level : Int, title : String) -> Unit {
let hashes = "#".repeat(level)
builder.write_string(hashes + " " + title + "\n\n")
}
///|
fn append_score_table(builder : StringBuilder, report : DoctorReport) -> Unit {
builder.write_string(
"| Grade | Conclusion | Errors | Warnings | Info | Profile |\n",
)
builder.write_string("| --- | --- | ---: | ---: | ---: | --- |\n")
builder.write_string(
"| " +
report.score.grade +
" | " +
report.conclusion() +
" | " +
"\{report.score.errors}" +
" | " +
"\{report.score.warnings}" +
" | " +
"\{report.score.infos}" +
" | " +
report.profile +
" |\n\n",
)
}
///|
fn append_diagnostic_table(
builder : StringBuilder,
diagnostics : Array[Diagnostic],
) -> Unit {
if diagnostics.length() == 0 {
builder.write_string(
"No diagnostics. The selected policy considers this project ready.\n\n",
)
return
}
builder.write_string("| Severity | Rule | Finding | Location |\n")
builder.write_string("| --- | --- | --- | --- |\n")
for diagnostic in diagnostics {
let location = match diagnostic.location {
Some(value) => value.path + ":" + "\{value.line}"
None => "-"
}
builder.write_string(
"| " +
severity_badge(diagnostic.severity) +
" | `" +
diagnostic.code +
"` | " +
markdown_escape(diagnostic.message) +
" | `" +
markdown_escape(location) +
"` |\n",
)
}
builder.write_string("\n")
}
///|
fn append_checklist(builder : StringBuilder, checklist : Array[String]) -> Unit {
for item in checklist {
builder.write_string("- [ ] " + item + "\n")
}
builder.write_string("\n")
}
///|
fn append_remediation(builder : StringBuilder, plan : RemediationPlan) -> Unit {
if plan.tasks.length() == 0 {
builder.write_string("No remediation tasks are required.\n\n")
return
}
let mut index = 1
for task in plan.tasks {
builder.write_string("### " + "\{index}" + ". " + task.title + "\n\n")
builder.write_string("Rules: `" + task.rule_ids.join("`, `") + "`\n\n")
for action in task.actions {
builder.write_string("- " + action + "\n")
}
builder.write_string("\n")
index += 1
}
}
///|
/// Render a reviewer-friendly report for pull requests and release notes.
pub fn render_markdown_report(report : DoctorReport) -> String {
let builder = StringBuilder()
append_heading(builder, 1, "Moon Doctor report")
builder.write_string("Project: `" + report.root + "`\n\n")
append_score_table(builder, report)
append_heading(builder, 2, "Diagnostics")
append_diagnostic_table(builder, report.diagnostics)
append_heading(builder, 2, "Release checklist")
append_checklist(builder, report.checklist)
append_heading(builder, 2, "Remediation plan")
append_remediation(builder, build_remediation_plan(report))
builder.to_string()
}
///|
pub fn markdown_report_json(report : DoctorReport) -> Json {
let plan = build_remediation_plan(report)
Json::object({
"markdown": Json::string(render_markdown_report(report)),
"remediation": remediation_plan_to_json(plan),
})
}