///|
/// Render a Markdown compatibility report for PR comments or release notes.
pub fn render_markdown_report(report : ApiReport) -> String {
let out = StringBuilder()
out.write("# MoonGuard API Compatibility Report\n\n")
out.write("- Recommendation: **")
out.write(impact_label(report.recommendation))
out.write("**\n")
out.write("- Changes: ")
out.write(report.changes.length().to_string())
out.write("\n\n")
if report.changes.is_empty() {
out.write("No public API changes detected.\n")
return out.to_string()
}
out.write("| Impact | Change | Symbol | Details |\n")
out.write("| --- | --- | --- | --- |\n")
for change in report.changes {
out.write("| ")
out.write(impact_label(change.impact))
out.write(" | ")
out.write(change_kind_label(change.kind))
out.write(" | `")
out.write(change.item_kind)
out.write(" ")
out.write(change.name)
out.write("` | ")
out.write(change_details(change))
out.write(" |\n")
}
out.to_string()
}
///|
/// Render a Markdown summary for a SemVer check result.
pub fn render_version_check_markdown(check : VersionCheck) -> String {
let out = StringBuilder()
out.write("## Version Check\n\n")
out.write("- Required bump: **")
out.write(impact_label(check.required))
out.write("**\n")
out.write("- Current version: `")
out.write(escape_markdown_cell(check.current))
out.write("`\n")
out.write("- Next version: `")
out.write(escape_markdown_cell(check.next))
out.write("`\n")
out.write("- Result: **")
if check.ok {
out.write("pass")
} else {
out.write("fail")
}
out.write("**\n")
out.write("- Reason: ")
out.write(escape_markdown_cell(check.reason))
out.write("\n")
out.to_string()
}
///|
/// Render a package-level Markdown report with a SemVer check section.
pub fn render_markdown_package_check_result(
report : ApiReport,
diagnostics : Array[ApiDiagnostic],
check : VersionCheck,
) -> String {
let out = StringBuilder()
out.write(render_markdown_package_report(report, diagnostics))
out.write("\n")
out.write(render_version_check_markdown(check))
out.to_string()
}
///|
/// Render a package-level Markdown report with summary and diagnostics.
pub fn render_markdown_package_report(
report : ApiReport,
diagnostics : Array[ApiDiagnostic],
) -> String {
let out = StringBuilder()
let summary = summarize_report(report)
let diagnostic_summary = summarize_diagnostics(diagnostics)
out.write("# MoonGuard Package API Compatibility Report\n\n")
out.write("- Recommendation: **")
out.write(impact_label(report.recommendation))
out.write("**\n")
out.write("- Changes: ")
out.write(summary.total.to_string())
out.write("\n")
out.write("- Major changes: ")
out.write(summary.major.to_string())
out.write("\n")
out.write("- Minor changes: ")
out.write(summary.minor.to_string())
out.write("\n")
out.write("- Patch changes: ")
out.write(summary.patch.to_string())
out.write("\n")
out.write("- Added/Removed/Changed: ")
out.write(summary.added.to_string())
out.write("/")
out.write(summary.removed.to_string())
out.write("/")
out.write(summary.changed.to_string())
out.write("\n")
out.write("- Diagnostics: ")
out.write(diagnostic_summary.total.to_string())
out.write(" (errors ")
out.write(diagnostic_summary.errors.to_string())
out.write(", warnings ")
out.write(diagnostic_summary.warnings.to_string())
out.write(", infos ")
out.write(diagnostic_summary.infos.to_string())
out.write(")\n\n")
write_markdown_diagnostics(out, diagnostics)
write_markdown_changes(out, report.changes)
out.to_string()
}
///|
/// Render a Markdown inventory for one package API snapshot.
pub fn render_markdown_snapshot_inventory(snapshot : ApiSnapshot) -> String {
let out = StringBuilder()
let diagnostic_summary = summarize_diagnostics(snapshot.diagnostics)
out.write("# MoonGuard API Snapshot Inventory\n\n")
out.write("- Items: ")
out.write(snapshot.items.length().to_string())
out.write("\n")
out.write("- Diagnostics: ")
out.write(diagnostic_summary.total.to_string())
out.write(" (errors ")
out.write(diagnostic_summary.errors.to_string())
out.write(", warnings ")
out.write(diagnostic_summary.warnings.to_string())
out.write(", infos ")
out.write(diagnostic_summary.infos.to_string())
out.write(")\n\n")
write_markdown_kind_counts(out, count_items_by_kind(snapshot.items))
write_markdown_diagnostics(out, snapshot.diagnostics)
out.to_string()
}
///|
/// Render a release-oriented Markdown report for maintainers and PR comments.
pub fn render_markdown_release_plan(plan : ReleasePlan) -> String {
let out = StringBuilder()
out.write("# MoonGuard Release Plan\n\n")
out.write("- Status: **")
out.write(escape_markdown_cell(plan.status))
out.write("**\n")
out.write("- Decision: ")
out.write(escape_markdown_cell(plan.decision))
out.write("\n")
out.write("- Next action: ")
out.write(escape_markdown_cell(plan.next_action))
out.write("\n")
out.write("- Required bump: **")
out.write(impact_label(plan.version_check.required))
out.write("**\n")
out.write("- Version: `")
out.write(escape_markdown_cell(plan.version_check.current))
out.write("` -> `")
out.write(escape_markdown_cell(plan.version_check.next))
out.write("`\n")
out.write("- Version check: **")
if plan.version_check.ok {
out.write("pass")
} else {
out.write("fail")
}
out.write("**\n")
out.write("- API changes: ")
out.write(plan.summary.total.to_string())
out.write(" (major ")
out.write(plan.summary.major.to_string())
out.write(", minor ")
out.write(plan.summary.minor.to_string())
out.write(", patch ")
out.write(plan.summary.patch.to_string())
out.write(")\n")
out.write("- Added/Removed/Changed: ")
out.write(plan.summary.added.to_string())
out.write("/")
out.write(plan.summary.removed.to_string())
out.write("/")
out.write(plan.summary.changed.to_string())
out.write("\n")
out.write("- Diagnostics: ")
out.write(plan.diagnostic_summary.total.to_string())
out.write(" (errors ")
out.write(plan.diagnostic_summary.errors.to_string())
out.write(", warnings ")
out.write(plan.diagnostic_summary.warnings.to_string())
out.write(", infos ")
out.write(plan.diagnostic_summary.infos.to_string())
out.write(")\n\n")
out.write("## Maintainer Checklist\n\n")
write_release_plan_checklist(out, plan)
out.write("\n")
write_markdown_diagnostics(out, plan.diagnostics)
write_markdown_changes(out, plan.report.changes)
out.to_string()
}
///|
fn write_release_plan_checklist(
out : StringBuilder,
plan : ReleasePlan,
) -> Unit {
if plan.diagnostic_summary.errors > 0 {
out.write("- [ ] Fix diagnostic errors in generated interface snapshots.\n")
} else {
out.write("- [x] Generated interface snapshots are readable.\n")
}
if plan.version_check.ok {
out.write("- [x] Proposed version satisfies MoonGuard's recommendation.\n")
} else {
out.write("- [ ] Update the proposed version before release.\n")
}
if plan.summary.major > 0 {
out.write("- [ ] Review breaking changes and document migration notes.\n")
} else if plan.summary.minor > 0 {
out.write("- [ ] Mention added public APIs in release notes.\n")
} else {
out.write("- [x] No public API release notes are required by MoonGuard.\n")
}
}
///|
fn write_markdown_diagnostics(
out : StringBuilder,
diagnostics : Array[ApiDiagnostic],
) -> Unit {
if diagnostics.is_empty() {
return
}
out.write("## Diagnostics\n\n")
out.write("| Severity | Code | Path | Message |\n")
out.write("| --- | --- | --- | --- |\n")
for diagnostic in diagnostics {
out.write("| ")
out.write(escape_markdown_cell(diagnostic.severity))
out.write(" | ")
out.write(escape_markdown_cell(diagnostic.code))
out.write(" | `")
out.write(escape_markdown_cell(diagnostic.path))
out.write("` | ")
out.write(escape_markdown_cell(diagnostic.message))
out.write(" |\n")
}
out.write("\n")
}
///|
fn write_markdown_changes(
out : StringBuilder,
changes : Array[ApiChange],
) -> Unit {
if changes.is_empty() {
out.write("No public API changes detected.\n")
return
}
out.write("| Impact | Change | Symbol | Details |\n")
out.write("| --- | --- | --- | --- |\n")
for change in changes {
out.write("| ")
out.write(impact_label(change.impact))
out.write(" | ")
out.write(change_kind_label(change.kind))
out.write(" | `")
out.write(change.item_kind)
out.write(" ")
out.write(change.name)
out.write("` | ")
out.write(change_details(change))
out.write(" |\n")
}
}
///|
fn write_markdown_kind_counts(
out : StringBuilder,
counts : Array[ApiKindCount],
) -> Unit {
out.write("## Item Kinds\n\n")
if counts.is_empty() {
out.write("No public API items detected.\n\n")
return
}
out.write("| Kind | Count |\n")
out.write("| --- | --- |\n")
for count in counts {
out.write("| ")
out.write(escape_markdown_cell(count.kind))
out.write(" | ")
out.write(count.count.to_string())
out.write(" |\n")
}
out.write("\n")
}
///|
fn escape_markdown_cell(text : String) -> String {
let out = StringBuilder(size_hint=text.length())
for ch in text {
if ch == '|' {
out.write("\\|")
} else {
out.write_char(ch)
}
}
out.to_string()
}