vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! Trace data model and CLI format parsing.

use std::str::FromStr;

/// Supported trace output formats.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraceFormat {
    /// Capture nothing user-visible.
    None,
    /// Human-readable summary table.
    Human,
    /// Chrome tracing JSON.
    Chrome,
    /// Inferno-compatible folded stacks.
    Flamegraph,
}

impl FromStr for TraceFormat {
    type Err = String;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "none" => Ok(Self::None),
            "human" => Ok(Self::Human),
            "chrome" => Ok(Self::Chrome),
            "flamegraph" => Ok(Self::Flamegraph),
            other => Err(format!(
                "Fix: unsupported trace format `{other}`. Use one of none,human,chrome,flamegraph."
            )),
        }
    }
}

impl TraceFormat {
    /// Render the accepted CLI values.
    #[inline]
    pub fn values() -> &'static str {
        "none,human,chrome,flamegraph"
    }
}

/// One completed tracing span.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TraceRecord {
    /// Span name.
    pub name: String,
    /// Optional layer id, such as `L3`.
    pub layer_id: Option<String>,
    /// Optional operation id.
    pub op_id: Option<String>,
    /// Optional verdict.
    pub verdict: Option<String>,
    /// Start time in microseconds relative to trace start.
    pub start_us: u128,
    /// Duration in microseconds.
    pub duration_us: u128,
    /// Stack at exit time, including this span.
    pub stack: Vec<String>,
    /// Numeric thread id assigned by the subscriber.
    pub thread_id: u64,
}

/// Completed span records from one observed run.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TraceSnapshot {
    /// Completed spans in exit order.
    pub records: Vec<TraceRecord>,
}

impl TraceSnapshot {
    /// Return spans that represent numbered enforcer layer executions.
    #[inline]
    pub fn layer_records(&self) -> impl Iterator<Item = &TraceRecord> {
        self.records
            .iter()
            .filter(|record| record.layer_id.as_deref().is_some_and(is_numbered_layer))
    }
}

fn is_numbered_layer(layer: &str) -> bool {
    matches!(
        layer,
        "L1" | "L2" | "L3" | "L4" | "L5" | "L6" | "L7" | "L8" | "L9"
    )
}