boundary-compiler-core 0.1.0

P31 v11A boundary compiler microkernel — strict JSON boundary compilation with receipt artifacts
Documentation
use crate::canonical::canonicalize_stable_sorted_json;
use crate::digest::sha256_digest_hex;
use crate::strict_json::StrictJsonValue;
use crate::types::{
    DigestHex, JsonPointerLikePath, TreatmentDifferenceV1, TreatmentIntegrityDecision,
    TreatmentIntegrityReceiptV1,
};
use std::collections::BTreeMap;

pub fn treatment_receipt_for_paths(
    raw_digest: &DigestHex,
    value: &StrictJsonValue,
    paths: &[JsonPointerLikePath],
) -> Option<TreatmentIntegrityReceiptV1> {
    if paths.is_empty() {
        return None;
    }

    let mut after_hashes = BTreeMap::new();
    let mut differences = Vec::new();
    let before_hashes = BTreeMap::new();
    let mut missing = false;

    for path in paths {
        let after = value
            .get_pointer(path)
            .and_then(|v| canonicalize_stable_sorted_json(v).ok())
            .map(|bytes| sha256_digest_hex(&bytes));
        if after.is_none() {
            missing = true;
            differences.push(TreatmentDifferenceV1 {
                path: path.clone(),
                before_digest: None,
                after_digest: None,
                description: "treatment-critical path is missing".to_string(),
            });
        }
        after_hashes.insert(path.clone(), after);
    }

    let decision = if missing {
        TreatmentIntegrityDecision::MissingCriticalPath
    } else {
        TreatmentIntegrityDecision::Preserved
    };

    Some(TreatmentIntegrityReceiptV1 {
        receipt_id: format!(
            "treatment-integrity:{}",
            raw_digest.trim_start_matches("sha256:")
        ),
        treatment_critical_paths: paths.to_vec(),
        before_hashes,
        after_hashes,
        differences,
        decision,
        waiver: None,
    })
}

pub fn treatment_receipt_for_unparsed_boundary(
    raw_digest: &DigestHex,
    paths: &[JsonPointerLikePath],
    description: &str,
) -> Option<TreatmentIntegrityReceiptV1> {
    if paths.is_empty() {
        return None;
    }

    let before_hashes = BTreeMap::new();
    let mut after_hashes = BTreeMap::new();
    let mut differences = Vec::new();

    for path in paths {
        after_hashes.insert(path.clone(), None);
        differences.push(TreatmentDifferenceV1 {
            path: path.clone(),
            before_digest: None,
            after_digest: None,
            description: description.to_string(),
        });
    }

    Some(TreatmentIntegrityReceiptV1 {
        receipt_id: format!(
            "treatment-integrity:{}",
            raw_digest.trim_start_matches("sha256:")
        ),
        treatment_critical_paths: paths.to_vec(),
        before_hashes,
        after_hashes,
        differences,
        decision: TreatmentIntegrityDecision::MissingCriticalPath,
        waiver: None,
    })
}