luajit_bindings/
utils.rs

1use std::ffi::{c_int, CStr};
2use std::fmt::Display;
3
4use crate::ffi::{self, lua_State};
5
6/// Does nothing if the stack is already taller than `n`, grows the stack
7/// height to `n` by adding `nil`s if it's not.
8pub unsafe fn grow_stack(lstate: *mut lua_State, n: c_int) {
9    if ffi::lua_gettop(lstate) < n {
10        ffi::lua_settop(lstate, n);
11    }
12}
13
14/// Returns a displayable representation of the Lua value at a given stack
15/// index.
16pub unsafe fn debug_value(
17    lstate: *mut lua_State,
18    n: c_int,
19) -> Box<dyn Display> {
20    match ffi::lua_type(lstate, n) {
21        ffi::LUA_TNONE | ffi::LUA_TNIL => Box::new("()"),
22
23        ffi::LUA_TBOOLEAN => Box::new(ffi::lua_toboolean(lstate, n) == 1),
24
25        ffi::LUA_TSTRING => Box::new(
26            CStr::from_ptr(ffi::lua_tostring(lstate, n)).to_string_lossy(),
27        ),
28
29        ffi::LUA_TNUMBER => Box::new(ffi::lua_tonumber(lstate, n)),
30
31        _ => Box::new("other"),
32    }
33}
34
35/// Assumes that the value at index `index` is a table and returns whether it's
36/// an array table (as opposed to a dictionary table).
37pub unsafe fn is_table_array(lstate: *mut lua_State, index: c_int) -> bool {
38    ffi::lua_pushnil(lstate);
39
40    if ffi::lua_next(lstate, index - 1) == 0 {
41        // Empty table.
42        if ffi::lua_getmetatable(lstate, index) == 0 {
43            return true;
44        }
45        ffi::lua_pop(lstate, 1);
46        return false;
47    }
48
49    let ty = ffi::lua_type(lstate, -2);
50    ffi::lua_pop(lstate, 2);
51    ty == ffi::LUA_TNUMBER
52}
53
54/// Returns the type of the Lua value at a given stack index.
55pub unsafe fn debug_type(lstate: *mut lua_State, n: c_int) -> impl Display {
56    CStr::from_ptr(ffi::luaL_typename(lstate, n)).to_string_lossy()
57}
58
59/// Pretty prints the contents of the Lua stack to the Neovim message area.
60pub unsafe fn debug_stack(lstate: *mut lua_State) {
61    let height = ffi::lua_gettop(lstate);
62
63    let stack_pp = (1..height + 1)
64        .map(|n| {
65            let idx = height + 1 - n;
66            let value = debug_value(lstate, -n);
67            let typename = debug_type(lstate, -n);
68            format!("{idx}: {value} ({typename})")
69        })
70        .collect::<Vec<String>>()
71        .join("\n");
72
73    crate::print!("{stack_pp}");
74}
75
76pub unsafe fn handle_error<E: std::error::Error + ?Sized>(
77    lstate: *mut lua_State,
78    err: &E,
79) -> ! {
80    let msg = err.to_string();
81    ffi::lua_pushlstring(lstate, msg.as_ptr() as *const _, msg.len());
82    ffi::lua_error(lstate);
83}
84
85pub fn type_name(ty: c_int) -> &'static str {
86    match ty {
87        ffi::LUA_TNONE => "empty stack",
88        ffi::LUA_TNIL => "nil",
89        ffi::LUA_TBOOLEAN => "boolean",
90        ffi::LUA_TLIGHTUSERDATA => "light userdata",
91        ffi::LUA_TNUMBER => "number",
92        ffi::LUA_TSTRING => "string",
93        ffi::LUA_TTABLE => "table",
94        ffi::LUA_TFUNCTION => "function",
95        ffi::LUA_TUSERDATA => "userdata",
96        ffi::LUA_TTHREAD => "thread",
97        _ => unreachable!(),
98    }
99}