From beea6c33fcb2f5bd16f40e1919b61a89b4aaecb6 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 25 Feb 2024 17:05:16 -0500 Subject: [PATCH] Implement methods for iterating through a Table --- src/lib.rs | 6 ++++ src/lref.rs | 2 +- src/table.rs | 20 ++++++++++++- src/table_iter.rs | 54 +++++++++++++++++++++++++++++++++++ src/table_pairs.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 src/table_iter.rs create mode 100644 src/table_pairs.rs diff --git a/src/lib.rs b/src/lib.rs index b0bc496..f4917ee 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,12 @@ pub use lref::*; pub mod variadic; pub use variadic::*; +pub mod table_iter; +pub use table_iter::*; + +pub mod table_pairs; +pub use table_pairs::*; + #[cfg(test)] pub mod tests; diff --git a/src/lref.rs b/src/lref.rs index 3f14f24..a33a4d4 100755 --- a/src/lref.rs +++ b/src/lref.rs @@ -12,7 +12,7 @@ use mlua_sys as lua; #[derive(Clone)] pub struct LuaRef<'a> { lref: Arc, - state: &'a State, + pub(crate) state: &'a State, } impl<'a> Drop for LuaRef<'a> { diff --git a/src/table.rs b/src/table.rs index 758afab..7324b32 100755 --- a/src/table.rs +++ b/src/table.rs @@ -1,6 +1,6 @@ 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)] pub struct Table<'a> { @@ -287,8 +287,26 @@ impl<'a> Table<'a> { pub fn try_from(state: &'a State, proxy: T) -> Result> { 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>(&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, V: FromLua<'a>>(&self) -> TablePairs<'a, K, V> { + TablePairs::new(self.lref.clone()) + } } + impl<'a> PushToLuaStack<'a> for Table<'a> { unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> { // no need to ensure stack, the LuaRef does it for us diff --git a/src/table_iter.rs b/src/table_iter.rs new file mode 100644 index 0000000..1c9fe45 --- /dev/null +++ b/src/table_iter.rs @@ -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, +} + +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:: + } + } +} + +impl<'a, T: FromLua<'a>> Iterator for TableIter<'a, T> { + type Item = crate::Result; + + fn next(&mut self) -> Option { + 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)); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/table_pairs.rs b/src/table_pairs.rs new file mode 100644 index 0000000..f5e66d4 --- /dev/null +++ b/src/table_pairs.rs @@ -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>, + _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> { + 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 { + 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)) + } + } + } +} \ No newline at end of file