hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
use super::halc::{decode_halc, HalcOrigin};
use std::collections::BTreeMap;

pub const HALC_TRACE_SCHEMA: &str = "hal.halc-trace/0-alpha";

const MAGIC_BYTES: usize = 4;
const VERSION_OFFSET: usize = MAGIC_BYTES;
const FLAGS_OFFSET: usize = VERSION_OFFSET + 2;
const PAYLOAD_LENGTH_OFFSET: usize = FLAGS_OFFSET + 2;
const CHECKSUM_OFFSET: usize = PAYLOAD_LENGTH_OFFSET + 4;
const CHECKSUM_BYTES: usize = 32;
const HEADER_BYTES: usize = CHECKSUM_OFFSET + CHECKSUM_BYTES;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HalcTraceStatus {
    Ok,
    Error,
}

impl HalcTraceStatus {
    pub fn as_keyword(self) -> &'static str {
        match self {
            Self::Ok => "ok",
            Self::Error => "error",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HalcTraceValue {
    String(String),
    Integer(u64),
    Boolean(bool),
    Strings(Vec<String>),
}

pub type HalcTraceEvidence = BTreeMap<String, HalcTraceValue>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HalcTraceEvent {
    pub id: u64,
    pub sequence: u64,
    pub stage: &'static str,
    pub status: HalcTraceStatus,
    pub evidence: HalcTraceEvidence,
    pub error: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HalcArtifactTrace {
    pub schema: &'static str,
    pub id: String,
    pub status: HalcTraceStatus,
    pub events: Vec<HalcTraceEvent>,
    pub result: Option<HalcTraceEvidence>,
    pub error: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HalcArtifactInspection {
    pub origin: HalcOrigin,
    pub format_version: u16,
    pub flags: u16,
    pub payload_length: u32,
    pub payload_checksum: String,
    pub namespace: String,
    pub resource: String,
    pub source_hash: String,
    pub form_count: usize,
    pub schema_definitions: Vec<String>,
    pub schema_functions: Vec<String>,
}

fn read_u16(bytes: &[u8], offset: usize) -> u16 {
    u16::from_be_bytes([bytes[offset], bytes[offset + 1]])
}

fn read_u32(bytes: &[u8], offset: usize) -> u32 {
    u32::from_be_bytes([
        bytes[offset],
        bytes[offset + 1],
        bytes[offset + 2],
        bytes[offset + 3],
    ])
}

fn hex(bytes: &[u8]) -> String {
    let mut output = String::with_capacity(bytes.len() * 2);
    for byte in bytes {
        use std::fmt::Write;
        let _ = write!(&mut output, "{byte:02x}");
    }
    output
}

fn origin_name(origin: HalcOrigin) -> &'static str {
    match origin {
        HalcOrigin::Halc => "halc",
        HalcOrigin::LegacyHir => "legacy-hir",
    }
}

pub fn inspect_halc_artifact(bytes: &[u8]) -> Result<HalcArtifactInspection, String> {
    let module = decode_halc(bytes)?;
    if bytes.len() < HEADER_BYTES {
        return Err("truncated artifact".into());
    }

    let mut schema_definitions: Vec<String> = module.schemas.definitions.keys().cloned().collect();
    schema_definitions.sort();
    let mut schema_functions: Vec<String> = module.schemas.functions.keys().cloned().collect();
    schema_functions.sort();

    Ok(HalcArtifactInspection {
        origin: module.origin,
        format_version: read_u16(bytes, VERSION_OFFSET),
        flags: read_u16(bytes, FLAGS_OFFSET),
        payload_length: read_u32(bytes, PAYLOAD_LENGTH_OFFSET),
        payload_checksum: hex(&bytes[CHECKSUM_OFFSET..HEADER_BYTES]),
        namespace: module.namespace,
        resource: module.resource,
        source_hash: hex(&module.source_hash),
        form_count: module.forms.len(),
        schema_definitions,
        schema_functions,
    })
}

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

fn integer(value: usize) -> HalcTraceValue {
    HalcTraceValue::Integer(value as u64)
}

fn event(
    id: u64,
    stage: &'static str,
    status: HalcTraceStatus,
    evidence: HalcTraceEvidence,
    error: Option<String>,
) -> HalcTraceEvent {
    HalcTraceEvent {
        id,
        sequence: id,
        stage,
        status,
        evidence,
        error,
    }
}

fn successful_trace(id: String, inspection: HalcArtifactInspection) -> HalcArtifactTrace {
    let mut module_identity = HalcTraceEvidence::new();
    module_identity.insert("module/namespace".into(), string(&inspection.namespace));
    module_identity.insert("module/resource".into(), string(&inspection.resource));
    module_identity.insert("source/hash".into(), string(&inspection.source_hash));
    module_identity.insert("form/count".into(), integer(inspection.form_count));

    let mut schema_index = HalcTraceEvidence::new();
    schema_index.insert(
        "schema/definitions".into(),
        HalcTraceValue::Strings(inspection.schema_definitions.clone()),
    );
    schema_index.insert(
        "schema/functions".into(),
        HalcTraceValue::Strings(inspection.schema_functions.clone()),
    );

    let mut envelope = HalcTraceEvidence::new();
    envelope.insert(
        "artifact/origin".into(),
        string(origin_name(inspection.origin)),
    );
    envelope.insert(
        "format/version".into(),
        HalcTraceValue::Integer(inspection.format_version as u64),
    );
    envelope.insert(
        "format/flags".into(),
        HalcTraceValue::Integer(inspection.flags as u64),
    );
    envelope.insert(
        "payload/bytes".into(),
        HalcTraceValue::Integer(inspection.payload_length as u64),
    );
    envelope.insert(
        "payload/checksum".into(),
        string(&inspection.payload_checksum),
    );

    let mut validation = HalcTraceEvidence::new();
    validation.insert("artifact/valid".into(), HalcTraceValue::Boolean(true));
    validation.insert(
        "payload/checksum".into(),
        string(&inspection.payload_checksum),
    );

    let mut decoded = HalcTraceEvidence::new();
    decoded.insert("decode/parity".into(), HalcTraceValue::Boolean(true));
    decoded.insert("form/count".into(), integer(inspection.form_count));

    let events = vec![
        event(
            1,
            "module/identity",
            HalcTraceStatus::Ok,
            module_identity.clone(),
            None,
        ),
        event(
            2,
            "schema/index",
            HalcTraceStatus::Ok,
            schema_index.clone(),
            None,
        ),
        event(
            3,
            "envelope/build",
            HalcTraceStatus::Ok,
            envelope.clone(),
            None,
        ),
        event(
            4,
            "artifact/validate",
            HalcTraceStatus::Ok,
            validation,
            None,
        ),
        event(5, "artifact/decode", HalcTraceStatus::Ok, decoded, None),
    ];

    let mut result = module_identity;
    result.extend(schema_index);
    result.extend(envelope);
    result.insert("decode/parity".into(), HalcTraceValue::Boolean(true));

    HalcArtifactTrace {
        schema: HALC_TRACE_SCHEMA,
        id,
        status: HalcTraceStatus::Ok,
        events,
        result: Some(result),
        error: None,
    }
}

fn failed_trace(id: String, bytes: &[u8], error: String) -> HalcArtifactTrace {
    let mut evidence = HalcTraceEvidence::new();
    evidence.insert("artifact/bytes".into(), integer(bytes.len()));
    let failure = event(
        1,
        "artifact/validate",
        HalcTraceStatus::Error,
        evidence,
        Some(error.clone()),
    );
    HalcArtifactTrace {
        schema: HALC_TRACE_SCHEMA,
        id,
        status: HalcTraceStatus::Error,
        events: vec![failure],
        result: None,
        error: Some(error),
    }
}

pub fn trace_halc_artifact(id: impl Into<String>, bytes: &[u8]) -> HalcArtifactTrace {
    let id = id.into();
    let id = if id.is_empty() {
        "halc-artifact".to_owned()
    } else {
        id
    };
    match inspect_halc_artifact(bytes) {
        Ok(inspection) => successful_trace(id, inspection),
        Err(error) => failed_trace(id, bytes, error),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::kernel::{halc::encode_halc_module, parse_forms};

    fn artifact() -> Vec<u8> {
        let source = "(ns demo.schema) \
                      (def Customer [:map [:id :int]]) \
                      (defn ^{:schema #'-/Customer} customer-id [customer] customer)";
        encode_halc_module(
            "demo.schema",
            "demo/schema.hal",
            source,
            parse_forms(source).unwrap(),
        )
        .unwrap()
    }

    #[test]
    fn inspects_and_traces_the_production_halc_artifact() {
        let artifact = artifact();
        let inspection = inspect_halc_artifact(&artifact).unwrap();
        assert_eq!(inspection.origin, HalcOrigin::Halc);
        assert_eq!(inspection.format_version, 1);
        assert_eq!(inspection.flags, 1);
        assert_eq!(inspection.namespace, "demo.schema");
        assert_eq!(inspection.resource, "demo/schema.hal");
        assert_eq!(inspection.form_count, 3);
        assert_eq!(
            inspection.schema_definitions,
            vec!["demo.schema/Customer".to_owned()]
        );
        assert_eq!(
            inspection.schema_functions,
            vec!["demo.schema/customer-id".to_owned()]
        );

        let trace = trace_halc_artifact("trace-1", &artifact);
        assert_eq!(trace.schema, HALC_TRACE_SCHEMA);
        assert_eq!(trace.status, HalcTraceStatus::Ok);
        assert_eq!(
            trace
                .events
                .iter()
                .map(|event| event.stage)
                .collect::<Vec<_>>(),
            vec![
                "module/identity",
                "schema/index",
                "envelope/build",
                "artifact/validate",
                "artifact/decode",
            ]
        );
        assert_eq!(
            trace.result.as_ref().unwrap().get("module/namespace"),
            Some(&HalcTraceValue::String("demo.schema".to_owned()))
        );
        assert_eq!(
            trace.result.as_ref().unwrap().get("decode/parity"),
            Some(&HalcTraceValue::Boolean(true))
        );
    }

    #[test]
    fn classifies_legacy_hir_without_weakening_validation() {
        let mut artifact = artifact();
        artifact[..MAGIC_BYTES].copy_from_slice(b"HIR\0");
        let inspection = inspect_halc_artifact(&artifact).unwrap();
        assert_eq!(inspection.origin, HalcOrigin::LegacyHir);
        assert_eq!(inspection.format_version, 1);
        assert_eq!(inspection.flags, 1);
    }

    #[test]
    fn invalid_artifacts_become_precise_validation_failure_traces() {
        let trace = trace_halc_artifact("bad", b"NOPE");
        assert_eq!(trace.status, HalcTraceStatus::Error);
        assert_eq!(trace.events.len(), 1);
        assert_eq!(trace.events[0].stage, "artifact/validate");
        assert_eq!(trace.events[0].status, HalcTraceStatus::Error);
        assert_eq!(trace.events[0].error.as_deref(), Some("bad magic"));
        assert_eq!(trace.error.as_deref(), Some("bad magic"));
        assert!(trace.result.is_none());
    }
}