fsm-guards 0.1.1

Fuel-metered JSONLogic guard evaluation with per-call custom operators — forked from jsonlogic-rs
Documentation
//! Custom operator registration and usage.
//!
//! Run with: cargo run --example custom_operators

use fsm_guards::{evaluate, EvalCtx};
use serde_json::{json, Value};
use std::sync::Arc;

fn main() {
    // ── state_in: membership check ─────────────────────────────────────────
    let ctx = EvalCtx::new().with_op("state_in", |args| {
        Ok(Value::Bool(args[1..].contains(&args[0])))
    });

    let rule = json!({"state_in": [{"var": "status"}, "active", "pending"]});
    println!("active → {}", evaluate(&rule, &json!({"status": "active"}), &ctx).unwrap()); // true
    println!("blocked → {}", evaluate(&rule, &json!({"status": "blocked"}), &ctx).unwrap()); // false

    // ── multiple operators ─────────────────────────────────────────────────
    let ctx = EvalCtx::new()
        .with_op("state_in", |args| Ok(Value::Bool(args[1..].contains(&args[0]))))
        .with_op("days_since", |args| {
            // Stub: in production this would compute real elapsed days
            let recorded = args[0].as_f64().unwrap_or(0.0);
            let now = args[1].as_f64().unwrap_or(0.0);
            Ok(json!(now - recorded))
        });

    let rule = json!({"and": [
        {"state_in": [{"var": "status"}, "active", "pending"]},
        {"<": [{"days_since": [{"var": "created_at"}, 1722470400.0]}, 30]}
    ]});
    let data = json!({"status": "active", "created_at": 1720000000.0});
    println!("compound: {}", evaluate(&rule, &data, &ctx).unwrap());

    // ── Arc sharing across threads ─────────────────────────────────────────
    let shared_ctx = Arc::new(
        EvalCtx::new().with_op("always_true", |_| Ok(Value::Bool(true))),
    );

    let handles: Vec<_> = (0..4)
        .map(|i| {
            let ctx = Arc::clone(&shared_ctx);
            let rule = json!({"always_true": []});
            std::thread::spawn(move || {
                let result = evaluate(&rule, &json!({}), &ctx).unwrap();
                println!("thread {i}: {result}");
            })
        })
        .collect();

    for h in handles {
        h.join().unwrap();
    }
}