///|
/// An artifact produced by a trace analysis run.
pub enum TraceArtifactKind {
Waveform
Report
CoverageData
AssertionData
ProtocolData
Manifest
} derive(Debug, Eq)
///|
/// A named artifact with a stable content digest.
pub struct TraceArtifact {
name : String
kind : TraceArtifactKind
format : String
bytes : Int
digest : Int64
ready : Bool
} derive(Debug)
///|
/// List artifact kinds.
pub fn trace_artifact_catalog() -> Array[TraceArtifactKind] {
[Waveform, Report, CoverageData, AssertionData, ProtocolData, Manifest]
}
///|
/// Create an artifact descriptor from textual content.
pub fn trace_artifact(
name : String,
kind : TraceArtifactKind,
format : String,
content : String,
) -> TraceArtifact {
{
name,
kind,
format,
bytes: content.length(),
digest: content.hash().to_int64(),
ready: content.length() > 0,
}
}
///|
/// Return whether an artifact is usable.
pub fn TraceArtifact::is_ready(self : TraceArtifact) -> Bool {
self.ready && self.bytes > 0
}
///|
/// Return a stable artifact label.
pub fn TraceArtifact::label(self : TraceArtifact) -> String {
self.name + "." + self.format
}
///|
/// Render one artifact descriptor as JSON.
pub fn TraceArtifact::to_json(self : TraceArtifact) -> String {
"{\"name\":\"" +
escape_text(self.name) +
"\",\"kind\":\"" +
trace_artifact_kind_name(self.kind) +
"\",\"format\":\"" +
self.format +
"\",\"bytes\":" +
self.bytes.to_string() +
",\"digest\":" +
self.digest.to_string() +
",\"ready\":" +
self.ready.to_string() +
"}"
}
///|
/// Render a compact artifact status line.
pub fn TraceArtifact::to_text(self : TraceArtifact) -> String {
self.label() +
" bytes=" +
self.bytes.to_string() +
" digest=" +
self.digest.to_string() +
" ready=" +
self.ready.to_string()
}
///|
/// Return artifacts that are ready for publication.
pub fn ready_trace_artifacts(
items : Array[TraceArtifact],
) -> Array[TraceArtifact] {
items.filter(fn(item) { item.is_ready() })
}
///|
/// Compare an artifact digest with independently generated content.
pub fn TraceArtifact::digest_matches(
self : TraceArtifact,
content : String,
) -> Bool {
self.digest == content.hash().to_int64() && self.bytes == content.length()
}
///|
/// Return the artifact kinds represented by a collection.
pub fn artifact_kinds(items : Array[TraceArtifact]) -> Array[TraceArtifactKind] {
let result : Array[TraceArtifactKind] = []
for item in items {
if !result.contains(item.kind) {
result.push(item.kind)
}
}
result
}
///|
/// Render a publication manifest from ready artifacts.
pub fn artifact_publication_manifest(items : Array[TraceArtifact]) -> String {
let ready = ready_trace_artifacts(items)
"{\"artifacts\":[" +
ready.map(fn(item) { item.to_json() }).join(",") +
"],\"count\":" +
ready.length().to_string() +
"}"
}
///|
/// Return whether every required artifact is ready.
pub fn artifacts_complete(
items : Array[TraceArtifact],
required : Array[TraceArtifactKind],
) -> Bool {
required.all(fn(kind) {
items.any(fn(item) { item.kind == kind && item.is_ready() })
})
}
///|
/// Check whether an artifact uses a supported interchange format.
pub fn artifact_format_supported(format : String) -> Bool {
format == "json" ||
format == "csv" ||
format == "vcd" ||
format == "markdown" ||
format == "html"
}
///|
/// Return labels for ready artifacts.
pub fn ready_artifact_labels(items : Array[TraceArtifact]) -> Array[String] {
ready_trace_artifacts(items).map(fn(item) { item.label() })
}
///|
/// Return a canonical format label for an artifact kind.
pub fn artifact_default_format(kind : TraceArtifactKind) -> String {
match kind {
Waveform => "vcd"
Report => "markdown"
CoverageData | AssertionData | ProtocolData => "csv"
Manifest => "json"
}
}
///|
/// Validate the canonical format selected for an artifact kind.
pub fn artifact_uses_canonical_format(item : TraceArtifact) -> Bool {
artifact_format_supported(item.format) &&
item.format == artifact_default_format(item.kind)
}
///|
/// Return the first artifact with a requested kind.
pub fn first_artifact_of_kind(
items : Array[TraceArtifact],
kind : TraceArtifactKind,
) -> TraceArtifact? {
for item in items {
if item.kind == kind {
return Some(item)
}
}
None
}
///|
/// Export artifact metadata as CSV.
pub fn trace_artifacts_csv(items : Array[TraceArtifact]) -> String {
let out = StringBuilder()
out.write_string("name,kind,format,bytes,digest,ready\n")
for item in items {
out.write_string(
escape_text(item.name) +
"," +
trace_artifact_kind_name(item.kind) +
"," +
item.format +
"," +
item.bytes.to_string() +
"," +
item.digest.to_string() +
"," +
item.ready.to_string() +
"\n",
)
}
out.to_string()
}
///|
/// Name an artifact kind.
pub fn trace_artifact_kind_name(value : TraceArtifactKind) -> String {
match value {
Waveform => "waveform"
Report => "report"
CoverageData => "coverage"
AssertionData => "assertions"
ProtocolData => "protocol"
Manifest => "manifest"
}
}