1use crate::lua::{
2 self, FromLuaMulti, Function, Result, ToLuaMulti, Value, ffi,
3 traits::{FromLua, ObjectLike, ToLua},
4};
5
6#[derive(Clone, Debug)]
7pub struct Table(pub(crate) Value);
8
9impl Table {
10 pub fn set(&self, state: &lua::State, key: impl ToLua, value: impl ToLua) -> Result<()> {
11 if !self.has_metatable(state) {
12 self.raw_set(state, key, value);
15 return Ok(());
16 }
17
18 self.set_protected(state, key, value)
20 }
21
22 pub fn get<V: FromLua>(&self, state: &lua::State, key: impl ToLua) -> Result<V> {
23 if !self.has_metatable(state) {
24 return self.raw_get(state, key);
25 }
26
27 self.get_protected(state, key)
28 }
29
30 pub fn len(&self, state: &lua::State) -> Result<usize> {
32 Ok(self.raw_len(state))
33 }
34
35 pub fn raw_set(&self, state: &lua::State, key: impl ToLua, value: impl ToLua) {
36 let _sg = state.stack_guard();
37
38 self.push_to_stack(state); key.push_to_stack(state); value.push_to_stack(state); ffi::lua_rawset(state.0, -3);
42 }
43
44 pub fn raw_get<V: FromLua>(&self, state: &lua::State, key: impl ToLua) -> Result<V> {
45 let _sg = state.stack_guard();
46
47 self.push_to_stack(state); key.push_to_stack(state); ffi::lua_rawget(state.0, -2);
50
51 V::try_from_stack(state, -1)
52 }
53
54 pub fn raw_len(&self, _: &lua::State) -> usize {
56 ffi::lua_rawlen(self.0.thread().0, self.0.index())
57 }
58
59 pub fn has_metatable(&self, _: &lua::State) -> bool {
61 let thread = self.0.thread();
62 if ffi::lua_getmetatable(thread.0, self.0.index()) == 0 {
63 false
64 } else {
65 ffi::lua_pop(thread.0, 1); true
67 }
68 }
69
70 #[inline]
71 pub fn ipairs<V: FromLua>(&self, state: &lua::State) -> IPairsIter<V> {
72 IPairsIter {
73 table: self.clone(),
74 state: state.clone(),
75 index: 0,
76 len: self.raw_len(state),
77 _phantom: std::marker::PhantomData,
78 }
79 }
80
81 pub fn set_metatable(&self, _: &lua::State, metatable: Option<Table>) {
82 let ref_thread = self.0.thread().0;
83 if let Some(metatable) = &metatable {
84 ffi::lua_pushvalue(ref_thread, metatable.0.index());
85 } else {
86 ffi::lua_pushnil(ref_thread);
87 }
88 ffi::lua_setmetatable(ref_thread, self.0.index());
89 }
90
91 pub(crate) fn set_protected(
92 &self,
93 state: &lua::State,
94 key: impl ToLua,
95 value: impl ToLua,
96 ) -> Result<()> {
97 let _sg = state.stack_guard();
98
99 unsafe extern "C-unwind" fn safe_settable(state: *mut ffi::lua_State) -> i32 {
100 ffi::lua_settable(state, -3);
102 0
103 }
104
105 ffi::lua_pushcfunction(state.0, Some(safe_settable));
106 self.push_to_stack(state); key.push_to_stack(state); value.push_to_stack(state); state.protect_lua_call(3, 0)?;
110
111 Ok(())
112 }
113
114 pub(crate) fn get_protected<V: FromLua>(
115 &self,
116 state: &lua::State,
117 key: impl ToLua,
118 ) -> Result<V> {
119 let _sg = state.stack_guard();
120
121 unsafe extern "C-unwind" fn safe_gettable(state: *mut ffi::lua_State) -> i32 {
122 ffi::lua_gettable(state, -2);
124 1
125 }
126
127 ffi::lua_pushcfunction(state.0, Some(safe_gettable));
128 self.push_to_stack(state); key.push_to_stack(state); state.protect_lua_call(2, 1)?;
131
132 V::try_from_stack(state, -1)
133 }
134}
135
136impl lua::State {
137 pub fn create_table(&self) -> Table {
138 self.create_table_with_capacity(0, 0)
139 }
140
141 pub fn create_table_with_capacity(&self, narr: i32, nrec: i32) -> Table {
142 lua::ffi::lua_createtable(self.0, narr, nrec);
143 Table(Value::pop_from_stack(self))
144 }
145}
146
147impl ToLua for Table {
148 fn push_to_stack(self, state: &lua::State) {
149 self.0.push_to_stack(state);
150 }
151
152 fn to_value(self, _: &lua::State) -> Value {
153 self.0
154 }
155}
156
157impl ToLua for &Table {
158 fn push_to_stack(self, state: &lua::State) {
159 #[allow(clippy::needless_borrow)]
160 (&self.0).push_to_stack(state);
161 }
162
163 fn to_value(self, _: &lua::State) -> Value {
164 self.0.clone()
165 }
166}
167
168impl FromLua for Table {
169 fn try_from_stack(state: &lua::State, index: i32) -> Result<Self> {
170 match lua::ffi::lua_type(state.0, index) {
171 lua::ffi::LUA_TTABLE => Ok(Table(Value::from_stack(state, index))),
172 _ => Err(state.type_error(index, "table")),
173 }
174 }
175}
176
177impl ObjectLike for Table {
178 #[inline]
179 fn get<V: FromLua>(&self, state: &lua::State, key: impl ToLua) -> Result<V> {
180 self.get(state, key)
181 }
182
183 #[inline]
184 fn set(&self, state: &lua::State, key: impl ToLua, value: impl ToLua) -> Result<()> {
185 self.set(state, key, value)
186 }
187
188 #[inline]
189 fn call<R: FromLuaMulti>(
190 &self,
191 state: &lua::State,
192 name: &str,
193 args: impl ToLuaMulti,
194 ) -> lua::Result<R> {
195 let func: Function = self.get(state, name)?;
196 func.call(state, args)
197 }
198
199 #[inline]
200 fn call_method<R: FromLuaMulti>(
201 &self,
202 state: &lua::State,
203 name: &str,
204 args: impl ToLuaMulti,
205 ) -> lua::Result<R> {
206 self.call(state, name, (self, args))
207 }
208}
209
210pub struct IPairsIter<V> {
211 table: Table,
212 state: lua::State,
213 index: usize,
214 len: usize,
215 _phantom: std::marker::PhantomData<V>,
216}
217
218impl<V: FromLua> Iterator for IPairsIter<V> {
219 type Item = (usize, V);
220
221 fn next(&mut self) -> Option<Self::Item> {
222 if self.index >= self.len {
223 return None;
224 }
225 self.index += 1;
226
227 let _sg = self.state.stack_guard();
228
229 (&self.table).push_to_stack(&self.state);
230 ffi::lua_rawgeti(self.state.0, -1, self.index as i32);
231
232 V::try_from_stack(&self.state, -1)
233 .ok()
234 .map(|value| (self.index, value))
235 }
236}
237
238#[macro_export]
250macro_rules! table {
251 ($state:expr) => {
253 $state.create_table()
254 };
255 ($state:expr, [$($val:expr),* $(,)?]) => {{
257 let t = $state.create_table();
258 let mut _i = 1;
259 $(
260 t.raw_set($state, _i, $val);
261 _i += 1;
262 )*
263 t
264 }};
265 ($state:expr, { $($key:expr => $val:expr),* $(,)? }) => {{
267 let t = $state.create_table();
268 $(
269 t.raw_set($state, $key, $val);
270 )*
271 t
272 }};
273}
274
275pub use table;