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}