use core::{error::Error, ffi::{CStr, c_int, c_void}, fmt::{self, Display, Formatter}, mem, num::NonZeroI32, slice};
use crate::{LuaState, SafeFn, ffi::{self, lua_CFunction, lua_State}};
pub type CFunction = extern "C-unwind" fn(lua: LuaState) -> AmountReturned;
impl SafeFn for CFunction {
type RawFn = lua_CFunction;
#[inline]
unsafe fn from_raw(raw: lua_CFunction) -> Self {
unsafe { mem::transmute(raw) }
}
#[inline]
fn to_raw(self) -> lua_CFunction {
unsafe { mem::transmute(self) }
}
}
#[repr(transparent)]
#[must_use = "this `AmountReturned` should be returned to the caller"]
pub struct AmountReturned(core::ffi::c_int);
impl AmountReturned {
pub const NONE: Self = Self(0);
pub const unsafe fn unchecked(amount: i32) -> Self {
Self(amount)
}
}
pub trait Writer: FnMut(&LuaState, &[u8]) -> Result<(), NonZeroI32> {}
impl<T: FnMut(&LuaState, &[u8]) -> Result<(), NonZeroI32>> Writer for T {}
#[repr(i32)]
#[derive(Debug)]
pub enum PCallError {
RuntimeError = ffi::LUA_ERRRUN,
MemoryError = ffi::LUA_ERRMEM,
ErrorHandlerError = ffi::LUA_ERRERR,
}
impl Display for PCallError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::RuntimeError => "runtime error",
Self::MemoryError => "memory allocation error",
Self::ErrorHandlerError => "error within error handler",
})
}
}
impl Error for PCallError {}
impl PCallError {
#[inline]
pub const unsafe fn check_raw(value: i32) -> Result<(), Self> {
if value == 0 { Ok(()) } else { Err(unsafe { mem::transmute::<i32, PCallError>(value) }) }
}
}
#[repr(i32)]
#[derive(Debug)]
pub enum LoadError {
SyntaxError = ffi::LUA_ERRSYNTAX,
MemoryError = ffi::LUA_ERRMEM,
FileError = ffi::LUA_ERRFILE,
}
impl Display for LoadError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::SyntaxError => "syntax error",
Self::MemoryError => "memory allocation error",
Self::FileError => "file error",
})
}
}
impl Error for LoadError {}
impl LoadError {
#[inline]
pub const unsafe fn check_raw(value: i32) -> Result<(), Self> {
if value == 0 { Ok(()) } else { Err(unsafe { mem::transmute::<i32, LoadError>(value) }) }
}
}
impl LuaState {
#[inline]
pub fn return_any(self, amount: i32) -> AmountReturned {
assert!(amount >= 0, "cannot return a negative amount of values");
assert!(amount <= self.get_top(), "tried to return {amount} values but the stack currently holds {} values", self.get_top());
unsafe { AmountReturned::unchecked(amount) }
}
#[inline]
#[must_use]
pub fn is_function(&self, index: i32) -> bool {
unsafe { ffi::lua_isfunction(self.0, index) != 0 }
}
#[inline]
#[must_use]
pub fn is_cfunction(&self, index: i32) -> bool {
unsafe { ffi::lua_iscfunction(self.0, index) != 0 }
}
#[inline]
#[must_use]
pub fn is_lua_function(&self, index: i32) -> bool {
self.is_function(index) && !self.is_cfunction(index)
}
#[inline]
#[must_use]
pub fn to_cfunction(&self, index: i32) -> Option<lua_CFunction> {
unsafe { ffi::lua_tocfunction(self.0, index) }
}
#[inline]
pub fn push_cfunction(&mut self, func: CFunction) {
unsafe { ffi::lua_pushcfunction(self.0, func.to_raw()); }
}
#[inline]
pub fn push_cclosure(&mut self, func: CFunction, n: u8) {
assert!(i32::from(n) <= self.get_top(), "tried to push a `CFunction` with {n} upvalues but the stack currently holds {} values", self.get_top());
unsafe { ffi::lua_pushcclosure(self.0, func.to_raw(), i32::from(n)); }
}
#[inline]
#[deprecated(since = "0.3.0", note ="renamed to `LuaState::register_cfunction`")]
pub fn register_function(&mut self, name: &CStr, func: CFunction) {
self.register_cfunction(name, func);
}
#[inline]
pub fn register_cfunction(&mut self, name: &CStr, func: CFunction) {
unsafe { ffi::lua_register(self.0, name.as_ptr(), func.to_raw()); }
}
pub const MULTI_RETURN: i32 = ffi::LUA_MULTRET;
#[inline]
pub fn call(&mut self, n_args: i32, n_results: i32) {
let top = self.get_top();
assert!(top > 0, "tried to call a function despite having an empty stack");
assert!(n_args < top, "cannot pass {n_args} arguments to the called function, as there are only {top} elements");
unsafe { ffi::lua_call(self.0, n_args, n_results); }
}
#[inline]
pub fn pcall(&mut self, n_args: i32, n_results: i32, error_func: i32) -> Result<(), PCallError> {
let top = self.get_top();
assert!(top > 0, "tried to pcall a function despite having an empty stack");
assert!(n_args < top, "cannot pass {n_args} arguments to the pcalled function, as there are only {top} elements");
unsafe { PCallError::check_raw(ffi::lua_pcall(self.0, n_args, n_results, error_func)) }
}
#[inline]
pub fn cpcall<T>(&mut self, func: CFunction, ud: *mut T) -> Result<(), PCallError> {
unsafe { PCallError::check_raw(ffi::lua_cpcall(self.0, func.to_raw(), ud.cast())) }
}
#[inline]
pub fn dump<F: Writer>(&self, mut writer: F) -> Result<(), NonZeroI32> {
unsafe extern "C-unwind" fn middleware<F: Writer>(lua: *mut lua_State, ptr: *const c_void, size: usize, writer: *mut c_void) -> c_int {
unsafe { mem::transmute((&mut *writer.cast::<F>())(&LuaState::from_ptr(lua), slice::from_raw_parts(ptr.cast(), size))) }
}
unsafe { mem::transmute(ffi::lua_dump(self.0, middleware::<F>, (&raw mut writer).cast())) }
}
#[inline]
pub fn load_bytes(&mut self, bytes: &[u8], chunk_name: &CStr) -> Result<(), LoadError> {
unsafe { LoadError::check_raw(ffi::luaL_loadbuffer(self.0, bytes.as_ptr().cast(), bytes.len(), chunk_name.as_ptr())) }
}
#[inline]
pub fn load_cstring(&mut self, cstring: &CStr) -> Result<(), LoadError> {
unsafe { LoadError::check_raw(ffi::luaL_loadstring(self.0, cstring.as_ptr())) }
}
#[inline]
pub fn load_file(&mut self, filename: &CStr) -> Result<(), LoadError> {
unsafe { LoadError::check_raw(ffi::luaL_loadfile(self.0, filename.as_ptr())) }
}
pub fn return_cfunction(mut self, func: CFunction) -> AmountReturned {
self.push_cfunction(func);
unsafe { AmountReturned::unchecked(1) }
}
#[inline]
pub fn arg_check(&self, condition: bool, arg_n: i32, extra_msg: &CStr) {
if !condition {
self.arg_error(arg_n, extra_msg)
}
}
}