use core::{ffi::CStr, marker::PhantomData, ops::Not};
use crate::{LuaState, ffi};
macro_rules! iterator_impl {
($iterator:ident, $assert_err:literal; $(impla {$($impl_fn:item)*};)? $($iter_fn:item)+) => {
impl $iterator<'_> {
#[inline]
pub fn new(lua: &LuaState, index: i32) -> Self {
let iterator = Self { lua: lua.as_ptr(), _marker: PhantomData };
let mut lua = iterator.lua();
lua.push_value(index);
lua.push_nil();
lua.push_nil();
assert!(lua.is_table(-3), $assert_err, index);
iterator
}
const fn lua(&self) -> LuaState {
unsafe { LuaState::from_ptr(self.lua) }
}
$($($impl_fn)*)?
}
impl Iterator for $iterator<'_> {
type Item = (i32, i32);
$($iter_fn)+
}
impl Drop for $iterator<'_> {
#[inline]
fn drop(&mut self) {
self.lua().pop(3);
}
}
};
}
#[repr(transparent)]
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Pairs<'a> {
lua: *mut ffi::lua_State,
_marker: PhantomData<&'a LuaState>,
}
iterator_impl!(
Pairs,
"cannot iterate over all key-value pairs of the element at index {} as it is not a table";
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let mut lua = self.lua();
lua.pop(1);
if unsafe { lua.next_unchecked(-2) } {
Some((-2, -1))
} else {
lua.push_nil();
lua.push_nil();
None
}
}
);
#[repr(transparent)]
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IPairs<'a> {
lua: *mut ffi::lua_State,
_marker: PhantomData<&'a LuaState>,
}
iterator_impl!(
IPairs,
"cannot iterate over the indexed key-value pairs of the element at index {} as it is not a table";
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn next(&mut self) -> Option<Self::Item> {
let mut lua = self.lua();
let i = lua.to_integer(-2) + 1;
lua.pop(2);
lua.push_integer(i);
unsafe { ffi::lua_rawgeti(lua.0, -2, i as i32); }
lua.is_nil(-1).not().then_some((-2, -1))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.lua().object_length(-3)))
}
);
impl IPairs<'_> {
#[inline]
pub fn current_index(&self) -> usize {
self.lua().to_integer(-2).cast_unsigned()
}
}
impl LuaState {
#[inline]
#[must_use]
pub fn is_table(&self, index: i32) -> bool {
unsafe { ffi::lua_istable(self.0, index) != 0 }
}
#[inline]
pub fn new_table(&mut self) {
unsafe { ffi::lua_newtable(self.0); }
}
#[inline]
pub fn create_table(&mut self, n_arr: i32, n_rec: i32) {
unsafe { ffi::lua_createtable(self.0, n_arr, n_rec); }
}
#[inline]
pub fn get_field(&mut self, index: i32, key: &CStr) {
unsafe { ffi::lua_getfield(self.0, index, key.as_ptr()); }
}
#[inline]
pub fn set_field(&mut self, index: i32, key: &CStr) {
assert!(self.get_top() > 0, "tried to set field {key:?} for the table at index {index} despite having an empty stack");
unsafe { ffi::lua_setfield(self.0, index, key.as_ptr()); }
}
#[inline]
pub fn get_global(&mut self, name: &CStr) {
unsafe { ffi::lua_getglobal(self.0, name.as_ptr()); }
}
#[inline]
pub fn set_global(&mut self, name: &CStr) {
assert!(self.get_top() > 0, "tried to set global {name:?} despite having an empty stack");
unsafe { ffi::lua_setglobal(self.0, name.as_ptr()); }
}
#[inline]
pub fn get_table(&mut self, index: i32) {
assert!(self.get_top() > 0, "tried to get a value from the table at index {index} despite having an empty stack");
unsafe { ffi::lua_gettable(self.0, index); }
}
#[inline]
pub fn set_table(&mut self, index: i32) {
assert!(2 <= self.get_top(), "tried to set a value for the table at index {index} despite not having `v` and `k` in the stack");
unsafe { ffi::lua_settable(self.0, index); }
}
#[inline]
pub fn raw_get(&mut self, index: i32) {
assert!(self.is_table(index), "cannot raw get a value from the element at index {index} as it is not a table");
assert!(self.get_top() > 0, "tried to raw get a value from the table at index {index} despite having an empty stack");
unsafe { ffi::lua_rawget(self.0, index); }
}
#[inline]
pub fn raw_set(&mut self, index: i32) {
assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
assert!(2 <= self.get_top(), "tried to raw set a value for the table at index {index} despite not having `v` and `k` in the stack");
unsafe { ffi::lua_rawset(self.0, index); }
}
#[inline]
pub fn raw_geti(&mut self, index: i32, i: i32) {
assert!(self.is_table(index), "cannot raw get an element from the element at index {index} as it is not a table");
unsafe { ffi::lua_rawgeti(self.0, index, i); }
}
#[inline]
pub fn raw_seti(&mut self, index: i32, i: i32) {
assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
assert!(self.get_top() > 0, "tried to raw set a value for the table at index {index} despite having an empty stack");
unsafe { ffi::lua_rawseti(self.0, index, i); }
}
pub fn next(&mut self, index: i32) -> bool {
assert!(self.is_table(index), "cannot get the next key-value pair of the element at index {index} as it is not a table");
unsafe { self.next_unchecked(index) }
}
pub unsafe fn next_unchecked(&mut self, index: i32) -> bool {
unsafe { ffi::lua_next(self.0, index) != 0 }
}
#[inline]
pub fn pairs(&self, index: i32) -> Pairs<'_> {
Pairs::new(self, index)
}
#[inline]
pub fn ipairs(&self, index: i32) -> IPairs<'_> {
IPairs::new(self, index)
}
#[inline]
pub fn get_metatable(&mut self, index: i32) -> bool {
unsafe { ffi::lua_getmetatable(self.0, index) != 0 }
}
#[inline]
pub fn set_metatable(&mut self, index: i32) -> bool {
assert!(self.get_top() > 0, "tried to set the metatable for the element at index {index} despite having an empty stack");
assert!(self.is_table(-1), "cannot set the metatable for the element at index {index}, as the given metatable is not a table");
unsafe { ffi::lua_setmetatable(self.0, index) != 0 }
}
#[inline]
pub fn get_fenv(&mut self, index: i32) {
unsafe { ffi::lua_getfenv(self.0, index); }
}
#[inline]
pub fn set_fenv(&mut self, index: i32) -> bool {
assert!(self.get_top() > 0, "tried to set the environment table for the element at index {index} despite having an empty stack");
assert!(self.is_table(-1), "cannot set the environment table for the element at index {index}, as the given environment table is not a table");
unsafe { ffi::lua_setfenv(self.0, index) != 0 }
}
#[inline]
pub fn call_meta(&mut self, index: i32, method: &CStr) -> bool {
unsafe { ffi::luaL_callmeta(self.0, index, method.as_ptr()) != 0 }
}
}