///|
/// Output format for machine and human reports.
pub enum ReportFormat {
Text
Markdown
Json
Csv
} derive(Debug, Eq)
///|
/// Options controlling report content.
pub struct ReportOptions {
format : ReportFormat
include_changes : Bool
include_validation : Bool
signal_pattern : String?
} derive(Debug, Eq)
///|
/// Create a complete text-report configuration.
pub fn default_report_options() -> ReportOptions {
{
format: Text,
include_changes: false,
include_validation: true,
signal_pattern: None,
}
}
///|
/// Create Markdown report options.
pub fn markdown_report_options() -> ReportOptions {
{
format: Markdown,
include_changes: false,
include_validation: true,
signal_pattern: None,
}
}
///|
/// Create JSON report options.
pub fn json_report_options() -> ReportOptions {
{
format: Json,
include_changes: true,
include_validation: true,
signal_pattern: None,
}
}
///|
/// Create CSV report options.
pub fn csv_report_options() -> ReportOptions {
{
format: Csv,
include_changes: true,
include_validation: false,
signal_pattern: None,
}
}
///|
/// Render a trace using the requested report format.
pub fn render_report(trace : Trace, options : ReportOptions) -> String {
let selected = match options.signal_pattern {
Some(pattern) => filter_trace_signals(trace, pattern)
None => trace
}
match options.format {
Text => extended_text_report(selected, options)
Markdown => markdown_report(selected, options)
Json => extended_json_report(selected, options)
Csv => changes_csv(selected)
}
}
///|
/// Render a Markdown summary with tables for all declared signals.
pub fn markdown_report(trace : Trace, options : ReportOptions) -> String {
let out : Array[String] = [
"# MoonBit VCD report",
"",
"| Metric | Value |",
"| --- | ---: |",
"| Signals | " + trace.signals.length().to_string() + " |",
"| Changes | " + trace.changes.length().to_string() + " |",
"| End time | " + trace.end_time.to_string() + " |",
"",
"| Signal | Changes | Rising | Falling | Unknown |",
"| --- | ---: | ---: | ---: | ---: |",
]
for signal in trace.signals {
let item = stats(trace, signal.id)
out.push(
"| " +
signal_path(signal) +
" | " +
item.changes.to_string() +
" | " +
item.rising_edges.to_string() +
" | " +
item.falling_edges.to_string() +
" | " +
item.unknown_values.to_string() +
" |",
)
}
if options.include_validation {
out.push("")
out.push("## Validation")
out.push("")
out.push(diagnostics_report(deep_validate(trace)))
}
if options.include_changes {
out.push("")
out.push("## Changes")
out.push("")
out.push("```")
out.push(changes_csv(trace).trim_end().to_owned())
out.push("```")
}
out.join("\n")
}
///|
/// Render a text report with analysis highlights.
pub fn extended_text_report(trace : Trace, options : ReportOptions) -> String {
let out : Array[String] = [
text_report(trace),
"timescale: " + trace.time_scale().display(),
"scope depth: " + maximum_scope_depth(trace).to_string(),
]
for signal in trace.signals {
let frequency = frequency_estimate(trace, signal.id)
let activity_item = activity(trace, signal.id)
out.push(
" " +
signal_path(signal) +
" active=" +
activity_item.active_time.to_string() +
" transitions=" +
activity_item.transition_count.to_string() +
" periods=" +
frequency.period_count.to_string(),
)
}
if options.include_validation {
out.push("validation: " + diagnostics_report(deep_validate(trace)))
}
out.join("\n")
}
///|
/// Render a report-oriented JSON object.
pub fn extended_json_report(trace : Trace, options : ReportOptions) -> String {
let signals = trace.signals
.map(fn(signal) {
let item = stats(trace, signal.id)
"{\"path\":\"" +
escape_text(signal_path(signal)) +
"\",\"id\":\"" +
escape_text(signal.id) +
"\",\"changes\":" +
item.changes.to_string() +
",\"rising\":" +
item.rising_edges.to_string() +
",\"falling\":" +
item.falling_edges.to_string() +
",\"unknown\":" +
item.unknown_values.to_string() +
"}"
})
.join(",")
let validation = if options.include_validation {
let (errors, warnings) = diagnostic_counts(deep_validate(trace))
",\"errors\":" +
errors.to_string() +
",\"warnings\":" +
warnings.to_string()
} else {
""
}
"{\"signals\":[" +
signals +
"],\"changes\":" +
trace.changes.length().to_string() +
",\"end_time\":" +
trace.end_time.to_string() +
validation +
"}"
}
///|
/// Render each signal's statistics as CSV.
pub fn stats_csv(trace : Trace) -> String {
let out : Array[String] = ["path,id,changes,rising,falling,unknown"]
for signal in trace.signals {
let item = stats(trace, signal.id)
out.push(
escape_text(signal_path(signal)) +
"," +
escape_text(signal.id) +
"," +
item.changes.to_string() +
"," +
item.rising_edges.to_string() +
"," +
item.falling_edges.to_string() +
"," +
item.unknown_values.to_string(),
)
}
out.join("\n")
}
///|
/// Return a one-line summary intended for command-line tools.
pub fn summary_line(trace : Trace) -> String {
"signals=" +
trace.signals.length().to_string() +
" changes=" +
trace.changes.length().to_string() +
" end=" +
trace.end_time.to_string() +
" valid=" +
deep_is_valid(trace).to_string()
}