oxi_luajit/
function.rs

1use std::error::Error;
2use std::ffi::{c_int, CStr};
3use std::mem;
4use std::ptr;
5
6use crate::ffi::{self, lua_State};
7use crate::{utils, Poppable, Pushable};
8
9/// Stores a function in the Lua registry, returning its ref.
10pub fn store<F, A, R, E>(fun: F) -> c_int
11where
12    F: Fn(A) -> Result<R, E> + 'static,
13    A: Poppable,
14    R: Pushable,
15    E: Error + 'static,
16{
17    type Callback =
18        Box<dyn Fn(*mut lua_State) -> Result<c_int, crate::Error> + 'static>;
19
20    unsafe extern "C" fn c_fun(lstate: *mut lua_State) -> c_int {
21        let fun = {
22            let idx = ffi::lua_upvalueindex(1);
23            let upv = ffi::lua_touserdata(lstate, idx) as *mut Callback;
24            &**upv
25        };
26
27        fun(lstate).unwrap_or_else(|err| utils::handle_error(lstate, &err))
28    }
29
30    unsafe {
31        crate::with_state(move |lstate| {
32            let fun = move |lstate| {
33                let args = A::pop(lstate)?;
34                let ret = fun(args)
35                    .map_err(crate::Error::push_error_from_err::<R, _>)?;
36                ret.push(lstate)
37            };
38
39            let ud = ffi::lua_newuserdata(lstate, mem::size_of::<Callback>());
40            ptr::write(ud as *mut Callback, Box::new(fun));
41
42            ffi::lua_pushcclosure(lstate, c_fun, 1);
43            ffi::luaL_ref(lstate, ffi::LUA_REGISTRYINDEX)
44        })
45    }
46}
47
48/// Calls a function previously stored in the Lua registry via [store].
49pub fn call<A, R>(lua_ref: c_int, args: A) -> Result<R, crate::Error>
50where
51    A: Pushable,
52    R: Poppable,
53{
54    unsafe {
55        crate::with_state(move |lstate| {
56            ffi::lua_rawgeti(lstate, ffi::LUA_REGISTRYINDEX, lua_ref);
57            let nargs = args.push(lstate)?;
58
59            match ffi::lua_pcall(lstate, nargs, -1, 0 /* <- errorfunc */) {
60                ffi::LUA_OK => R::pop(lstate),
61
62                err_code => {
63                    let msg = CStr::from_ptr(ffi::lua_tostring(lstate, -1))
64                        .to_string_lossy()
65                        .to_string();
66
67                    ffi::lua_pop(lstate, 1);
68
69                    match err_code {
70                        ffi::LUA_ERRRUN => {
71                            Err(crate::Error::RuntimeError(msg))
72                        },
73
74                        ffi::LUA_ERRMEM => Err(crate::Error::MemoryError(msg)),
75
76                        ffi::LUA_ERRERR => {
77                            unreachable!("errorfunc is 0, this never happens!")
78                        },
79
80                        _ => unreachable!(),
81                    }
82                },
83            }
84        })
85    }
86}
87
88/// Removes the function reference stored in the Lua registry
89pub fn remove(lua_ref: c_int) {
90    unsafe {
91        crate::with_state(|lstate| {
92            ffi::luaL_unref(lstate, ffi::LUA_REGISTRYINDEX, lua_ref)
93        })
94    }
95}