noita_entangled_worlds/ewext/src/lua_state.rs

149 lines
3.8 KiB
Rust
Raw Normal View History

use std::{
cell::Cell,
ffi::{c_char, c_int, CStr},
2024-11-24 23:12:35 +03:00
mem, slice,
};
2024-11-24 23:12:35 +03:00
use eyre::{bail, Context, OptionExt};
2024-11-21 18:50:10 +03:00
use crate::{
lua_bindings::{lua_CFunction, lua_State, LUA_GLOBALSINDEX},
LUA,
};
2024-11-21 17:08:03 +03:00
thread_local! {
static CURRENT_LUA_STATE: Cell<Option<LuaState>> = Cell::default();
}
2024-11-21 17:08:03 +03:00
#[derive(Clone, Copy)]
2024-11-25 02:33:02 +03:00
pub struct LuaState {
2024-11-21 18:50:10 +03:00
lua: *mut lua_State,
}
2024-11-21 17:08:03 +03:00
impl LuaState {
2024-11-25 02:33:02 +03:00
pub fn new(lua: *mut lua_State) -> Self {
2024-11-21 18:50:10 +03:00
Self { lua }
}
/// Returns a lua state that is considered "current". Usually set when we get called from noita.
2024-11-25 02:33:02 +03:00
pub fn current() -> eyre::Result<Self> {
CURRENT_LUA_STATE
.get()
.ok_or_eyre("No current lua state available")
}
2024-11-25 02:33:02 +03:00
pub fn make_current(self) {
CURRENT_LUA_STATE.set(Some(self));
}
2024-11-21 18:50:10 +03:00
pub(crate) fn raw(&self) -> *mut lua_State {
self.lua
2024-11-21 17:08:03 +03:00
}
2024-11-25 02:33:02 +03:00
pub fn to_integer(&self, index: i32) -> isize {
2024-11-21 18:50:10 +03:00
unsafe { LUA.lua_tointeger(self.lua, index) }
}
2024-11-25 02:33:02 +03:00
pub fn to_number(&self, index: i32) -> f64 {
2024-11-24 23:12:35 +03:00
unsafe { LUA.lua_tonumber(self.lua, index) }
}
2024-11-25 02:33:02 +03:00
pub fn to_bool(&self, index: i32) -> bool {
2024-11-24 23:12:35 +03:00
unsafe { LUA.lua_toboolean(self.lua, index) > 0 }
}
2024-11-25 02:33:02 +03:00
pub fn to_string(&self, index: i32) -> eyre::Result<String> {
2024-11-24 23:12:35 +03:00
let mut size = 0;
let buf = unsafe { LUA.lua_tolstring(self.lua, index, &mut size) };
if buf.is_null() {
bail!("Expected a string, but got a null pointer");
}
let slice = unsafe { slice::from_raw_parts(buf as *const u8, size) };
Ok(String::from_utf8(slice.to_owned())
.context("Attempting to get lua string, expecting it to be utf-8")?)
}
2024-11-25 02:33:02 +03:00
pub fn to_cfunction(&self, index: i32) -> lua_CFunction {
2024-11-21 18:50:10 +03:00
unsafe { LUA.lua_tocfunction(self.lua, index) }
}
2024-11-25 02:33:02 +03:00
pub fn push_number(&self, val: f64) {
2024-11-24 23:12:35 +03:00
unsafe { LUA.lua_pushnumber(self.lua, val) };
}
2024-11-25 02:33:02 +03:00
pub fn push_integer(&self, val: isize) {
2024-11-24 23:12:35 +03:00
unsafe { LUA.lua_pushinteger(self.lua, val) };
}
2024-11-25 02:33:02 +03:00
pub fn push_bool(&self, val: bool) {
2024-11-24 23:12:35 +03:00
unsafe { LUA.lua_pushboolean(self.lua, val as i32) };
}
2024-11-25 02:33:02 +03:00
pub fn push_string(&self, s: &str) {
unsafe {
2024-11-24 23:12:35 +03:00
LUA.lua_pushlstring(self.lua, s.as_bytes().as_ptr() as *const c_char, s.len());
}
}
2024-11-25 02:33:02 +03:00
pub fn push_nil(&self) {
unsafe { LUA.lua_pushnil(self.lua) }
}
pub fn call(&self, nargs: i32, nresults: i32) {
2024-11-24 23:12:35 +03:00
unsafe { LUA.lua_call(self.lua, nargs, nresults) };
}
2024-11-25 02:33:02 +03:00
pub fn get_global(&self, name: &CStr) {
2024-11-21 18:50:10 +03:00
unsafe { LUA.lua_getfield(self.lua, LUA_GLOBALSINDEX, name.as_ptr()) };
}
2024-11-25 02:33:02 +03:00
pub fn pop_last(&self) {
2024-11-21 18:50:10 +03:00
unsafe { LUA.lua_settop(self.lua, -2) };
2024-11-21 17:08:03 +03:00
}
2024-11-25 02:33:02 +03:00
pub fn pop_last_n(&self, n: i32) {
unsafe { LUA.lua_settop(self.lua, -1 - (n)) };
}
/// Raise an error with message `s`
///
/// This takes String so that it gets deallocated properly, as this functions doesn't return.
unsafe fn raise_error(&self, s: String) -> ! {
self.push_string(&s);
mem::drop(s);
unsafe { LUA.lua_error(self.lua) };
// lua_error does not return.
unreachable!()
}
}
pub(crate) trait LuaFnRet {
fn do_return(self, lua: LuaState) -> c_int;
}
/// Function intends to return several values that it has on stack.
pub(crate) struct ValuesOnStack(pub(crate) c_int);
impl LuaFnRet for ValuesOnStack {
fn do_return(self, _lua: LuaState) -> c_int {
self.0
}
}
impl LuaFnRet for () {
fn do_return(self, _lua: LuaState) -> c_int {
0
}
}
impl<R: LuaFnRet> LuaFnRet for eyre::Result<R> {
fn do_return(self, lua: LuaState) -> c_int {
match self {
Ok(ok) => ok.do_return(lua),
Err(err) => unsafe {
lua.raise_error(format!("Error in ewext call: {:?}", err));
},
}
}
2024-11-21 17:08:03 +03:00
}