use core::{ffi::CStr, mem, ptr};
use crate::{AmountReturned, CFunction, LuaState, ffi::{self, lua_CFunction, luaL_Reg}, map_ptr};
#[cfg(doc)]
use crate::reg;
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct Reg(luaL_Reg);
impl Reg {
pub const END: Self = Self(luaL_Reg { name: ptr::null(), func: None });
#[inline]
#[must_use]
pub const fn new(name: &'static CStr, func: CFunction) -> Self {
Self(luaL_Reg {
name: name.as_ptr(), func: Some(unsafe { mem::transmute::<CFunction, lua_CFunction>(func) })
})
}
#[inline]
#[must_use]
pub const unsafe fn from_raw(raw: luaL_Reg) -> Self {
Self(raw)
}
#[inline]
#[must_use]
pub const fn into_raw(self) -> luaL_Reg {
self.0
}
#[inline]
#[must_use]
pub const fn name(&self) -> Option<&'static CStr> {
map_ptr!(self.0.name, unsafe { CStr::from_ptr(self.0.name) })
}
#[inline]
#[must_use]
pub const fn func(&self) -> Option<CFunction> {
match self.0.func {
Some(func) => Some(unsafe { mem::transmute::<lua_CFunction, CFunction>(func) }),
None => None,
}
}
#[inline]
#[must_use]
pub const fn is_end(&self) -> bool {
self.0.name.is_null() && self.0.func.is_none()
}
}
#[macro_export]
macro_rules! reg {
($(($name:expr, $func:expr)),* $(,)?) => {
&[$($crate::Reg::new($name, $func)),*, $crate::Reg::END]
};
($($func:ident),* $(,)?) => {
$crate::reg![$((unsafe {core::ffi::CStr::from_bytes_with_nul_unchecked(concat!(stringify!($func), '\0').as_bytes())}, $func)),*]
}
}
impl LuaState {
#[inline]
pub fn return_module(mut self, list: &[Reg]) -> AmountReturned {
self.new_module(list);
unsafe { AmountReturned::unchecked(1) }
}
#[inline]
pub fn register_module<'a>(&mut self, name: impl Into<Option<&'a CStr>>, list: &[Reg]) {
assert!(list.last().is_some_and(Reg::is_end), "tried to set a global module using an unterminated named function list");
unsafe { ffi::luaL_register(self.0, name.into().map_or(ptr::null(), CStr::as_ptr), list.as_ptr().cast()); }
}
#[inline]
pub fn new_module(&mut self, list: &[Reg]) {
assert!(list.last().is_some_and(Reg::is_end), "tried to create a module using an unterminated named function list");
unsafe { ffi::luaL_newlib(self.0, list.as_ptr().cast()); }
}
#[inline]
pub fn new_module_table(&mut self, list: &[Reg]) {
assert!(list.last().is_some_and(Reg::is_end), "tried to create a module's table using an unterminated named function list");
unsafe { ffi::luaL_newlibtable(self.0, list.as_ptr().cast()); }
}
}