harn-vm 0.8.3

Async bytecode virtual machine for the Harn programming language
Documentation
pub const TRIGGER_INBOX_LEGACY_TOPIC: &str = "trigger.inbox";
pub const TRIGGER_INBOX_CLAIMS_TOPIC: &str = "trigger.inbox.claims";
pub const TRIGGER_INBOX_ENVELOPES_TOPIC: &str = "trigger.inbox.envelopes";
pub const TRIGGER_OUTBOX_TOPIC: &str = "trigger.outbox";
pub const TRIGGER_ATTEMPTS_TOPIC: &str = "trigger.attempts";
pub const TRIGGER_DLQ_TOPIC: &str = "trigger.dlq";
pub const TRIGGER_CANCEL_REQUESTS_TOPIC: &str = "trigger.cancel.requests";
pub const TRIGGER_OPERATION_AUDIT_TOPIC: &str = "trigger.operations.audit";
pub const TRIGGERS_LIFECYCLE_TOPIC: &str = "triggers.lifecycle";

pub fn classify_trigger_dlq_error(error: &str) -> &'static str {
    let error = error.to_ascii_lowercase();
    if error.contains("budget")
        && (error.contains("exhaust")
            || error.contains("exceeded")
            || error.contains("limit")
            || error.contains("short-circuit"))
    {
        return "budget_exhausted";
    }
    if error.contains("timeout")
        || error.contains("timed out")
        || error.contains("deadline")
        || error.contains("wall-clock")
        || error.contains("wall clock")
    {
        return "handler_timeout";
    }
    if error.contains("401")
        || error.contains("403")
        || error.contains("unauthorized")
        || error.contains("forbidden")
        || error.contains("auth failed")
        || error.contains("authentication")
        || error.contains("permission denied")
    {
        return "auth_failed";
    }
    if error.contains("provider")
        && (error.contains("5xx")
            || error.contains("500")
            || error.contains("502")
            || error.contains("503")
            || error.contains("504")
            || error.contains("upstream")
            || error.contains("service unavailable"))
    {
        return "provider_5xx";
    }
    if error.contains("predicate")
        && (error.contains("panic")
            || error.contains("crash")
            || error.contains("thrown")
            || error.contains("exception")
            || error.contains("vmerror::thrown"))
    {
        return "predicate_panic";
    }
    if error.contains("handler")
        && (error.contains("panic")
            || error.contains("crash")
            || error.contains("thrown")
            || error.contains("exception")
            || error.contains("vmerror::thrown"))
    {
        return "handler_panic";
    }
    if error.contains("vmerror::thrown") || error.contains("handler threw") {
        return "handler_panic";
    }
    "unknown"
}

#[cfg(test)]
mod tests {
    use super::classify_trigger_dlq_error;

    #[test]
    fn classify_trigger_dlq_error_maps_operational_classes() {
        assert_eq!(
            classify_trigger_dlq_error("provider returned 503 service unavailable"),
            "provider_5xx"
        );
        assert_eq!(
            classify_trigger_dlq_error("predicate VmError::Thrown: nope"),
            "predicate_panic"
        );
        assert_eq!(
            classify_trigger_dlq_error("handler timed out after wall-clock deadline"),
            "handler_timeout"
        );
        assert_eq!(
            classify_trigger_dlq_error("downstream auth failed with 401"),
            "auth_failed"
        );
        assert_eq!(
            classify_trigger_dlq_error("trigger budget exhausted"),
            "budget_exhausted"
        );
        assert_eq!(classify_trigger_dlq_error("boom"), "unknown");
    }
}