hlua_badtouch/lua_tables.rs
1use std::marker::PhantomData;
2
3use ffi;
4use LuaContext;
5
6use AsLua;
7use AsMutLua;
8use Push;
9use PushGuard;
10use PushOne;
11use LuaRead;
12use Void;
13
14/// Represents a table stored in the Lua context.
15///
16/// Just like you can read variables as integers and strings, you can also read Lua table by
17/// requesting a `LuaTable` object. Doing so will mutably borrow the object which you got the table
18/// from.
19///
20/// # Example: reading a global variable
21///
22/// ```
23/// let mut lua = hlua::Lua::new();
24/// lua.execute::<()>("a = {28, 92, 17};").unwrap();
25///
26/// let mut table: hlua::LuaTable<_> = lua.get("a").unwrap();
27/// for (k, v) in table.iter::<i32, i32>().filter_map(|e| e) {
28/// println!("{} => {}", k, v);
29/// }
30/// ```
31///
32#[derive(Debug)]
33pub struct LuaTable<L> {
34 table: L,
35 index: i32,
36}
37
38impl<L> LuaTable<L> {
39 // Return the index on the stack of this table, assuming -(offset - 1)
40 // items have been pushed to the stack since it was loaded.
41 // For example if you push one element over the table, call `offset(-1)` to know where the
42 // table is.
43 #[inline]
44 fn offset(&self, offset: i32) -> i32 {
45 if self.index >= 0 || self.index == ffi::LUA_REGISTRYINDEX {
46 // If this table is the registry or was indexed from the bottom of the stack, its
47 // current position will be unchanged.
48 self.index
49 } else {
50 // If this table was indexed from the top of the stack, its current
51 // index will have been pushed down by the newly-pushed items.
52 self.index + offset
53 }
54 }
55}
56
57unsafe impl<'lua, L> AsLua<'lua> for LuaTable<L>
58 where L: AsLua<'lua>
59{
60 #[inline]
61 fn as_lua(&self) -> LuaContext {
62 self.table.as_lua()
63 }
64}
65
66unsafe impl<'lua, L> AsMutLua<'lua> for LuaTable<L>
67 where L: AsMutLua<'lua>
68{
69 #[inline]
70 fn as_mut_lua(&mut self) -> LuaContext {
71 self.table.as_mut_lua()
72 }
73}
74
75impl<'lua, L> LuaRead<L> for LuaTable<L>
76 where L: AsMutLua<'lua>
77{
78 #[inline]
79 fn lua_read_at_position(mut lua: L, index: i32) -> Result<LuaTable<L>, L> {
80 if unsafe { ffi::lua_istable(lua.as_mut_lua().0, index) } {
81 Ok(LuaTable {
82 table: lua,
83 index: index,
84 })
85 } else {
86 Err(lua)
87 }
88 }
89}
90
91impl<'lua, L> LuaTable<L>
92 where L: AsMutLua<'lua>
93{
94 /// Destroys the `LuaTable` and returns its inner Lua context. Useful when it takes Lua by
95 /// value.
96 // TODO: find an example where it is useful
97 #[inline]
98 pub fn into_inner(self) -> L {
99 self.table
100 }
101
102 /// Iterates over the elements inside the table.
103 // TODO: doc
104 #[inline]
105 pub fn iter<K, V>(&mut self) -> LuaTableIterator<L, K, V> {
106 unsafe {
107 ffi::lua_pushnil(self.table.as_mut_lua().0);
108
109 let raw_lua = self.table.as_lua();
110 LuaTableIterator {
111 table: self,
112 finished: false,
113 raw_lua: raw_lua,
114 marker: PhantomData,
115 }
116 }
117 }
118
119 /// Loads a value in the table given its index.
120 ///
121 /// The index must implement the `PushOne` trait and the return type must implement the
122 /// `LuaRead` trait. See
123 /// [the documentation at the crate root](index.html#pushing-and-loading-values) for more
124 /// information.
125 ///
126 /// # Example: reading a table inside of a table.
127 ///
128 /// ```
129 /// let mut lua = hlua::Lua::new();
130 /// lua.execute::<()>("a = { 9, { 8, 7 }, 6 }").unwrap();
131 ///
132 /// let mut table = lua.get::<hlua::LuaTable<_>, _>("a").unwrap();
133 ///
134 /// assert_eq!(table.get::<i32, _, _>(1).unwrap(), 9);
135 /// assert_eq!(table.get::<i32, _, _>(3).unwrap(), 6);
136 ///
137 /// {
138 /// let mut subtable: hlua::LuaTable<_> = table.get(2).unwrap();
139 /// assert_eq!(subtable.get::<i32, _, _>(1).unwrap(), 8);
140 /// assert_eq!(subtable.get::<i32, _, _>(2).unwrap(), 7);
141 /// }
142 /// ```
143 ///
144 #[inline]
145 pub fn get<'a, R, I, E>(&'a mut self, index: I) -> Option<R>
146 where R: LuaRead<PushGuard<&'a mut LuaTable<L>>>,
147 I: for<'b> PushOne<&'b mut &'a mut LuaTable<L>, Err = E>,
148 E: Into<Void>,
149 {
150 unsafe {
151 // Because of a weird borrow error, we need to push the index by borrowing `&mut &mut L`
152 // instead of `&mut L`. `self` matches `&mut L`, so in theory we could do `&mut self`.
153 // But in practice `self` isn't mutable, so we need to move it into `me` first.
154 // TODO: remove this by simplifying the PushOne requirement ; however this is complex
155 // because of the empty_array method
156 let mut me = self;
157
158 index.push_no_err(&mut me).assert_one_and_forget();
159 ffi::lua_gettable(me.as_mut_lua().0, me.offset(-1));
160
161 let raw_lua = me.as_lua();
162 let guard = PushGuard {
163 lua: me,
164 size: 1,
165 raw_lua: raw_lua,
166 };
167
168 if ffi::lua_isnil(raw_lua.0, -1) {
169 None
170 } else {
171 LuaRead::lua_read(guard).ok()
172 }
173 }
174 }
175
176 /// Loads a value in the table, with the result capturing the table by value.
177 // TODO: doc
178 #[inline]
179 pub fn into_get<R, I, E>(mut self, index: I) -> Result<R, PushGuard<Self>>
180 where R: LuaRead<PushGuard<LuaTable<L>>>,
181 I: for<'b> PushOne<&'b mut LuaTable<L>, Err = E>,
182 E: Into<Void>,
183 {
184 unsafe {
185 index.push_no_err(&mut self).assert_one_and_forget();
186
187 ffi::lua_gettable(self.as_mut_lua().0, self.offset(-1));
188
189 let raw_lua = self.as_lua();
190 let guard = PushGuard {
191 lua: self,
192 size: 1,
193 raw_lua: raw_lua,
194 };
195
196 if ffi::lua_isnil(raw_lua.0, -1) {
197 Err(guard)
198 } else {
199 LuaRead::lua_read(guard)
200 }
201 }
202 }
203
204 /// Inserts or modifies an elements of the table.
205 ///
206 /// Contrary to `checked_set`, can only be called when writing the key and value cannot fail
207 /// (which is the case for most types).
208 ///
209 /// The index and the value must both implement the `PushOne` trait. See
210 /// [the documentation at the crate root](index.html#pushing-and-loading-values) for more
211 /// information.
212 // TODO: doc
213 #[inline]
214 pub fn set<I, V, Ei, Ev>(&mut self, index: I, value: V)
215 where I: for<'r> PushOne<&'r mut LuaTable<L>, Err = Ei>,
216 V: for<'r, 's> PushOne<&'r mut PushGuard<&'s mut LuaTable<L>>, Err = Ev>,
217 Ei: Into<Void>,
218 Ev: Into<Void>,
219 {
220 match self.checked_set(index, value) {
221 Ok(()) => (),
222 Err(_) => unreachable!(),
223 }
224 }
225
226 /// Inserts or modifies an elements of the table.
227 ///
228 /// Returns an error if we failed to write the key and the value. This can only happen for a
229 /// limited set of types. You are encouraged to use the `set` method if writing cannot fail.
230 // TODO: doc
231 #[inline]
232 pub fn checked_set<I, V, Ke, Ve>(&mut self,
233 index: I,
234 value: V)
235 -> Result<(), CheckedSetError<Ke, Ve>>
236 where I: for<'r> PushOne<&'r mut LuaTable<L>, Err = Ke>,
237 V: for<'r, 's> PushOne<&'r mut PushGuard<&'s mut LuaTable<L>>, Err = Ve>
238 {
239 unsafe {
240 let raw_lua = self.as_mut_lua().0;
241 let my_offset = self.offset(-2);
242
243 let mut guard = match index.push_to_lua(self) {
244 Ok(guard) => {
245 assert_eq!(guard.size, 1);
246 guard
247 }
248 Err((err, _)) => {
249 return Err(CheckedSetError::KeyPushError(err));
250 }
251 };
252
253 match value.push_to_lua(&mut guard) {
254 Ok(pushed) => {
255 assert_eq!(pushed.size, 1);
256 pushed.forget()
257 }
258 Err((err, _)) => {
259 return Err(CheckedSetError::ValuePushError(err));
260 }
261 };
262
263 guard.forget();
264 ffi::lua_settable(raw_lua, my_offset);
265 Ok(())
266 }
267 }
268
269 /// Inserts an empty array, then loads it.
270 #[inline]
271 pub fn empty_array<'s, I, E>(&'s mut self, index: I) -> LuaTable<PushGuard<&'s mut LuaTable<L>>>
272 where I: for<'a> PushOne<&'a mut &'s mut LuaTable<L>, Err = E> + Clone,
273 E: Into<Void>,
274 {
275 // TODO: cleaner implementation
276 unsafe {
277 let mut me = self;
278 match index.clone().push_to_lua(&mut me) {
279 Ok(pushed) => {
280 assert_eq!(pushed.size, 1);
281 pushed.forget()
282 }
283 Err(_) => panic!(), // TODO:
284 };
285
286 match Vec::<u8>::with_capacity(0).push_to_lua(&mut me) {
287 Ok(pushed) => pushed.forget(),
288 Err(_) => panic!(), // TODO:
289 };
290
291 ffi::lua_settable(me.as_mut_lua().0, me.offset(-2));
292
293 me.get(index).unwrap()
294 }
295 }
296
297 /// Obtains or creates the metatable of the table.
298 ///
299 /// A metatable is an additional table that can be attached to a table or a userdata. It can
300 /// contain anything, but its most interesting usage are the following special methods:
301 ///
302 /// - If non-nil, the `__index` entry of the metatable is used as a function whenever the user
303 /// tries to read a non-existing entry in the table or userdata. Its signature is
304 /// `(object, index) -> value`.
305 /// - If non-nil, the `__newindex` entry of the metatable is used as a function whenever the
306 /// user tries to write a non-existing entry in the table or userdata. Its signature is
307 /// `(object, index, value)`.
308 /// - If non-nil, the `__lt`, `__le` and `__eq` entries correspond respectively to operators
309 /// `<`, `<=` and `==`. Their signature is `(a, b) -> bool`. Other operators are
310 /// automatically derived from these three functions.
311 /// - If non-nil, the `__add`, `__mul`, `__sub`, `__div`, `__unm`, `__pow` and `__concat`
312 /// entries correspond to operators `+`, `*`, `-`, `/`, `-` (unary), `^` and `..`. Their
313 /// signature is `(a, b) -> result`.
314 /// - If non-nil, the `__gc` entry is called whenever the garbage collector is about to drop
315 /// the object. Its signature is simply `(obj)`. Remember that usercode is able to modify
316 /// the metatable as well, so there is no strong guarantee that this is actually going to be
317 /// called.
318 ///
319 /// Interestingly enough, a metatable can also have a metatable. For example if you try to
320 /// access a non-existing field in a table, Lua will look for the `__index` function in its
321 /// metatable. If that function doesn't exist, it will try to use the `__index` function of the
322 /// metatable's metatable in order to get the `__index` function of the metatable. This can
323 /// go on infinitely.
324 ///
325 /// # Example
326 ///
327 /// ```
328 /// use hlua::Lua;
329 /// use hlua::LuaTable;
330 /// use hlua::AnyLuaValue;
331 ///
332 /// let mut lua = Lua::new();
333 /// lua.execute::<()>("a = {}").unwrap();
334 ///
335 /// {
336 /// let mut table: LuaTable<_> = lua.get("a").unwrap();
337 /// let mut metatable = table.get_or_create_metatable();
338 /// metatable.set("__index", hlua::function2(|_: AnyLuaValue, var: String| -> AnyLuaValue {
339 /// println!("The user tried to access non-existing index {:?}", var);
340 /// AnyLuaValue::LuaNil
341 /// }));
342 /// }
343 /// ```
344 #[inline]
345 pub fn get_or_create_metatable(mut self) -> LuaTable<PushGuard<L>> {
346 unsafe {
347 // We put the metatable at the top of the stack.
348 if ffi::lua_getmetatable(self.table.as_mut_lua().0, self.index) == 0 {
349 // No existing metatable ; create one then set it and reload it.
350 ffi::lua_newtable(self.table.as_mut_lua().0);
351 ffi::lua_setmetatable(self.table.as_mut_lua().0, self.offset(-1));
352 let r = ffi::lua_getmetatable(self.table.as_mut_lua().0, self.index);
353 debug_assert!(r != 0);
354 }
355
356 let raw_lua = self.as_lua();
357 LuaTable {
358 table: PushGuard {
359 lua: self.table,
360 size: 1,
361 raw_lua: raw_lua,
362 },
363 index: -1,
364 }
365 }
366 }
367
368 /// Builds the `LuaTable` that yields access to the registry.
369 ///
370 /// The registry is a special table available from anywhere and that is not directly
371 /// accessible from Lua code. It can be used to store whatever you want to keep in memory.
372 ///
373 /// # Example
374 ///
375 /// ```
376 /// use hlua::Lua;
377 /// use hlua::LuaTable;
378 ///
379 /// let mut lua = Lua::new();
380 ///
381 /// let mut table = LuaTable::registry(&mut lua);
382 /// table.set(3, "hello");
383 /// ```
384 #[inline]
385 pub fn registry(lua: L) -> LuaTable<L> {
386 LuaTable {
387 table: lua,
388 index: ffi::LUA_REGISTRYINDEX,
389 }
390 }
391}
392
393/// Error returned by the `checked_set` function.
394// TODO: implement `Error` on this type
395#[derive(Debug, Copy, Clone)]
396pub enum CheckedSetError<K, V> {
397 /// Error while pushing the key.
398 KeyPushError(K),
399 /// Error while pushing the value.
400 ValuePushError(V),
401}
402
403/// Iterator that enumerates the content of a Lua table.
404///
405/// See `LuaTable::iter` for more info.
406// Implementation note: While the LuaTableIterator is active, the current key is constantly
407// pushed over the table. The destructor takes care of removing it.
408#[derive(Debug)]
409pub struct LuaTableIterator<'t, L: 't, K, V> {
410 table: &'t mut LuaTable<L>,
411 finished: bool, // if true, the key is not on the stack anymore
412 raw_lua: LuaContext,
413 marker: PhantomData<(K, V)>,
414}
415
416unsafe impl<'t, 'lua, L, K, V> AsLua<'lua> for LuaTableIterator<'t, L, K, V>
417 where L: AsMutLua<'lua>
418{
419 #[inline]
420 fn as_lua(&self) -> LuaContext {
421 self.table.as_lua()
422 }
423}
424
425unsafe impl<'t, 'lua, L, K, V> AsMutLua<'lua> for LuaTableIterator<'t, L, K, V>
426 where L: AsMutLua<'lua>
427{
428 #[inline]
429 fn as_mut_lua(&mut self) -> LuaContext {
430 self.table.as_mut_lua()
431 }
432}
433
434impl<'t, 'lua, L, K, V> Iterator for LuaTableIterator<'t, L, K, V>
435 where L: AsMutLua<'lua> + 't,
436 K: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static,
437 V: for<'i, 'j> LuaRead<&'i mut &'j mut LuaTableIterator<'t, L, K, V>> + 'static
438{
439 type Item = Option<(K, V)>;
440
441 #[inline]
442 fn next(&mut self) -> Option<Option<(K, V)>> {
443 unsafe {
444 if self.finished {
445 return None;
446 }
447
448 // As a reminder, the key is always at the top of the stack unless `finished` is true.
449
450 // This call pops the current key and pushes the next key and value at the top.
451 if ffi::lua_next(self.table.as_mut_lua().0, self.table.offset(-1)) == 0 {
452 self.finished = true;
453 return None;
454 }
455
456 // Reading the key and value.
457 let mut me = self;
458 let key = LuaRead::lua_read_at_position(&mut me, -2).ok();
459 let value = LuaRead::lua_read_at_position(&mut me, -1).ok();
460
461 // Removing the value, leaving only the key on the top of the stack.
462 ffi::lua_pop(me.table.as_mut_lua().0, 1);
463
464 if key.is_none() || value.is_none() {
465 Some(None)
466 } else {
467 Some(Some((key.unwrap(), value.unwrap())))
468 }
469 }
470 }
471}
472
473impl<'t, L, K, V> Drop for LuaTableIterator<'t, L, K, V> {
474 #[inline]
475 fn drop(&mut self) {
476 unsafe {
477 if !self.finished {
478 ffi::lua_pop(self.raw_lua.0, 1);
479 }
480 }
481 }
482}
483
484#[cfg(test)]
485mod tests {
486 use Lua;
487 use LuaTable;
488 use PushGuard;
489 use function0;
490
491 #[test]
492 fn iterable() {
493 let mut lua = Lua::new();
494
495 let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
496
497 let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
498 let mut counter = 0;
499
500 for (key, value) in table.iter().filter_map(|e| e) {
501 let _: u32 = key;
502 let _: u32 = value;
503 assert_eq!(key + value, 10);
504 counter += 1;
505 }
506
507 assert_eq!(counter, 3);
508 }
509
510 #[test]
511 fn iterable_multipletimes() {
512 let mut lua = Lua::new();
513
514 let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
515
516 let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
517
518 for _ in 0..10 {
519 let table_content: Vec<Option<(u32, u32)>> = table.iter().collect();
520 assert_eq!(table_content,
521 vec![Some((1, 9)), Some((2, 8)), Some((3, 7))]);
522 }
523 }
524
525 #[test]
526 fn get_set() {
527 let mut lua = Lua::new();
528
529 let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
530 let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
531
532 let x: i32 = table.get(2).unwrap();
533 assert_eq!(x, 8);
534
535 table.set(3, "hello");
536 let y: String = table.get(3).unwrap();
537 assert_eq!(y, "hello");
538
539 let z: i32 = table.get(1).unwrap();
540 assert_eq!(z, 9);
541 }
542
543 #[test]
544 fn table_over_table() {
545 let mut lua = Lua::new();
546
547 lua.execute::<()>("a = { 9, { 8, 7 }, 6 }").unwrap();
548 let mut table = lua.get::<LuaTable<_>, _>("a").unwrap();
549
550 let x: i32 = table.get(1).unwrap();
551 assert_eq!(x, 9);
552
553 {
554 let mut subtable = table.get::<LuaTable<_>, _, _>(2).unwrap();
555
556 let y: i32 = subtable.get(1).unwrap();
557 assert_eq!(y, 8);
558
559 let z: i32 = subtable.get(2).unwrap();
560 assert_eq!(z, 7);
561 }
562
563 let w: i32 = table.get(3).unwrap();
564 assert_eq!(w, 6);
565 }
566
567 #[test]
568 fn metatable() {
569 let mut lua = Lua::new();
570
571 let _: () = lua.execute("a = { 9, 8, 7 }").unwrap();
572
573 {
574 let table = lua.get::<LuaTable<_>, _>("a").unwrap();
575
576 let mut metatable = table.get_or_create_metatable();
577 fn handler() -> i32 {
578 5
579 };
580 metatable.set("__add".to_string(), function0(handler));
581 }
582
583 let r: i32 = lua.execute("return a + a").unwrap();
584 assert_eq!(r, 5);
585 }
586
587 #[test]
588 fn empty_array() {
589 let mut lua = Lua::new();
590
591 {
592 let mut array = lua.empty_array("a");
593 array.set("b", 3)
594 }
595
596 let mut table: LuaTable<_> = lua.get("a").unwrap();
597 assert!(3 == table.get("b").unwrap());
598 }
599
600 #[test]
601 fn by_value() {
602 let mut lua = Lua::new();
603
604 {
605 let mut array = lua.empty_array("a");
606 {
607 let mut array2 = array.empty_array("b");
608 array2.set("c", 3);
609 }
610 }
611
612 let table: LuaTable<PushGuard<Lua>> = lua.into_get("a").ok().unwrap();
613 let mut table2: LuaTable<PushGuard<LuaTable<PushGuard<Lua>>>> =
614 table.into_get("b").ok().unwrap();
615 assert!(3 == table2.get("c").unwrap());
616 let table: LuaTable<PushGuard<Lua>> = table2.into_inner().into_inner();
617 // do it again to make sure the stack is still sane
618 let mut table2: LuaTable<PushGuard<LuaTable<PushGuard<Lua>>>> =
619 table.into_get("b").ok().unwrap();
620 assert!(3 == table2.get("c").unwrap());
621 let table: LuaTable<PushGuard<Lua>> = table2.into_inner().into_inner();
622 let _lua: Lua = table.into_inner().into_inner();
623 }
624
625 #[test]
626 fn registry() {
627 let mut lua = Lua::new();
628
629 let mut table = LuaTable::registry(&mut lua);
630 table.set(3, "hello");
631 let y: String = table.get(3).unwrap();
632 assert_eq!(y, "hello");
633 }
634
635 #[test]
636 fn registry_metatable() {
637 let mut lua = Lua::new();
638
639 let registry = LuaTable::registry(&mut lua);
640 let mut metatable = registry.get_or_create_metatable();
641 metatable.set(3, "hello");
642 }
643}