pm_rlua/
lua_tables.rs

1use std::marker::PhantomData;
2
3use libc;
4
5use td_clua::{self, lua_State};
6use LuaGuard;
7use LuaPush;
8use LuaRead;
9/// Represents a table stored in the Lua context.
10///
11/// Loading this type mutably borrows the Lua context.
12pub struct LuaTable {
13    table: *mut lua_State,
14    pop: i32,
15    index: i32,
16}
17
18impl LuaRead for LuaTable {
19    fn lua_read_with_pop(lua: *mut lua_State, index: i32, pop: i32) -> Option<LuaTable> {
20        if unsafe { td_clua::lua_istable(lua, index) } {
21            for _ in 0..pop {
22                unsafe {
23                    td_clua::lua_pushnil(lua);
24                }
25            }
26            Some(LuaTable {
27                table: lua,
28                pop: pop,
29                index: index,
30            })
31        } else {
32            None
33        }
34    }
35}
36
37impl Drop for LuaTable {
38    fn drop(&mut self) {
39        if self.pop != 0 {
40            unsafe {
41                td_clua::lua_pop(self.table, self.pop);
42            };
43            self.pop = 0;
44        }
45    }
46}
47
48/// Iterator that enumerates the content of a Lua table.
49// while the LuaTableIterator is active, the current key is constantly pushed over the table
50pub struct LuaTableIterator<'t, K, V> {
51    table: &'t mut LuaTable,
52    finished: bool, // if true, the key is not on the stack anymore
53    marker: PhantomData<(K, V)>,
54}
55
56impl LuaTable {
57    /// Destroys the LuaTable and returns its inner Lua context. Useful when it takes Lua by value.
58    pub fn into_inner(self) -> *mut lua_State {
59        self.table
60    }
61
62    /// Iterates over the elements inside the table.
63    pub fn iter<K, V>(&mut self) -> LuaTableIterator<K, V> {
64        unsafe { td_clua::lua_pushnil(self.table) };
65
66        LuaTableIterator {
67            table: self,
68            finished: false,
69            marker: PhantomData,
70        }
71    }
72
73    /// Loads a value in the table given its index.
74    pub fn query<'a, R, I>(&'a mut self, index: I) -> Option<R>
75    where
76        R: LuaRead,
77        I: LuaPush,
78    {
79        index.push_to_lua(self.table);
80        unsafe {
81            td_clua::lua_gettable(
82                self.table,
83                if self.index > 0 {
84                    self.index
85                } else {
86                    self.index - 1
87                },
88            );
89        }
90        let _guard = LuaGuard::new(self.table, 1);
91        LuaRead::lua_read_with_pop(self.table, -1, 1)
92    }
93
94    /// Inserts or modifies an elements of the table.
95    pub fn set<I, V>(&mut self, index: I, value: V)
96    where
97        I: LuaPush,
98        V: LuaPush,
99    {
100        index.push_to_lua(self.table);
101        value.push_to_lua(self.table);
102        unsafe {
103            td_clua::lua_settable(
104                self.table,
105                if self.index > 0 {
106                    self.index
107                } else {
108                    self.index - 2
109                },
110            );
111        }
112    }
113
114    /// Inserts or modifies an elements of the table.
115    pub fn register<I>(&mut self, index: I, func: extern "C" fn(*mut lua_State) -> libc::c_int)
116    where
117        I: LuaPush,
118    {
119        index.push_to_lua(self.table);
120        unsafe {
121            td_clua::lua_pushcfunction(self.table, func);
122            td_clua::lua_settable(
123                self.table,
124                if self.index > 0 {
125                    self.index
126                } else {
127                    self.index - 2
128                },
129            );
130        }
131    }
132
133    // /// Inserts an empty table, then loads it.
134    pub fn empty_table<I>(&mut self, index: I) -> LuaTable
135    where
136        I: LuaPush + Clone,
137    {
138        index.clone().push_to_lua(self.table);
139        unsafe {
140            td_clua::lua_newtable(self.table);
141            td_clua::lua_settable(
142                self.table,
143                if self.index > 0 {
144                    self.index
145                } else {
146                    self.index - 2
147                },
148            );
149        }
150        self.query(index).unwrap()
151    }
152
153    pub fn table_len(&mut self) -> usize {
154        unsafe { td_clua::lua_rawlen(self.table, self.index) }
155    }
156
157    // /// Obtains or create the metatable of the table.
158    pub fn get_or_create_metatable(&mut self) -> LuaTable {
159        let result = unsafe { td_clua::lua_getmetatable(self.table, self.index) };
160
161        if result == 0 {
162            unsafe {
163                td_clua::lua_newtable(self.table);
164                td_clua::lua_setmetatable(self.table, -2);
165                let r = td_clua::lua_getmetatable(self.table, self.index);
166                assert!(r != 0);
167            }
168        }
169
170        LuaTable {
171            table: self.table,
172            pop: 1,
173            index: -1,
174        }
175    }
176}
177
178impl<'t, K, V> Iterator for LuaTableIterator<'t, K, V>
179where
180    K: LuaRead + 'static,
181    V: LuaRead + 'static,
182{
183    type Item = Option<(K, V)>;
184
185    fn next(&mut self) -> Option<Option<(K, V)>> {
186        if self.finished {
187            return None;
188        }
189        let state = self.table.table;
190        // this call pushes the next key and value on the stack
191        if unsafe { !td_clua::lua_istable(state, -2) || td_clua::lua_next(state, -2) == 0 } {
192            self.finished = true;
193            return None;
194        }
195
196        let key = LuaRead::lua_read_at_position(state, -2);
197        let value = LuaRead::lua_read_at_position(state, -1);
198        // removing the value, leaving only the key on the top of the stack
199        unsafe { td_clua::lua_pop(state, 1) };
200
201        if key.is_none() || value.is_none() {
202            Some(None)
203        } else {
204            Some(Some((key.unwrap(), value.unwrap())))
205        }
206    }
207}
208
209impl<'t, K, V> Drop for LuaTableIterator<'t, K, V> {
210    fn drop(&mut self) {
211        if !self.finished {
212            unsafe { td_clua::lua_pop(self.table.table, 1) }
213        }
214    }
215}