///|
/// Stages reported by a reusable parse-and-analyze pipeline.
pub enum PipelineStage {
SourceInventory
Parse
Validation
Index
Analysis
Export
} derive(Debug, Eq)
///|
/// One stage result with deterministic counters and a status flag.
pub struct PipelineResult {
stage : PipelineStage
ok : Bool
items : Int
detail : String
} derive(Debug, Eq)
///|
/// Complete result of the high-level processing pipeline.
pub struct TracePipeline {
source : SourceStatistics
trace : Trace?
results : Array[PipelineResult]
} derive(Debug)
///|
/// Run inventory, parse, validation, indexing, analysis, and export checks.
pub fn run_pipeline(source : String) -> TracePipeline {
let source_info = source_statistics(source)
let results : Array[PipelineResult] = [
{
stage: SourceInventory,
ok: source_info.nonempty_lines > 0,
items: source_info.lines,
detail: source_statistics_line(source_info),
},
]
match parse_normalized(source) {
Err(error) => {
results.push({
stage: Parse,
ok: false,
items: 0,
detail: parse_error_message(error),
})
{ source: source_info, trace: None, results }
}
Ok(trace) => {
results.push({
stage: Parse,
ok: true,
items: trace.changes.length(),
detail: "ok",
})
let summary = validation_summary(trace)
results.push({
stage: Validation,
ok: structural_gate(trace),
items: summary.diagnostics,
detail: validation_summary_line(summary),
})
let index = build_index(trace)
results.push({
stage: Index,
ok: index.is_consistent(trace),
items: index.change_count(""),
detail: "signals=" + index.signal_count().to_string(),
})
let metrics = trace_analysis_metrics(trace)
results.push({
stage: Analysis,
ok: analysis_safe(trace),
items: metrics.length(),
detail: trace_quality_report(trace),
})
results.push({
stage: Export,
ok: export_parseable(trace),
items: json_size(trace),
detail: export_summary(trace),
})
{ source: source_info, trace: Some(trace), results }
}
}
}
///|
/// Return whether all completed pipeline stages passed.
pub fn TracePipeline::is_success(self : TracePipeline) -> Bool {
self.results.all(fn(result) { result.ok })
}
///|
/// Return the number of pipeline stages that passed.
pub fn TracePipeline::passed_stages(self : TracePipeline) -> Int {
self.results.count_if(fn(result) { result.ok })
}
///|
/// Return the number of pipeline stages that failed.
pub fn TracePipeline::failed_stages(self : TracePipeline) -> Int {
self.results.count_if(fn(result) { !result.ok })
}
///|
/// Render a pipeline result as readable text.
pub fn TracePipeline::to_text(self : TracePipeline) -> String {
let out : Array[String] = [
"MoonBit VCD pipeline",
source_statistics_line(self.source),
]
for result in self.results {
out.push(
pipeline_stage_name(result.stage) +
": " +
result.ok.to_string() +
" " +
result.detail,
)
}
out.join("\n")
}
///|
/// Render pipeline stages as CSV.
pub fn TracePipeline::to_csv(self : TracePipeline) -> String {
let out : Array[String] = ["stage,ok,items,detail"]
for result in self.results {
out.push(
pipeline_stage_name(result.stage) +
"," +
result.ok.to_string() +
"," +
result.items.to_string() +
"," +
escape_text(result.detail),
)
}
out.join("\n")
}
///|
/// Render a pipeline result as JSON.
pub fn TracePipeline::to_json(self : TracePipeline) -> String {
let body = self.results
.map(fn(result) {
"{\"stage\":\"" +
pipeline_stage_name(result.stage) +
"\",\"ok\":" +
result.ok.to_string() +
",\"items\":" +
result.items.to_string() +
",\"detail\":\"" +
escape_text(result.detail) +
"\"}"
})
.join(",")
"{\"success\":" +
self.is_success().to_string() +
",\"results\":[" +
body +
"]}"
}
///|
fn pipeline_stage_name(stage : PipelineStage) -> String {
match stage {
SourceInventory => "source"
Parse => "parse"
Validation => "validation"
Index => "index"
Analysis => "analysis"
Export => "export"
}
}
///|
/// Return the parsed trace when every required stage passed.
pub fn TracePipeline::successful_trace(self : TracePipeline) -> Trace? {
if self.is_success() {
self.trace
} else {
None
}
}
///|
/// Return a one-line pipeline verdict for CI.
pub fn pipeline_verdict(pipeline : TracePipeline) -> String {
(if pipeline.is_success() { "pass" } else { "fail" }) +
" stages=" +
pipeline.passed_stages().to_string() +
"/" +
pipeline.results.length().to_string()
}