use std::{
ffi::{CStr, CString},
mem::size_of,
};
pub type StatePtr = *mut dmsdk_ffi::lua_State;
pub type Function = extern "C" fn(l: StatePtr) -> i32;
pub type Reg = &'static [(&'static str, Function)];
#[derive(Clone, Copy)]
pub struct State {
ptr: StatePtr,
}
impl State {
pub unsafe fn new(ptr: StatePtr) -> Self {
Self { ptr }
}
pub fn ptr(&self) -> StatePtr {
self.ptr
}
}
#[macro_export]
macro_rules! declare_functions {
($ident:ident, $($func:ident),*) => {
paste! {
const $ident: lua::Reg = &[$((stringify!($func), [<_wrapped_ $func>]), )*];
$(
#[no_mangle]
extern "C" fn [<_wrapped_ $func>](l: lua::StatePtr) -> i32 {
unsafe {
$func(lua::State::new(l))
}
}
)*
}
};
}
pub fn push_userdata<T>(l: State, userdata: T) {
unsafe {
let ptr = dmsdk_ffi::lua_newuserdata(l.ptr, size_of::<T>()) as *mut T;
ptr.write(userdata);
}
}
pub unsafe fn to_userdata<T>(l: State, i: i32) -> T {
let ptr = dmsdk_ffi::lua_touserdata(l.ptr, i) as *mut T;
ptr.read()
}
pub fn push_integer(l: State, n: isize) {
unsafe {
dmsdk_ffi::lua_pushinteger(l.ptr, n);
}
}
pub fn push_string(l: State, s: &str) {
let s = CString::new(s).unwrap();
unsafe {
dmsdk_ffi::lua_pushstring(l.ptr, s.as_ptr());
}
}
pub fn check_string(l: State, i: i32) -> String {
unsafe {
let ptr = dmsdk_ffi::luaL_checklstring(l.ptr, i, std::ptr::null_mut());
let cstr = CStr::from_ptr(ptr);
String::from_utf8_lossy(cstr.to_bytes()).into_owned()
}
}
pub fn check_bytes(l: State, i: i32) -> Vec<u8> {
let mut length = 0;
let ptr = unsafe { dmsdk_ffi::luaL_checklstring(l.ptr, i, &mut length) };
let mut vec = Vec::with_capacity(length as usize);
for i in 0..length {
let byte = unsafe { *ptr.add(i as usize) as u8 };
vec.push(byte);
}
vec
}
pub fn check_int(l: State, i: i32) -> isize {
unsafe { dmsdk_ffi::luaL_checkinteger(l.ptr, i) }
}
pub fn check_float(l: State, i: i32) -> f64 {
unsafe { dmsdk_ffi::luaL_checknumber(l.ptr, i) }
}
pub fn to_bool(l: State, i: i32) -> bool {
unsafe { dmsdk_ffi::lua_toboolean(l.ptr, i) > 0 }
}
pub fn get_top(l: State) -> i32 {
unsafe { dmsdk_ffi::lua_gettop(l.ptr) }
}
pub fn new_table(l: State) {
unsafe {
dmsdk_ffi::lua_createtable(l.ptr, 0, 0);
}
}
pub fn raw_set_i(l: State, i: i32, n: i32) {
unsafe {
dmsdk_ffi::lua_rawseti(l.ptr, i, n);
}
}
pub fn pop(l: State, n: i32) {
unsafe {
dmsdk_ffi::lua_settop(l.ptr, -n - 1);
}
}
pub fn register(l: State, lib_name: &str, functions: &[(&str, Function)]) {
let lib_name = CString::new(lib_name).unwrap();
let cstringed_fns: Vec<(CString, Function)> = functions
.iter()
.map(|&(name, func)| (CString::new(name).unwrap(), func))
.collect();
let mut lua_fns: Vec<dmsdk_ffi::luaL_Reg> = Vec::with_capacity(cstringed_fns.len() + 1);
for &(ref name, func) in &cstringed_fns {
lua_fns.push(dmsdk_ffi::luaL_Reg {
name: name.as_ptr(),
func: Some(func),
});
}
lua_fns.push(dmsdk_ffi::luaL_Reg {
name: std::ptr::null(),
func: None,
});
unsafe {
dmsdk_ffi::luaL_register(l.ptr, lib_name.as_ptr(), lua_fns.as_ptr());
}
}
#[doc(hidden)]
pub fn __error(l: State, str: &str) -> ! {
let s = CString::new(str).unwrap();
unsafe {
dmsdk_ffi::luaL_error(l.ptr, s.as_ptr());
}
panic!("luaL_error failed to return!")
}
#[doc(hidden)]
pub fn __push_fstring(l: State, str: &str) {
let s = CString::new(str).unwrap();
unsafe {
dmsdk_ffi::lua_pushfstring(l.ptr, s.as_ptr());
}
}
#[macro_export]
macro_rules! __internal_lua_error {
($l:ident, $($arg:tt)*) => {
lua::__error($l, &format!($($arg)*));
};
}
#[macro_export]
macro_rules! __internal_push_fstring {
($l:ident, $($arg:tt)*) => {
lua::__push_fstring($l, &format!($($arg)*));
};
}
#[doc(inline)]
pub use crate::{
__internal_lua_error as error, __internal_push_fstring as push_fstring, declare_functions,
};