eclisp 0.2.0

Bindings for Embeddable Common Lisp
use eclisp::{Lisp, LispContext, LispError, sys};

struct Transform {
    x: f32,
}

unsafe extern "C" fn transform_x(ptr: sys::cl_object) -> sys::cl_object {
    let transform = unsafe { &*sys::ecl_foreign_data_pointer_safe(ptr).cast::<Transform>() };
    unsafe { sys::ecl_make_single_float(transform.x) }
}

unsafe extern "C" fn transform_sum(left: sys::cl_object, right: sys::cl_object) -> sys::cl_object {
    let left = unsafe { &*sys::ecl_foreign_data_pointer_safe(left).cast::<Transform>() };
    let right = unsafe { &*sys::ecl_foreign_data_pointer_safe(right).cast::<Transform>() };
    unsafe { sys::ecl_make_single_float(left.x + right.x) }
}

fn native_getter(ctx: &LispContext<'_>) -> Result<(), LispError> {
    let mut transform = Transform { x: 42.0 };

    eclisp::defun_native!(ctx, "%transform-x", transform_x; (_))?;
    let foreign = unsafe { ctx.foreign_data(std::ptr::addr_of!(transform))? };

    let result = ctx.call("%transform-x", &[&foreign])?;
    assert_eq!(result.try_as_f64()?, 42.0);

    transform.x = 84.0;
    assert_eq!(transform.x, 84.0);

    let result = ctx.call("%transform-x", &[&foreign])?;
    assert_eq!(result.try_as_f64()?, 84.0);

    Ok(())
}

fn native_multiple_objects(ctx: &LispContext<'_>) -> Result<(), LispError> {
    let left = Transform { x: 20.0 };
    let right = Transform { x: 22.0 };

    eclisp::defun_native!(ctx, "%transform-sum", transform_sum; (_, _))?;
    let left = unsafe { ctx.foreign_data(std::ptr::addr_of!(left))? };
    let right = unsafe { ctx.foreign_data(std::ptr::addr_of!(right))? };

    let result = ctx.call("%transform-sum", &[&left, &right])?;
    assert_eq!(result.try_as_f64()?, 42.0);

    Ok(())
}

fn run(name: &str, lisp: &LispContext<'_>, test: fn(&LispContext<'_>) -> Result<(), LispError>) {
    print!("test {name} ... ");

    match test(lisp) {
        Ok(()) => println!("ok"),
        Err(error) => panic!("{name} failed: {error}"),
    }
}

fn main() -> Result<(), LispError> {
    let lisp = Lisp::new()?;

    lisp.enter(|ctx| {
        run("native_getter", &ctx, native_getter);
        run("native_multiple_objects", &ctx, native_multiple_objects);
    });

    Ok(())
}