nvim_oxi_luajit/
function.rs

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