Skip to main content

fsm_guards/
ctx.rs

1use serde_json::Value;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5/// Signature for a custom operator handler.
6///
7/// Receives a slice of already-evaluated argument values (not raw AST nodes).
8/// Returns a `Value` that the evaluator inserts back into the expression tree,
9/// or an error that surfaces as `GuardError::CustomOpFailed`.
10pub type CustomOp =
11    dyn Fn(&[Value]) -> Result<Value, Box<dyn std::error::Error + Send + Sync>>
12        + Send
13        + Sync;
14
15/// Per-evaluation context: fuel budget + custom operator map.
16///
17/// `EvalCtx` is `Send + Sync`. Build once, share via `Arc<EvalCtx>` across
18/// threads. Each `evaluate()` call receives `&EvalCtx` and maintains its own
19/// stack-local fuel counter — no shared mutable state.
20pub struct EvalCtx {
21    /// Maximum AST nodes the evaluator may visit. Default: 200.
22    pub fuel: usize,
23    /// Custom operator map: operator name → handler.
24    pub ops: HashMap<String, Arc<CustomOp>>,
25}
26
27impl EvalCtx {
28    /// Create a context with 200 fuel and no custom operators.
29    pub fn new() -> Self {
30        Self {
31            fuel: 200,
32            ops: HashMap::new(),
33        }
34    }
35
36    /// Override the fuel budget.
37    pub fn with_fuel(mut self, fuel: usize) -> Self {
38        self.fuel = fuel;
39        self
40    }
41
42    /// Register a custom operator. Operators are matched by exact name string.
43    pub fn with_op<F>(mut self, name: &str, f: F) -> Self
44    where
45        F: Fn(&[Value]) -> Result<Value, Box<dyn std::error::Error + Send + Sync>>
46            + Send
47            + Sync
48            + 'static,
49    {
50        self.ops.insert(name.to_string(), Arc::new(f));
51        self
52    }
53}
54
55impl Default for EvalCtx {
56    fn default() -> Self {
57        Self::new()
58    }
59}