///|
fn metadata_diff_totals_json(totals : MetadataEncodingTotals) -> Json {
let row : Map[String, Json] = Map([])
row.set("custom_count", int_json(totals.custom_count))
row.set("custom_encoded_bytes", int_json(totals.custom_encoded_bytes))
row.set("section_header_bytes", int_json(totals.section_header_bytes))
row.set("name_encoding_bytes", int_json(totals.name_encoding_bytes))
row.set("content_bytes", int_json(totals.content_bytes))
Json::object(row)
}
///|
fn metadata_diff_group_json(group : MetadataComparisonGroup) -> Json {
let row : Map[String, Json] = Map([])
row.set("kind", Json::string(group.kind))
row.set("name", path_optional_string(group.name))
row.set(
"baseline_ordinals",
Json::array(group.baseline_ordinals.map(int_json)),
)
row.set("current_ordinals", Json::array(group.current_ordinals.map(int_json)))
row.set("baseline", metadata_diff_totals_json(group.baseline))
row.set("current", metadata_diff_totals_json(group.current))
row.set("delta", metadata_diff_totals_json(group.delta))
Json::object(row)
}
///|
/// Complete reports remain nested objects with the original single-file schema.
pub fn render_metadata_diff_json(report : MetadataComparison) -> String {
let root : Map[String, Json] = Map([])
root.set("format", Json::string("moonsize-metadata-diff"))
root.set("schema_version", int_json(1))
root.set("baseline", metadata_json_value(report.baseline))
root.set("current", metadata_json_value(report.current))
root.set("delta", metadata_diff_totals_json(report.delta))
root.set(
"baseline_non_custom_bytes",
int_json(report.baseline_non_custom_bytes),
)
root.set(
"current_non_custom_bytes",
int_json(report.current_non_custom_bytes),
)
root.set("delta_non_custom_bytes", int_json(report.delta_non_custom_bytes))
root.set("delta_file_bytes", int_json(report.delta_file_bytes))
root.set(
"categories",
Json::array(report.categories.map(metadata_diff_group_json)),
)
root.set(
"name_groups",
Json::array(report.name_groups.map(metadata_diff_group_json)),
)
root.set("comparison_scope", Json::string(report.comparison_scope))
Json::object(root).stringify(indent=2) + "\n"
}
///|
fn metadata_diff_coverage(side : String, report : MetadataReport) -> String {
let out = StringBuilder()
out.write_string("\{side}: \{report.file_size} B; SHA-256 \{report.sha256}\n")
out.write_string(
"Byte ledger complete: \{report.byte_ledger_complete}; custom-name decoding: \{report.custom_name_status}; name details: \{report.name_details_status}\n",
)
for row in report.customs {
if row.unbounded_tail is Some(range) {
out.write_string(
"Unbounded name tail, custom #\{row.ordinal}: " +
metadata_range_text(range) +
"\n",
)
}
}
for sub in report.subsections {
if sub.unparsed is Some(range) {
out.write_string(
"Unparsed name payload, custom #\{sub.custom_ordinal}, subsection #\{sub.ordinal}: " +
metadata_range_text(range) +
"\n",
)
}
}
out.write_string("Diagnostics: \{report.diagnostics.length()}\n")
for d in report.diagnostics {
out.write_string(
"custom #\{d.custom_ordinal}, file offset \{d.offset}: \{d.reason}\n",
)
}
out.to_string()
}
///|
fn metadata_diff_group_text(group : MetadataComparisonGroup) -> String {
let label = if group.kind == "unavailable_names" {
"unavailable_names (accounting only)"
} else {
path_optional_string(group.name).stringify()
}
"\{label} \{group.baseline.custom_count} \{group.current.custom_count} \{group.delta.custom_count} \{group.baseline.custom_encoded_bytes} \{group.current.custom_encoded_bytes} \{group.delta.custom_encoded_bytes} \{group.delta.section_header_bytes} \{group.delta.name_encoding_bytes} \{group.delta.content_bytes}\n"
}
///|
pub fn render_metadata_diff(report : MetadataComparison, top : Int) -> String {
let out = StringBuilder()
out.write_string("CUSTOM ENCODING COMPARISON (current - baseline)\n\n")
out.write_string(metadata_diff_coverage("Baseline", report.baseline))
out.write_string(metadata_diff_coverage("Current", report.current))
out.write_string(
"\nOUTER BYTE LEDGER (internal name details are not compared)\n",
)
for
(label, input) in [
("Baseline", report.baseline),
("Current", report.current),
] {
let s = input.summary
out.write_string(
"\{label} custom: \{s.custom_bytes} B = headers \{s.section_header_bytes} B + name encodings \{s.custom_name_encoding_bytes} B + contents \{s.content_bytes} B; \{s.custom_count} instances\n",
)
}
let d = report.delta
out.write_string(
"Custom delta: \{d.custom_encoded_bytes} B = headers \{d.section_header_bytes} B + name encodings \{d.name_encoding_bytes} B + contents \{d.content_bytes} B; count delta \{d.custom_count}\n",
)
out.write_string(
"Non-custom: \{report.baseline_non_custom_bytes} -> \{report.current_non_custom_bytes} B\n",
)
out.write_string(
"File delta: \{report.delta_file_bytes} B = custom \{d.custom_encoded_bytes} B + non-custom \{report.delta_non_custom_bytes} B\n",
)
let heading = "NAME BEFORE COUNT AFTER COUNT COUNT DELTA BEFORE B AFTER B ENCODED DELTA B HEADER DELTA B NAME DELTA B CONTENT DELTA B\n"
out.write_string(
"\nCATEGORY TOTALS (all categories; alternative view)\n" + heading,
)
for group in report.categories {
out.write_string(metadata_diff_group_text(group))
}
let shown = top.max(0).min(report.name_groups.length())
out.write_string(
"\nFULL-NAME GROUPS: showing \{shown} of \{report.name_groups.length()}\n" +
heading,
)
for i in 0..