fsm-guards 0.1.0

Fuel-metered JSONLogic guard evaluation with per-call custom operators — forked from jsonlogic-rs
Documentation
use std::fmt;

/// Error returned by the low-level `engine::apply` tier.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ApplyError {
    UnknownOperator(String),
    InvalidArguments(String),
    /// A registered custom operator returned `Err`. Carries the operator name
    /// and the error message so `evaluate()` can surface them in `GuardError::CustomOpFailed`.
    CustomOpFailed { op: String, source: String },
    /// In-loop fuel budget exhausted. Internal variant; `evaluate()` maps this
    /// to `GuardError::FuelExceeded { limit }`.
    #[doc(hidden)]
    FuelExceeded,
}

impl fmt::Display for ApplyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnknownOperator(op) => write!(f, "unknown operator: {op}"),
            Self::InvalidArguments(msg) => write!(f, "invalid arguments: {msg}"),
            Self::CustomOpFailed { op, source } => {
                write!(f, "custom operator '{op}' failed: {source}")
            }
            Self::FuelExceeded => write!(f, "fuel budget exhausted"),
        }
    }
}

impl std::error::Error for ApplyError {}

/// Error returned by the high-level `evaluate()` tier.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GuardError {
    /// Structural AST problem or unsupported operator.
    Malformed(String),
    /// `{"var": "X"}` where `X`'s root key is absent from the data object.
    /// Detected via pre-walk; names the offending path explicitly.
    MissingVar(String),
    /// Type coercion or argument-arity error from the evaluator.
    TypeError(String),
    /// In-loop node budget exhausted before evaluation completed.
    FuelExceeded { limit: usize },
    /// A registered custom operator returned `Err(...)`.
    CustomOpFailed { op: String, source: String },
}

impl fmt::Display for GuardError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Malformed(msg) => write!(f, "guard rule malformed: {msg}"),
            Self::MissingVar(path) => write!(f, "guard references missing variable '{path}'"),
            Self::TypeError(msg) => write!(f, "guard type error: {msg}"),
            Self::FuelExceeded { limit } => {
                write!(f, "guard rule too complex (node limit: {limit})")
            }
            Self::CustomOpFailed { op, source } => {
                write!(f, "custom operator '{op}' failed: {source}")
            }
        }
    }
}

impl std::error::Error for GuardError {}