hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
use crate::core::Value;
use crate::journal::{Journal, JournalEvent, JournalEventKind, JournalStatus, ValuePreview};
use crate::kernel::halc_trace::{
    HalcArtifactTrace, HalcTraceEvent, HalcTraceStatus, HalcTraceValue,
};
use crate::lang::data::{OrderedMap, Vector};

use super::{
    BytecodeSummary, CaseObservation, Check, ExpectedOutcome, HalcSummary, ProductionReport,
    StageOutcome, TeachingAnnotation, REPORT_SCHEMA,
};

pub(super) fn report_value(report: &ProductionReport, browser_only: bool) -> Value {
    let selected = report
        .cases
        .iter()
        .filter(|case| !browser_only || case.case.browser_safe)
        .collect::<Vec<_>>();
    let checks = selected
        .iter()
        .flat_map(|case| case.checks.iter())
        .collect::<Vec<_>>();
    let passed_checks = checks.iter().filter(|check| check.pass).count();
    let failed_checks = checks.len().saturating_sub(passed_checks);
    let status = if failed_checks == 0 {
        "passed"
    } else {
        "failed"
    };

    object([
        ("schema", string(REPORT_SCHEMA)),
        (
            "view",
            string(if browser_only { "browser" } else { "complete" }),
        ),
        ("status", string(status)),
        ("terminalNeutral", Value::Bool(true)),
        (
            "corpus",
            object([
                ("schema", string(super::CORPUS_SCHEMA)),
                ("id", string(&report.corpus.id)),
                ("upstream", string(&report.corpus.upstream)),
            ]),
        ),
        (
            "summary",
            object([
                ("cases", integer(selected.len())),
                ("checks", integer(checks.len())),
                ("passed", integer(passed_checks)),
                ("failed", integer(failed_checks)),
            ]),
        ),
        ("runtimeMatrix", runtime_matrix()),
        ("cases", vector(selected.into_iter().map(case_value))),
    ])
}

fn runtime_matrix() -> Value {
    object([
        (
            "rust",
            object([
                ("supported", Value::Bool(true)),
                ("interpreter", string("production-evaluation-journal")),
                ("halc", string("production-encoder-decoder")),
                ("bytecode", string("production-observation-session")),
            ]),
        ),
        (
            "wasm",
            object([
                ("supported", Value::Bool(true)),
                ("contract", string("same-library-report")),
                ("validation", string("wasm32-ci-compile")),
            ]),
        ),
        (
            "truffle",
            object([
                ("supported", Value::Bool(false)),
                ("status", string("unsupported")),
                ("reason", string("production-corpus-runner-pending-406")),
            ]),
        ),
    ])
}

fn case_value(observation: &CaseObservation) -> Value {
    let passed = observation.checks.iter().all(|check| check.pass);
    object([
        ("id", string(&observation.case.id)),
        ("upstreamId", string(&observation.case.upstream_id)),
        ("sourceId", string(&observation.case.source_id)),
        ("namespace", string(&observation.case.namespace)),
        ("resource", string(&observation.case.resource)),
        ("source", string(&observation.case.source)),
        ("browserSafe", Value::Bool(observation.case.browser_safe)),
        ("expected", expected_value(&observation.case.expected)),
        ("passed", Value::Bool(passed)),
        (
            "stages",
            object([
                (
                    "interpreter",
                    object([
                        ("required", Value::Bool(observation.interpreter_required)),
                        ("outcome", outcome_value(&observation.interpreter)),
                        (
                            "trace",
                            journal_value(&observation.journal, &observation.case.source_id),
                        ),
                    ]),
                ),
                (
                    "halc",
                    object([
                        ("summary", halc_summary_value(&observation.halc)),
                        ("trace", halc_trace_value(&observation.halc_trace)),
                    ]),
                ),
                (
                    "bytecode",
                    object([
                        ("summary", bytecode_summary_value(&observation.bytecode)),
                        ("trace", observation.bytecode_trace.clone()),
                    ]),
                ),
            ]),
        ),
        ("checks", vector(observation.checks.iter().map(check_value))),
        (
            "teaching",
            vector(observation.teaching.iter().map(annotation_value)),
        ),
    ])
}

fn expected_value(expected: &ExpectedOutcome) -> Value {
    match expected {
        ExpectedOutcome::Display(value) => {
            object([("status", string("returned")), ("display", string(value))])
        }
        ExpectedOutcome::ErrorCategory(value) => {
            object([("status", string("error")), ("category", string(value))])
        }
        ExpectedOutcome::CompileError(value) => object([
            ("status", string("compile-error")),
            ("marker", string(value)),
        ]),
    }
}

fn outcome_value(outcome: &StageOutcome) -> Value {
    object([
        ("status", string(&outcome.status)),
        ("display", optional_string(outcome.display.as_deref())),
        ("category", optional_string(outcome.category.as_deref())),
        ("message", optional_string(outcome.message.as_deref())),
        ("truncated", Value::Bool(outcome.truncated)),
    ])
}

fn halc_summary_value(summary: &HalcSummary) -> Value {
    object([
        ("status", string(&summary.status)),
        (
            "decodeParity",
            summary.decode_parity.map(Value::Bool).unwrap_or(Value::Nil),
        ),
        (
            "handoffStatus",
            optional_string(summary.handoff_status.as_deref()),
        ),
        (
            "handoffCategory",
            optional_string(summary.handoff_category.as_deref()),
        ),
        (
            "fallback",
            summary.fallback.map(Value::Bool).unwrap_or(Value::Nil),
        ),
        ("namespace", optional_string(summary.namespace.as_deref())),
        ("resource", optional_string(summary.resource.as_deref())),
        (
            "sourceHash",
            optional_string(summary.source_hash.as_deref()),
        ),
        ("events", integer(summary.event_count)),
        (
            "sequencesContiguous",
            Value::Bool(summary.sequences_contiguous),
        ),
    ])
}

fn bytecode_summary_value(summary: &BytecodeSummary) -> Value {
    object([
        ("outcome", outcome_value(&summary.outcome)),
        ("sourceId", optional_string(summary.source_id.as_deref())),
        ("steps", integer(summary.step_count)),
        ("dropped", integer(summary.dropped)),
        (
            "sequencesContiguous",
            Value::Bool(summary.sequences_contiguous),
        ),
    ])
}

fn check_value(check: &Check) -> Value {
    object([
        ("id", string(&check.id)),
        ("pass", Value::Bool(check.pass)),
        ("expected", string(&check.expected)),
        ("actual", string(&check.actual)),
    ])
}

fn annotation_value(annotation: &TeachingAnnotation) -> Value {
    object([
        ("concept", string(&annotation.concept)),
        ("stage", string(&annotation.stage)),
        ("sequence", integer(annotation.sequence)),
        ("detail", string(&annotation.detail)),
    ])
}

fn journal_value(journal: &Journal, source_id: &str) -> Value {
    object([
        ("schema", string(journal.schema)),
        ("id", string(journal.journal_id.to_string())),
        ("sourceId", string(source_id)),
        ("status", string(journal_status(&journal.status))),
        (
            "events",
            vector(journal.events.iter().map(journal_event_value)),
        ),
        (
            "result",
            journal
                .result
                .as_ref()
                .map(preview_value)
                .unwrap_or(Value::Nil),
        ),
        ("error", optional_string(journal.error.as_deref())),
    ])
}

fn journal_event_value(event: &JournalEvent) -> Value {
    object([
        ("id", integer_u64(event.id.0)),
        ("sequence", integer_u64(event.sequence)),
        ("kind", string(journal_kind(&event.kind))),
        (
            "operation",
            event
                .operation
                .map(|value| integer_u64(value.0))
                .unwrap_or(Value::Nil),
        ),
        (
            "parentOperation",
            event
                .parent_operation
                .map(|value| integer_u64(value.0))
                .unwrap_or(Value::Nil),
        ),
        ("depth", integer(event.depth)),
        ("function", optional_string(event.function.as_deref())),
        ("values", vector(event.values.iter().map(preview_value))),
        ("message", optional_string(event.message.as_deref())),
    ])
}

fn preview_value(preview: &ValuePreview) -> Value {
    object([
        ("type", string(&preview.type_name)),
        ("display", string(&preview.display)),
        ("truncated", Value::Bool(preview.truncated)),
    ])
}

fn halc_trace_value(trace: &HalcArtifactTrace) -> Value {
    object([
        ("schema", string(trace.schema)),
        ("id", string(&trace.id)),
        ("status", string(halc_status(trace.status))),
        ("events", vector(trace.events.iter().map(halc_event_value))),
        (
            "result",
            trace
                .result
                .as_ref()
                .map(evidence_value)
                .unwrap_or(Value::Nil),
        ),
        ("error", optional_string(trace.error.as_deref())),
    ])
}

fn halc_event_value(event: &HalcTraceEvent) -> Value {
    object([
        ("id", integer_u64(event.id)),
        ("sequence", integer_u64(event.sequence)),
        ("stage", string(event.stage)),
        ("status", string(halc_status(event.status))),
        ("evidence", evidence_value(&event.evidence)),
        ("error", optional_string(event.error.as_deref())),
    ])
}

fn evidence_value(evidence: &std::collections::BTreeMap<String, HalcTraceValue>) -> Value {
    object(evidence.iter().map(|(key, value)| {
        let value = match value {
            HalcTraceValue::String(value) => string(value),
            HalcTraceValue::Integer(value) => integer_u64(*value),
            HalcTraceValue::Boolean(value) => Value::Bool(*value),
            HalcTraceValue::Strings(values) => {
                vector(values.iter().map(|value| string(value.as_str())))
            }
        };
        (key.clone(), value)
    }))
}

fn journal_status(status: &JournalStatus) -> &'static str {
    match status {
        JournalStatus::Ok => "ok",
        JournalStatus::Error => "error",
        JournalStatus::Truncated => "truncated",
    }
}

fn journal_kind(kind: &JournalEventKind) -> &'static str {
    match kind {
        JournalEventKind::EvaluationStart => "evaluation/start",
        JournalEventKind::MacroExpand => "macro/expand",
        JournalEventKind::OperationEnter => "operation/enter",
        JournalEventKind::OperationReturn => "operation/return",
        JournalEventKind::Error => "evaluation/error",
        JournalEventKind::JournalTruncated => "journal/truncated",
    }
}

fn halc_status(status: HalcTraceStatus) -> &'static str {
    match status {
        HalcTraceStatus::Ok => "ok",
        HalcTraceStatus::Error => "error",
    }
}

fn object<I, K>(fields: I) -> Value
where
    I: IntoIterator<Item = (K, Value)>,
    K: Into<String>,
{
    Value::OrderedMap(Box::new(OrderedMap::from_iter(
        fields
            .into_iter()
            .map(|(key, value)| (Value::String(key.into()), value)),
    )))
}

fn vector<I>(values: I) -> Value
where
    I: IntoIterator<Item = Value>,
{
    Value::Vector(Vector::from_iter(values))
}

fn string(value: impl Into<String>) -> Value {
    Value::String(value.into())
}

fn optional_string(value: Option<&str>) -> Value {
    value.map(string).unwrap_or(Value::Nil)
}

fn integer(value: usize) -> Value {
    Value::Number(i64::try_from(value).unwrap_or(i64::MAX))
}

fn integer_u64(value: u64) -> Value {
    Value::Number(i64::try_from(value).unwrap_or(i64::MAX))
}