vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use std::path::Path;

use clap::Parser;

use crate::contribute::pipeline::run_contribute;
use crate::contribute::{ContributeFilter, StageStatus};

/// Arguments for the contribution feedback loop.
#[derive(Debug, Parser)]
pub struct Args {
    /// Run mechanical gates for one operation id.
    #[arg(long, conflicts_with = "all")]
    pub op: Option<String>,
    /// Run mechanical gates for every registered operation.
    #[arg(long)]
    pub all: bool,
}

/// Run contribution checks and emit JSON plus a human stderr summary.
pub fn run(args: Args) -> Result<(), Box<dyn std::error::Error>> {
    let filter = if args.all {
        ContributeFilter::All
    } else if let Some(op) = args.op {
        ContributeFilter::Op(Box::leak(op.into_boxed_str()))
    } else {
        return Err("Fix: specify --op <id> or --all.".into());
    };

    let vyre_src_root = Path::new(".");
    let reports = run_contribute(filter, vyre_src_root);

    let json = serde_json::to_string_pretty(&reports)
        .map_err(|err| format!("Fix: failed to serialize report: {err}"))?;
    println!("{json}");

    let total_wall_ms: u64 = reports.iter().map(|r| r.total_duration_ms).sum();
    let any_fail = reports
        .iter()
        .any(|r| r.overall_status == StageStatus::Fail);
    let any_skip = reports
        .iter()
        .any(|r| r.overall_status == StageStatus::Skip);

    for report in &reports {
        eprintln!("op: {} | status: {:?}", report.op_id, report.overall_status);
        for stage in &report.stages {
            eprintln!(
                "  [{:12}] {:4}ms | {:?} | {} finding(s)",
                stage.stage_name,
                stage.duration_ms,
                stage.status,
                stage.findings.len()
            );
        }
    }

    eprintln!("Total wall time: {total_wall_ms}ms");

    if any_fail {
        return Err("Fix: contribution feedback loop reported one or more failed stages.".into());
    }
    let _ = any_skip;
    Ok(())
}