use std::marker::PhantomData;
use ffi;
use LuaContext;
use AsLua;
use AsMutLua;
use Push;
use PushGuard;
use PushOne;
use LuaRead;
use Void;
#[derive(Debug)]
pub struct LuaTable<L> {
table: L,
index: i32,
}
impl<L> LuaTable<L> {
#[inline]
fn offset(&self, offset: i32) -> i32 {
if self.index >= 0 || self.index == ffi::LUA_REGISTRYINDEX {
self.index
} else {
self.index + offset
}
}
}
unsafe impl<'lua, L> AsLua<'lua> for LuaTable<L>
where L: AsLua<'lua>
{
#[inline]
fn as_lua(&self) -> LuaContext {
self.table.as_lua()
}
}
unsafe impl<'lua, L> AsMutLua<'lua> for LuaTable<L>
where L: AsMutLua<'lua>
{
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
self.table.as_mut_lua()
}
}
impl<'lua, L> LuaRead<L> for LuaTable<L>
where L: AsMutLua<'lua>
{
#[inline]
fn lua_read_at_position(mut lua: L, index: i32) -> Result<LuaTable<L>, L> {
if unsafe { ffi::lua_istable(lua.as_mut_lua().0, index) } {
Ok(LuaTable {
table: lua,
index: index,
})
} else {
Err(lua)
}
}
}
impl<'lua, L> LuaTable<L>
where L: AsMutLua<'lua>
{
#[inline]
pub fn into_inner(self) -> L {
self.table
}
#[inline]
pub fn iter<K, V>(&mut self) -> LuaTableIterator<L, K, V> {
unsafe {
ffi::lua_pushnil(self.table.as_mut_lua().0);
let raw_lua = self.table.as_lua();
LuaTableIterator {
table: self,
finished: false,
raw_lua: raw_lua,
marker: PhantomData,
}
}
}
#[inline]
pub fn get<'a, R, I, E>(&'a mut self, index: I) -> Option<R>
where R: LuaRead<PushGuard<&'a mut LuaTable<L>>>,
I: for<'b> PushOne<&'b mut &'a mut LuaTable<L>, Err = E>,
E: Into<Void>,
{
unsafe {
let mut me = self;
index.push_no_err(&mut me).assert_one_and_forget();
ffi::lua_gettable(me.as_mut_lua().0, me.offset(-1));
let raw_lua = me.as_lua();
let guard = PushGuard {
lua: me,
size: 1,
raw_lua: raw_lua,
};
if ffi::lua_isnil(raw_lua.0, -1) {
None
} else {
LuaRead::lua_read(guard).ok()
}
}
}
#[inline]
pub fn into_get<R, I, E>(mut self, index: I) -> Result<R, PushGuard<Self>>
where R: LuaRead<PushGuard<LuaTable<L>>>,
I: for<'b> PushOne<&'b mut LuaTable<L>, Err = E>,
E: Into<Void>,
{
unsafe {
index.push_no_err(&mut self).assert_one_and_forget();
ffi::lua_gettable(self.as_mut_lua().0, self.offset(-1));
let raw_lua = self.as_lua();
let guard = PushGuard {
lua: self,
size: 1,
raw_lua: raw_lua,
};
if ffi::lua_isnil(raw_lua.0, -1) {
Err(guard)
} else {
LuaRead::lua_read(guard)
}
}
}
#[inline]
pub fn set<I, V, Ei, Ev>(&mut self, index: I, value: V)
where I: for<'r> PushOne<&'r mut LuaTable<L>, Err = Ei>,
V: for<'r, 's> PushOne<&'r mut PushGuard<&'s mut LuaTable<L>>, Err = Ev>,
Ei: Into<Void>,
Ev: Into<Void>,
{
match self.checked_set(index, value) {
Ok(()) => (),
Err(_) => unreachable!(),
}
}
#[inline]
pub fn checked_set<I, V, Ke, Ve>(&mut self,
index: I,
value: V)
-> Result<(), CheckedSetError<Ke, Ve>>
where I: for<'r> PushOne<&'r mut LuaTable<L>, Err = Ke>,
V: for<'r, 's> PushOne<&'r mut PushGuard<&'s mut LuaTable<L>>, Err = Ve>
{
unsafe {
let raw_lua = self.as_mut_lua().0;
let my_offset = self.offset(-2);
let mut guard = match index.push_to_lua(self) {
Ok(guard) => {
assert_eq!(guard.size, 1);
guard
}
Err((err, _)) => {
return Err(CheckedSetError::KeyPushError(err));
}
};
match value.push_to_lua(&mut guard) {
Ok(pushed) => {
assert_eq!(pushed.size, 1);
pushed.forget()
}
Err((err, _)) => {
return Err(CheckedSetError::ValuePushError(err));
}
};
guard.forget();
ffi::lua_settable(raw_lua, my_offset);
Ok(())
}
}
#[inline]
pub fn empty_array<'s, I, E>(&'s mut self, index: I) -> LuaTable<PushGuard<&'s mut LuaTable<L>>>
where I: for<'a> PushOne<&'a mut &'s mut LuaTable<L>, Err = E> + Clone,
E: Into<Void>,
{
unsafe {
let mut me = self;
match index.clone().push_to_lua(&mut me) {
Ok(pushed) => {
assert_eq!(pushed.size, 1);
pushed.forget()
}
Err(_) => panic!(), };
match Vec::<u8>::with_capacity(0).push_to_lua(&mut me) {
Ok(pushed) => pushed.forget(),
Err(_) => panic!(), };
ffi::lua_settable(me.as_mut_lua().0, me.offset(-2));
me.get(index).unwrap()
}
}
#[inline]
pub fn get_or_create_metatable(mut self) -> LuaTable<PushGuard<L>> {
unsafe {
if ffi::lua_getmetatable(self.table.as_mut_lua().0, self.index) == 0 {
ffi::lua_newtable(self.table.as_mut_lua().0);
ffi::lua_setmetatable(self.table.as_mut_lua().0, self.offset(-1));
let r = ffi::lua_getmetatable(self.table.as_mut_lua().0, self.index);
debug_assert!(r != 0);
}
let raw_lua = self.as_lua();
LuaTable {
table: PushGuard {
lua: self.table,
size: 1,
raw_lua: raw_lua,
},
index: -1,
}
}
}
#[inline]
pub fn registry(lua: L) -> LuaTable<L> {
LuaTable {
table: lua,
index: ffi::LUA_REGISTRYINDEX,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum CheckedSetError<K, V> {
KeyPushError(K),
ValuePushError(V),
}
#[derive(Debug)]
pub struct LuaTableIterator<'t, L: 't, K, V> {
table: &'t mut LuaTable<L>,
finished: bool, raw_lua: LuaContext,
marker: PhantomData<(K, V)>,
}
unsafe impl<'t, 'lua, L, K, V> AsLua<'lua> for LuaTableIterator<'t, L, K, V>
where L: AsMutLua<'lua>
{
#[inline]
fn as_lua(&self) -> LuaContext {
self.table.as_lua()
}
}
unsafe impl<'t, 'lua, L, K, V> AsMutLua<'lua> for LuaTableIterator<'t, L, K, V>
where L: AsMutLua<'lua>
{
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
self.table.as_mut_lua()
}
}
impl<'t, 'lua, L, K, V> Iterator for LuaTableIterator<'t, L, K, V>
where L: AsMutLua<'lua> + 't,
K: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static,
V: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static
{
type Item = Option<(K, V)>;
#[inline]
fn next(&mut self) -> Option<Option<(K, V)>> {
unsafe {
if self.finished {
return None;
}
if ffi::lua_next(self.table.as_mut_lua().0, self.table.offset(-1)) == 0 {
self.finished = true;
return None;
}
let mut me = self;
let key = LuaRead::lua_read_at_position(&mut me, -2).ok();
let value = LuaRead::lua_read_at_position(&mut me, -1).ok();
ffi::lua_pop(me.table.as_mut_lua().0, 1);
if key.is_none() || value.is_none() {
Some(None)
} else {
Some(Some((key.unwrap(), value.unwrap())))
}
}
}
}
impl<'t, L, K, V> Drop for LuaTableIterator<'t, L, K, V> {
#[inline]
fn drop(&mut self) {
unsafe {
if !self.finished {
ffi::lua_pop(self.raw_lua.0, 1);
}
}
}
}
#[cfg(test)]
mod tests {
use Lua;
use LuaTable;
use PushGuard;
use function0;
#[test]
fn iterable() {
let mut lua = Lua::new();
let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
let mut counter = 0;
for (key, value) in table.iter().filter_map(|e| e) {
let _: u32 = key;
let _: u32 = value;
assert_eq!(key + value, 10);
counter += 1;
}
assert_eq!(counter, 3);
}
#[test]
fn iterable_multipletimes() {
let mut lua = Lua::new();
let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
for _ in 0..10 {
let table_content: Vec<Option<(u32, u32)>> = table.iter().collect();
assert_eq!(table_content,
vec![Some((1, 9)), Some((2, 8)), Some((3, 7))]);
}
}
#[test]
fn get_set() {
let mut lua = Lua::new();
let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
let x: i32 = table.get(2).unwrap();
assert_eq!(x, 8);
table.set(3, "hello");
let y: String = table.get(3).unwrap();
assert_eq!(y, "hello");
let z: i32 = table.get(1).unwrap();
assert_eq!(z, 9);
}
#[test]
fn table_over_table() {
let mut lua = Lua::new();
lua.execute::<()>("a = { 9, { 8, 7 }, 6 }").unwrap();
let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
let x: i32 = table.get(1).unwrap();
assert_eq!(x, 9);
{
let mut subtable = table.get::<LuaTable<_>, _, _>(2).unwrap();
let y: i32 = subtable.get(1).unwrap();
assert_eq!(y, 8);
let z: i32 = subtable.get(2).unwrap();
assert_eq!(z, 7);
}
let w: i32 = table.get(3).unwrap();
assert_eq!(w, 6);
}
#[test]
fn metatable() {
let mut lua = Lua::new();
let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
{
let table = lua.get::<LuaTable<_>, _>("a").unwrap();
let mut metatable = table.get_or_create_metatable();
fn handler() -> i32 {
5
};
metatable.set("__add".to_string(), function0(handler));
}
let r: i32 = lua.execute("return a + a").unwrap();
assert_eq!(r, 5);
}
#[test]
fn empty_array() {
let mut lua = Lua::new();
{
let mut array = lua.empty_array("a");
array.set("b", 3)
}
let mut table: LuaTable<_> = lua.get("a").unwrap();
assert!(3 == table.get("b").unwrap());
}
#[test]
fn by_value() {
let mut lua = Lua::new();
{
let mut array = lua.empty_array("a");
{
let mut array2 = array.empty_array("b");
array2.set("c", 3);
}
}
let table: LuaTable<PushGuard<Lua>> = lua.into_get("a").ok().unwrap();
let mut table2: LuaTable<PushGuard<LuaTable<PushGuard<Lua>>>> =
table.into_get("b").ok().unwrap();
assert!(3 == table2.get("c").unwrap());
let table: LuaTable<PushGuard<Lua>> = table2.into_inner().into_inner();
let mut table2: LuaTable<PushGuard<LuaTable<PushGuard<Lua>>>> =
table.into_get("b").ok().unwrap();
assert!(3 == table2.get("c").unwrap());
let table: LuaTable<PushGuard<Lua>> = table2.into_inner().into_inner();
let _lua: Lua = table.into_inner().into_inner();
}
#[test]
fn registry() {
let mut lua = Lua::new();
let mut table = LuaTable::registry(&mut lua);
table.set(3, "hello");
let y: String = table.get(3).unwrap();
assert_eq!(y, "hello");
}
#[test]
fn registry_metatable() {
let mut lua = Lua::new();
let registry = LuaTable::registry(&mut lua);
let mut metatable = registry.get_or_create_metatable();
metatable.set(3, "hello");
}
}