///|
/// Stable report projections for CI logs, Markdown artifacts, and automation.
///|
pub(all) enum ReportFormat {
Summary
Json
Markdown
Lines
} derive(Debug, ToJson, Eq)
///|
pub(all) struct CiAnnotation {
level : String
code : String
path : String
message : String
title : String
} derive(Debug, ToJson, Eq)
///|
pub(all) struct ReportSection {
title : String
lines : Array[String]
passed : Bool
} derive(Debug, ToJson, Eq)
///|
pub(all) struct ReportBundle {
title : String
verdict : String
score : Int
sections : Array[ReportSection]
annotations : Array[CiAnnotation]
lines : Array[String]
valid : Bool
} derive(Debug, ToJson, Eq)
///|
pub fn ci_annotation(
level : String,
code : String,
path : String,
message : String,
title : String,
) -> CiAnnotation {
{ level, code, path, message, title }
}
///|
pub fn report_section(
title : String,
lines : Array[String],
passed : Bool,
) -> ReportSection {
{ title, lines, passed }
}
///|
pub fn report_bundle_from_profile(profile : ProductionProfile) -> ReportBundle {
let sections : Array[ReportSection] = []
sections.push(
report_section(
"Production",
production_profile_summary_lines(profile),
profile.gate.passed,
),
)
sections.push(
report_section(
"Semantic validation",
semantic_report_summary_lines(profile.semantic),
profile.semantic.valid,
),
)
sections.push(
report_section(
"Geometry",
geometry_report_summary_lines(profile.geometry),
profile.geometry.degenerate_triangle_count == 0,
),
)
sections.push(
report_section(
"Scene graph",
scene_report_summary_lines(profile.scene),
profile.scene.cycle_node_count == 0,
),
)
sections.push(
report_section(
"Resources",
resource_manifest_summary_lines(profile.resources),
profile.resources.valid,
),
)
sections.push(
report_section(
"Optimization",
optimization_plan_summary_lines(profile.optimization),
!optimization_plan_has_blockers(profile.optimization),
),
)
let annotations : Array[CiAnnotation] = []
for issue in profile.semantic.issues {
annotations.push(
ci_annotation(
semantic_severity_name(issue.severity),
issue.rule,
issue.path,
issue.message,
"glTF semantic validation",
),
)
}
for line in profile.gate.violations {
annotations.push(
ci_annotation(
"error", "PRODUCTION_GATE", "production", line, "Production quality gate",
),
)
}
let lines : Array[String] = []
for section in sections {
lines.push("## \{section.title}")
for line in section.lines {
lines.push(line)
}
}
{
title: "moonbit-gltf-tools asset report",
verdict: production_verdict_name(profile.verdict),
score: profile.gate.score,
sections,
annotations,
lines,
valid: profile.gate.passed,
}
}
///|
pub fn report_bundle_summary(bundle : ReportBundle) -> String {
"\{bundle.title}: verdict=\{bundle.verdict}, score=\{bundle.score}, valid=\{bundle.valid}"
}
///|
pub fn report_bundle_json(bundle : ReportBundle) -> String {
Repr(bundle).to_string()
}
///|
pub fn report_bundle_lines(bundle : ReportBundle) -> Array[String] {
bundle.lines
}
///|
pub fn report_bundle_markdown(bundle : ReportBundle) -> String {
let builder = StringBuilder()
builder.write_string("# ")
builder.write_string(bundle.title)
builder.write_string("\n\n")
builder.write_string(
"Verdict: **\{bundle.verdict}** \nScore: **\{bundle.score}/100**\n\n",
)
for section in bundle.sections {
builder.write_string("## \{section.title}\n\n")
for line in section.lines {
builder.write_string("- \{line}\n")
}
builder.write_string("\n")
}
builder.to_string()
}
///|
pub fn report_bundle_annotations(bundle : ReportBundle) -> Array[String] {
bundle.annotations.map(annotation => {
"::notice title=\{annotation.title},file=\{annotation.path}::\{annotation.code}: \{annotation.message}"
})
}
///|
pub fn report_bundle_has_errors(bundle : ReportBundle) -> Bool {
!bundle.valid
}
///|
pub fn report_bundle_section(
bundle : ReportBundle,
title : String,
) -> ReportSection? {
for section in bundle.sections {
if section.title == title {
return Some(section)
}
}
None
}
///|
pub fn report_bundle_failed_sections(bundle : ReportBundle) -> Array[String] {
bundle.sections
.filter(section => !section.passed)
.map(section => section.title)
}
///|
pub fn report_bundle_summary_lines(bundle : ReportBundle) -> Array[String] {
[
report_bundle_summary(bundle),
"sections=\{bundle.sections.length()}, annotations=\{bundle.annotations.length()}",
"failed_sections=\{report_bundle_failed_sections(bundle).join(",")}",
]
}