///|
/// Render a machine-readable JSON compatibility report.
pub fn render_json_report(report : ApiReport) -> String {
let out = StringBuilder()
write_json_report_object(out, report, 0)
out.write("\n")
out.to_string()
}
///|
/// Render a combined JSON object for API diff and version-check output.
pub fn render_json_check_result(
report : ApiReport,
check : VersionCheck,
) -> String {
let out = StringBuilder()
out.write("{\n")
write_json_indent(out, 2)
out.write("\"report\": ")
write_json_report_object(out, report, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"version_check\": ")
write_json_version_check_object(out, check, 2)
out.write("\n")
out.write("}\n")
out.to_string()
}
///|
/// Render a package-level JSON report with diagnostics and a SemVer check.
pub fn render_json_package_check_result(
report : ApiReport,
diagnostics : Array[ApiDiagnostic],
check : VersionCheck,
) -> String {
let out = StringBuilder()
out.write("{\n")
write_json_indent(out, 2)
out.write("\"report\": ")
write_json_report_object(out, report, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostic_summary\": ")
write_json_diagnostic_summary_object(
out,
summarize_diagnostics(diagnostics),
2,
)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostics\": ")
write_json_diagnostics_array(out, diagnostics, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"version_check\": ")
write_json_version_check_object(out, check, 2)
out.write("\n")
out.write("}\n")
out.to_string()
}
///|
/// Render a package-level JSON report with diagnostics.
pub fn render_json_package_report(
report : ApiReport,
diagnostics : Array[ApiDiagnostic],
) -> String {
let out = StringBuilder()
out.write("{\n")
write_json_indent(out, 2)
out.write("\"report\": ")
write_json_report_object(out, report, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostic_summary\": ")
write_json_diagnostic_summary_object(
out,
summarize_diagnostics(diagnostics),
2,
)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostics\": ")
write_json_diagnostics_array(out, diagnostics, 2)
out.write("\n")
out.write("}\n")
out.to_string()
}
///|
/// Render a JSON inventory for one package API snapshot.
pub fn render_json_snapshot_inventory(snapshot : ApiSnapshot) -> String {
let out = StringBuilder()
out.write("{\n")
write_json_number_field(out, "item_count", snapshot.items.length(), 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"kind_counts\": ")
write_json_kind_counts_array(out, count_items_by_kind(snapshot.items), 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostic_summary\": ")
write_json_diagnostic_summary_object(
out,
summarize_diagnostics(snapshot.diagnostics),
2,
)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostics\": ")
write_json_diagnostics_array(out, snapshot.diagnostics, 2)
out.write("\n")
out.write("}\n")
out.to_string()
}
///|
/// Render a release-oriented JSON report for CI and dashboards.
pub fn render_json_release_plan(plan : ReleasePlan) -> String {
let out = StringBuilder()
out.write("{\n")
write_json_string_field(out, "status", plan.status, 2)
out.write(",\n")
write_json_string_field(out, "decision", plan.decision, 2)
out.write(",\n")
write_json_string_field(out, "next_action", plan.next_action, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"summary\": ")
write_json_api_summary_object(out, plan.summary, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostic_summary\": ")
write_json_diagnostic_summary_object(out, plan.diagnostic_summary, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"version_check\": ")
write_json_version_check_object(out, plan.version_check, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"report\": ")
write_json_report_object(out, plan.report, 2)
out.write(",\n")
write_json_indent(out, 2)
out.write("\"diagnostics\": ")
write_json_diagnostics_array(out, plan.diagnostics, 2)
out.write("\n")
out.write("}\n")
out.to_string()
}
///|
fn write_json_report_object(
out : StringBuilder,
report : ApiReport,
indent : Int,
) -> Unit {
out.write("{\n")
write_json_string_field(
out,
"recommendation",
impact_label(report.recommendation),
indent + 2,
)
out.write(",\n")
write_json_number_field(
out,
"change_count",
report.changes.length(),
indent + 2,
)
out.write(",\n")
write_json_summary_field(out, report, indent + 2)
out.write(",\n")
write_json_changes_field(out, report.changes, indent + 2)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_diagnostics_array(
out : StringBuilder,
diagnostics : Array[ApiDiagnostic],
indent : Int,
) -> Unit {
out.write("[")
if diagnostics.is_empty() {
out.write("]")
return
}
out.write("\n")
for i in 0.. 0 {
out.write(",\n")
}
write_json_diagnostic(out, diagnostics[i], indent + 2)
}
out.write("\n")
write_json_indent(out, indent)
out.write("]")
}
///|
fn write_json_kind_counts_array(
out : StringBuilder,
counts : Array[ApiKindCount],
indent : Int,
) -> Unit {
out.write("[")
if counts.is_empty() {
out.write("]")
return
}
out.write("\n")
for i in 0.. 0 {
out.write(",\n")
}
write_json_kind_count(out, counts[i], indent + 2)
}
out.write("\n")
write_json_indent(out, indent)
out.write("]")
}
///|
fn write_json_kind_count(
out : StringBuilder,
count : ApiKindCount,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
out.write("{\n")
write_json_string_field(out, "kind", count.kind, indent + 2)
out.write(",\n")
write_json_number_field(out, "count", count.count, indent + 2)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_diagnostic_summary_object(
out : StringBuilder,
summary : DiagnosticSummary,
indent : Int,
) -> Unit {
out.write("{\n")
write_json_number_field(out, "total", summary.total, indent + 2)
out.write(",\n")
write_json_number_field(out, "errors", summary.errors, indent + 2)
out.write(",\n")
write_json_number_field(out, "warnings", summary.warnings, indent + 2)
out.write(",\n")
write_json_number_field(out, "infos", summary.infos, indent + 2)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_diagnostic(
out : StringBuilder,
diagnostic : ApiDiagnostic,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
out.write("{\n")
write_json_string_field(out, "severity", diagnostic.severity, indent + 2)
out.write(",\n")
write_json_string_field(out, "code", diagnostic.code, indent + 2)
out.write(",\n")
write_json_string_field(out, "path", diagnostic.path, indent + 2)
out.write(",\n")
write_json_string_field(out, "message", diagnostic.message, indent + 2)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_version_check_object(
out : StringBuilder,
check : VersionCheck,
indent : Int,
) -> Unit {
out.write("{\n")
write_json_bool_field(out, "ok", check.ok, indent + 2)
out.write(",\n")
write_json_string_field(
out,
"required",
impact_label(check.required),
indent + 2,
)
out.write(",\n")
write_json_string_field(out, "current", check.current, indent + 2)
out.write(",\n")
write_json_string_field(out, "next", check.next, indent + 2)
out.write(",\n")
write_json_string_field(out, "reason", check.reason, indent + 2)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_api_summary_object(
out : StringBuilder,
summary : ApiSummary,
indent : Int,
) -> Unit {
out.write("{\n")
write_json_number_field(out, "total", summary.total, indent + 2)
out.write(",\n")
write_json_number_field(out, "major", summary.major, indent + 2)
out.write(",\n")
write_json_number_field(out, "minor", summary.minor, indent + 2)
out.write(",\n")
write_json_number_field(out, "patch", summary.patch, indent + 2)
out.write(",\n")
write_json_number_field(out, "added", summary.added, indent + 2)
out.write(",\n")
write_json_number_field(out, "removed", summary.removed, indent + 2)
out.write(",\n")
write_json_number_field(out, "changed", summary.changed, indent + 2)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_summary_field(
out : StringBuilder,
report : ApiReport,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
out.write("\"summary\": {\n")
write_json_number_field(
out,
"major",
count_changes_by_impact(report.changes, Major),
indent + 2,
)
out.write(",\n")
write_json_number_field(
out,
"minor",
count_changes_by_impact(report.changes, Minor),
indent + 2,
)
out.write(",\n")
write_json_number_field(
out,
"patch",
count_changes_by_impact(report.changes, Patch),
indent + 2,
)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_changes_field(
out : StringBuilder,
changes : Array[ApiChange],
indent : Int,
) -> Unit {
write_json_indent(out, indent)
out.write("\"changes\": [")
if changes.is_empty() {
out.write("]")
return
}
out.write("\n")
for i in 0.. 0 {
out.write(",\n")
}
write_json_change(out, changes[i], indent + 2)
}
out.write("\n")
write_json_indent(out, indent)
out.write("]")
}
///|
fn write_json_change(
out : StringBuilder,
change : ApiChange,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
out.write("{\n")
write_json_string_field(
out,
"kind",
change_kind_label(change.kind),
indent + 2,
)
out.write(",\n")
write_json_string_field(
out,
"impact",
impact_label(change.impact),
indent + 2,
)
out.write(",\n")
write_json_string_field(out, "item_kind", change.item_kind, indent + 2)
out.write(",\n")
write_json_string_field(out, "name", change.name, indent + 2)
out.write(",\n")
write_json_optional_string_field(
out,
"old_signature",
change.old_signature,
indent + 2,
)
out.write(",\n")
write_json_optional_string_field(
out,
"new_signature",
change.new_signature,
indent + 2,
)
out.write("\n")
write_json_indent(out, indent)
out.write("}")
}
///|
fn write_json_string_field(
out : StringBuilder,
key : String,
value : String,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
write_json_string(out, key)
out.write(": ")
write_json_string(out, value)
}
///|
fn write_json_optional_string_field(
out : StringBuilder,
key : String,
value : String?,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
write_json_string(out, key)
out.write(": ")
match value {
Some(text) => write_json_string(out, text)
None => out.write("null")
}
}
///|
fn write_json_number_field(
out : StringBuilder,
key : String,
value : Int,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
write_json_string(out, key)
out.write(": ")
out.write(value.to_string())
}
///|
fn write_json_bool_field(
out : StringBuilder,
key : String,
value : Bool,
indent : Int,
) -> Unit {
write_json_indent(out, indent)
write_json_string(out, key)
out.write(": ")
if value {
out.write("true")
} else {
out.write("false")
}
}
///|
fn write_json_string(out : StringBuilder, text : String) -> Unit {
out.write("\"")
out.write(escape_json_string(text))
out.write("\"")
}
///|
fn write_json_indent(out : StringBuilder, indent : Int) -> Unit {
for _ in 0.. Int {
let mut count = 0
for change in changes {
if change.impact == impact {
count += 1
}
}
count
}
///|
fn escape_json_string(text : String) -> String {
let out = StringBuilder(size_hint=text.length())
for ch in text {
if ch == '"' {
out.write("\\\"")
} else if ch == '\\' {
out.write("\\\\")
} else if ch == '\n' {
out.write("\\n")
} else if ch == '\r' {
out.write("\\r")
} else if ch == '\t' {
out.write("\\t")
} else if ch.to_int() < 32 {
out.write("\\u00")
out.write(hex_digit(ch.to_int() / 16))
out.write(hex_digit(ch.to_int() % 16))
} else {
out.write_char(ch)
}
}
out.to_string()
}
///|
fn hex_digit(value : Int) -> String {
match value {
0 => "0"
1 => "1"
2 => "2"
3 => "3"
4 => "4"
5 => "5"
6 => "6"
7 => "7"
8 => "8"
9 => "9"
10 => "a"
11 => "b"
12 => "c"
13 => "d"
14 => "e"
15 => "f"
_ => "0"
}
}