use std::marker::PhantomData;
use libc;
use crate::{LuaPush, LuaRead, lua_State, sys};
pub struct LuaTable {
table: *mut lua_State,
pop : i32,
index : i32,
}
impl LuaRead for LuaTable {
fn lua_read_with_pop_impl(lua: *mut lua_State, index: i32, pop: i32) -> Option<LuaTable> {
if unsafe { sys::lua_istable(lua, index) } {
for _ in 0 .. pop {
unsafe { sys::lua_pushnil(lua); }
}
Some(LuaTable { table: lua, pop : pop, index : index })
} else {
None
}
}
}
impl Drop for LuaTable {
fn drop(&mut self) {
if self.pop != 0 {
unsafe { sys::lua_pop(self.table, self.pop); };
self.pop = 0;
}
}
}
pub struct LuaTableIterator<'t, K, V> {
table: &'t mut LuaTable,
finished: bool, marker: PhantomData<(K, V)>,
}
impl LuaTable {
pub fn into_inner(self) -> *mut lua_State {
self.table
}
pub fn iter<K, V>(&mut self) -> LuaTableIterator<K, V> {
unsafe { sys::lua_pushnil(self.table) };
LuaTableIterator {
table: self,
finished: false,
marker: PhantomData,
}
}
pub fn query<'a, R, I>(&'a mut self, index: I) -> Option<R>
where R: LuaRead,
I: LuaPush
{
index.push_to_lua(self.table);
unsafe { sys::lua_gettable(self.table, if self.index > 0 { self.index } else {self.index - 1}); }
LuaRead::lua_read_with_pop(self.table, -1, 1)
}
pub fn set<I, V>(&mut self, index: I, value: V)
where I: LuaPush,
V: LuaPush
{
index.push_to_lua(self.table);
value.push_to_lua(self.table);
unsafe { sys::lua_settable(self.table, if self.index > 0 { self.index } else {self.index - 2}); }
}
pub fn register<I>(&mut self, index: I, func : extern "C" fn(*mut lua_State) -> libc::c_int)
where I: LuaPush
{
index.push_to_lua(self.table);
unsafe {
sys::lua_pushcfunction(self.table, func);
sys::lua_settable(self.table, if self.index > 0 { self.index } else {self.index - 2});
}
}
pub fn empty_table<I>(&mut self, index: I) -> LuaTable
where I: LuaPush + Clone
{
index.clone().push_to_lua(self.table);
unsafe {
sys::lua_newtable(self.table);
sys::lua_settable(self.table, if self.index > 0 { self.index } else {self.index - 2});
}
self.query(index).unwrap()
}
pub fn table_len(&mut self) -> usize {
unsafe {
sys::lua_rawlen(self.table, self.index)
}
}
pub fn get_or_create_metatable(&mut self) -> LuaTable {
let result = unsafe { sys::lua_getmetatable(self.table, self.index) };
if result == 0 {
unsafe {
sys::lua_newtable(self.table);
sys::lua_setmetatable(self.table, -2);
let r = sys::lua_getmetatable(self.table, self.index);
assert!(r != 0);
}
}
LuaTable {
table: self.table,
pop: 1,
index : -1,
}
}
}
impl<'t, K, V> Iterator for LuaTableIterator<'t, K, V>
where K: LuaRead + 'static,
V: LuaRead + 'static
{
type Item = Option<(K, V)>;
fn next(&mut self) -> Option<Option<(K,V)>> {
if self.finished {
return None;
}
let state = self.table.table;
if unsafe { !sys::lua_istable(state, -2) || sys::lua_next(state, -2) == 0 } {
self.finished = true;
return None;
}
let key = LuaRead::lua_read_at_position(state, -2);
let value = LuaRead::lua_read_at_position(state, -1);
unsafe { sys::lua_pop(state, 1) };
if key.is_none() || value.is_none() {
Some(None)
} else {
Some(Some((key.unwrap(), value.unwrap())))
}
}
}
impl<'t, K, V> Drop for LuaTableIterator<'t, K, V> {
fn drop(&mut self) {
if !self.finished {
unsafe { sys::lua_pop(self.table.table, 1) }
}
}
}