Implement methods for iterating through a Table

This commit is contained in:
SeanOMik 2024-02-25 17:05:16 -05:00
parent 70e2985cc4
commit beea6c33fc
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
5 changed files with 150 additions and 2 deletions

View File

@ -37,6 +37,12 @@ pub use lref::*;
pub mod variadic; pub mod variadic;
pub use variadic::*; pub use variadic::*;
pub mod table_iter;
pub use table_iter::*;
pub mod table_pairs;
pub use table_pairs::*;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;

View File

@ -12,7 +12,7 @@ use mlua_sys as lua;
#[derive(Clone)] #[derive(Clone)]
pub struct LuaRef<'a> { pub struct LuaRef<'a> {
lref: Arc<i32>, lref: Arc<i32>,
state: &'a State, pub(crate) state: &'a State,
} }
impl<'a> Drop for LuaRef<'a> { impl<'a> Drop for LuaRef<'a> {

View File

@ -1,6 +1,6 @@
use mlua_sys as lua; use mlua_sys as lua;
use crate::{ensure_type, AsLua, Error, FromLua, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State, Value}; use crate::{ensure_type, AsLua, Error, FromLua, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State, TableIter, TablePairs, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Table<'a> { pub struct Table<'a> {
@ -287,8 +287,26 @@ impl<'a> Table<'a> {
pub fn try_from<T: TableProxy + Sized>(state: &'a State, proxy: T) -> Result<Table<'a>> { pub fn try_from<T: TableProxy + Sized>(state: &'a State, proxy: T) -> Result<Table<'a>> {
proxy.as_table(state) proxy.as_table(state)
} }
/// Returns an iterator over the sequence values in the table, this does not execute any metamethods.
///
/// # Note
/// elua cannot protect the table from mutation from another source
pub fn sequence_iter<T: FromLua<'a>>(&self) -> TableIter<'a, T> {
TableIter::new(self.lref.clone())
} }
/// Returns an iterator that returns the key value pairs of the Lua table.
/// This is similar to lua's `pairs` method, but it doesn't execute the `__pairs` metamethod.
///
/// # Note
/// elua cannot protect the table from mutation from another source
pub fn pairs<K: FromLua<'a>, V: FromLua<'a>>(&self) -> TablePairs<'a, K, V> {
TablePairs::new(self.lref.clone())
}
}
impl<'a> PushToLuaStack<'a> for Table<'a> { impl<'a> PushToLuaStack<'a> for Table<'a> {
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
// no need to ensure stack, the LuaRef does it for us // no need to ensure stack, the LuaRef does it for us

54
src/table_iter.rs Normal file
View File

@ -0,0 +1,54 @@
use std::marker::PhantomData;
use crate::{FromLua, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, Value};
use mlua_sys as lua;
pub struct TableIter<'a, T: FromLua<'a>> {
lref: LuaRef<'a>,
index: i64,
_marker: PhantomData<T>,
}
impl<'a, T: FromLua<'a>> TableIter<'a, T> {
pub(crate) fn new(table_ref: LuaRef<'a>) -> Self {
Self {
lref: table_ref,
index: 1,
_marker: PhantomData::<T>
}
}
}
impl<'a, T: FromLua<'a>> Iterator for TableIter<'a, T> {
type Item = crate::Result<T>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let state = self.lref.state;
let s = state.state_ptr();
if let Err(e) = state.ensure_stack(1) {
return Some(Err(e));
}
let _g = StackGuard::new(state);
if let Err(e) = self.lref.push_to_lua_stack(state) {
return Some(Err(e));
}
match lua::lua_rawgeti(s, -1, self.index) {
lua::LUA_TNIL => None,
_ => {
self.index += 1;
match Value::from_lua_stack(state) {
Ok(v) => Some(T::from_lua(state, v)),
Err(e) => {
return Some(Err(e));
}
}
}
}
}
}
}

70
src/table_pairs.rs Normal file
View File

@ -0,0 +1,70 @@
use std::marker::PhantomData;
use crate::{AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, Value};
use mlua_sys as lua;
pub struct TablePairs<'a, K: FromLua<'a>, V: FromLua<'a>> {
lref: LuaRef<'a>,
last_val: Option<Value<'a>>,
_marker: PhantomData<(K, V)>
}
impl<'a, K: FromLua<'a>, V: FromLua<'a>> TablePairs<'a, K, V> {
pub(crate) fn new(table_ref: LuaRef<'a>) -> Self {
Self {
lref: table_ref,
last_val: Some(Value::Nil),
_marker: PhantomData::<(K, V)>,
}
}
unsafe fn get_item(&mut self) -> crate::Result<Option<(K, V)>> {
let state = self.lref.state;
let s = state.state_ptr();
let _g = StackGuard::new(state);
state.ensure_stack(5)?;
self.lref.push_to_lua_stack(state)?;
self.last_val.push_to_lua_stack(state)?;
if lua::lua_next(s, -2) != 0 {
let right_val = Value::from_lua_stack(state)?;
let left_val = Value::from_lua_stack(state)?;
self.last_val = Some(left_val.clone());
let right = V::from_lua(state, right_val)?;
let left = K::from_lua(state, left_val)?;
Ok(Some((left, right)))
} else {
self.last_val = None;
Ok(None)
}
}
}
impl<'a, K: FromLua<'a>, V: FromLua<'a>> Iterator for TablePairs<'a, K, V> {
type Item = crate::Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
if let None = self.last_val {
return None;
}
match unsafe { self.get_item() } {
Ok(Some(tup)) => {
Some(Ok(tup))
},
Ok(None) => {
None
},
Err(e) => {
Some(Err(e))
}
}
}
}