use core::marker::PhantomData;
use luau_vm::thread::StackGuard;
use super::Table;
use crate::error::Error;
use crate::value::{FromLua, Value};
pub struct TablePairs<'table, 'lua, K, V> {
table: &'table Table<'lua>,
key: Option<Value<'lua>>,
_marker: PhantomData<(K, V)>,
}
impl<'lua> Table<'lua> {
pub fn pairs<K, V>(&self) -> TablePairs<'_, 'lua, K, V>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
{
TablePairs {
table: self,
key: Some(Value::Nil),
_marker: PhantomData,
}
}
pub fn for_each<K, V>(
&self,
mut function: impl FnMut(K, V) -> Result<(), Error>,
) -> Result<(), Error>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
{
unsafe {
let thread = self.thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
self.push_to(&thread)?;
let table_index = vm_thread.get_top();
vm_thread
.push_nil()
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
while vm_thread
.next(table_index)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?
!= 0
{
let key = K::from_stack(&thread, -2)?;
let value = V::from_stack(&thread, -1)?;
function(key, value)?;
vm_thread.pop(1);
}
}
Ok(())
}
}
impl<'lua, K, V> Iterator for TablePairs<'_, 'lua, K, V>
where
K: FromLua<'lua>,
V: FromLua<'lua>,
{
type Item = Result<(K, V), Error>;
fn next(&mut self) -> Option<Self::Item> {
let previous_key = self.key.take()?;
let result = (|| unsafe {
let thread = self.table.thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
self.table.push_to(&thread)?;
let table_index = vm_thread.get_top();
previous_key.push_to(&thread)?;
if vm_thread
.next(table_index)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?
== 0
{
return Ok(None);
}
let key = Value::from_stack(&thread, -2)?;
let next_key = key.try_clone()?;
let key = K::from_lua(key, thread.lua_ref())?;
let value = V::from_stack(&thread, -1)?;
Ok(Some((next_key, key, value)))
})();
match result {
Ok(Some((next_key, key, value))) => {
self.key = Some(next_key);
Some(Ok((key, value)))
}
Ok(None) => None,
Err(error) => Some(Err(error)),
}
}
}