expy 0.0.2

Embeddable & extensible expression evaluator
Documentation
//! Custom API tests.

use std::sync::{Arc, Mutex};

use expy::{Context, EvalError, Value, eval_in};


#[test]
fn function_constant() {
    let mut ctx = Context::new();
    ctx.set("foo", |_: &mut Context, _: &_| Ok(Value::Integer(42)));
    assert_eq!(eval_in(&mut ctx, "foo()").unwrap().unwrap_integer(), 42);
}

#[test]
fn function_pure() {
    let mut ctx = Context::new();
    ctx.set("foo", |_: &mut Context, args: &[Value]| {
        let Some(&Value::Integer(x)) = args.get(0) else {
            return Err(EvalError::custom("wrong arguments"));
        };
        Ok(Value::Integer(x * 2 + 1))
    });
    assert_eq!(eval_in(&mut ctx, "foo(3)").unwrap().unwrap_integer(), 7);
    assert_eq!(eval_in(&mut ctx, "foo(11)").unwrap().unwrap_integer(), 23);
}

#[test]
fn function_pure_external() {
    fn foo(_: &mut Context, args: &[Value]) -> Result<Value, EvalError> {
        let Some(&Value::Integer(x)) = args.get(0) else {
            return Err(EvalError::custom("wrong arguments"));
        };
        Ok(Value::Integer(x * 2 + 1))
    }

    let mut ctx = Context::new();
    ctx.set("foo", foo);
    assert_eq!(eval_in(&mut ctx, "foo(3)").unwrap().unwrap_integer(), 7);
    assert_eq!(eval_in(&mut ctx, "foo(11)").unwrap().unwrap_integer(), 23);
}

#[test]
fn function_stateful() {
    let mut ctx = Context::new();
    ctx.set("inc", |ctx: &mut Context, _: &_| {
        let mut c = ctx.get("counter").and_then(Value::as_integer).copied().unwrap_or_default();
        c += 1;
        ctx.set("counter", c);
        Ok(Value::Integer(c))
    });
    for c in 1..=100 {
        assert_eq!(eval_in(&mut ctx, "inc()").unwrap().unwrap_integer(), c);
    }
}

#[test]
fn function_local() {
    let mut ctx = Context::new();

    // we'd normally use atomics for counters but this is meant to be general
    let counter = Arc::new(Mutex::new(0));
    ctx.set("inc", {
        let counter = counter.clone();
        move |_: &mut Context, _: &_| {
            let mut c = counter.lock().unwrap();
            *c += 1;
            Ok(Value::Integer(*c))
        }
    });
    for c in 1..=100 {
        assert_eq!(eval_in(&mut ctx, "inc()").unwrap().unwrap_integer(), c);
    }
}