hlua_badtouch/
rust_tables.rs

1use ffi;
2use any::{AnyLuaValue, AnyHashableLuaValue};
3
4use Push;
5use PushGuard;
6use PushOne;
7use AsMutLua;
8use TuplePushError;
9use LuaRead;
10
11use std::collections::{BTreeMap, HashMap, HashSet};
12use std::hash::Hash;
13use std::iter;
14
15#[inline]
16fn push_iter<'lua, L, V, I, E>(mut lua: L, iterator: I) -> Result<PushGuard<L>, (E, L)>
17    where L: AsMutLua<'lua>,
18          V: for<'b> Push<&'b mut L, Err = E>,
19          I: Iterator<Item = V>
20{
21    // creating empty table
22    unsafe { ffi::lua_newtable(lua.as_mut_lua().0) };
23
24    for (elem, index) in iterator.zip((1..)) {
25        let size = match elem.push_to_lua(&mut lua) {
26            Ok(pushed) => pushed.forget_internal(),
27            Err((_err, _lua)) => panic!(),     // TODO: wrong   return Err((err, lua)),      // FIXME: destroy the temporary table
28        };
29
30        match size {
31            0 => continue,
32            1 => {
33                let index = index as u32;
34                match index.push_to_lua(&mut lua) {
35                    Ok(pushed) => pushed.forget_internal(),
36                    Err(_) => unreachable!(),
37                };
38                unsafe { ffi::lua_insert(lua.as_mut_lua().0, -2) }
39                unsafe { ffi::lua_settable(lua.as_mut_lua().0, -3) }
40            }
41            2 => unsafe { ffi::lua_settable(lua.as_mut_lua().0, -3) },
42            _ => unreachable!(),
43        }
44    }
45
46    let raw_lua = lua.as_lua();
47    Ok(PushGuard {
48        lua: lua,
49        size: 1,
50        raw_lua: raw_lua,
51    })
52}
53
54#[inline]
55fn push_rec_iter<'lua, L, V, I, E>(mut lua: L, iterator: I) -> Result<PushGuard<L>, (E, L)>
56    where L: AsMutLua<'lua>,
57          V: for<'a> Push<&'a mut L, Err = E>,
58          I: Iterator<Item = V>
59{
60    let (nrec, _) = iterator.size_hint();
61
62    // creating empty table with pre-allocated non-array elements
63    unsafe { ffi::lua_createtable(lua.as_mut_lua().0, 0, nrec as i32) };
64
65    for elem in iterator {
66        let size = match elem.push_to_lua(&mut lua) {
67            Ok(pushed) => pushed.forget_internal(),
68            Err((_err, _lua)) => panic!(),     // TODO: wrong   return Err((err, lua)),      // FIXME: destroy the temporary table
69        };
70
71        match size {
72            0 => continue,
73            2 => unsafe { ffi::lua_settable(lua.as_mut_lua().0, -3) },
74            _ => unreachable!(),
75        }
76    }
77
78    let raw_lua = lua.as_lua();
79    Ok(PushGuard {
80        lua: lua,
81        size: 1,
82        raw_lua: raw_lua,
83    })
84}
85
86impl<'lua, L, T, E> Push<L> for Vec<T>
87    where L: AsMutLua<'lua>,
88          T: for<'a> Push<&'a mut L, Err = E>
89{
90    type Err = E;
91
92    #[inline]
93    fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (E, L)> {
94        push_iter(lua, self.into_iter())
95    }
96}
97
98impl<'lua, L, T, E> PushOne<L> for Vec<T>
99    where L: AsMutLua<'lua>,
100          T: for<'a> Push<&'a mut L, Err = E>
101{
102}
103
104impl<'lua, L> LuaRead<L> for Vec<AnyLuaValue>
105    where L: AsMutLua<'lua>
106{
107    fn lua_read_at_position(lua: L, index: i32) -> Result<Self, L> {
108        // We need this as iteration order isn't guaranteed to match order of
109        // keys, even if they're numeric
110        // https://www.lua.org/manual/5.2/manual.html#pdf-next
111        let mut dict: BTreeMap<i32, AnyLuaValue> = BTreeMap::new();
112
113        let mut me = lua;
114        unsafe { ffi::lua_pushnil(me.as_mut_lua().0) };
115        let index = index - 1;
116
117        loop {
118            if unsafe { ffi::lua_next(me.as_mut_lua().0, index) } == 0 {
119                break;
120            }
121
122            let key = {
123                let maybe_key: Option<i32> =
124                    LuaRead::lua_read_at_position(&mut me, -2).ok();
125                match maybe_key {
126                    None => {
127                        // Cleaning up after ourselves
128                        unsafe { ffi::lua_pop(me.as_mut_lua().0, 2) };
129                        return Err(me)
130                    }
131                    Some(k) => k,
132                }
133            };
134
135            let value: AnyLuaValue =
136                LuaRead::lua_read_at_position(&mut me, -1).ok().unwrap();
137
138            unsafe { ffi::lua_pop(me.as_mut_lua().0, 1) };
139
140            dict.insert(key, value);
141        }
142
143        let (maximum_key, minimum_key) =
144            (*dict.keys().max().unwrap_or(&1), *dict.keys().min().unwrap_or(&1));
145
146        if minimum_key != 1 {
147            // Rust doesn't support sparse arrays or arrays with negative
148            // indices
149            return Err(me);
150        }
151
152        let mut result =
153            Vec::with_capacity(maximum_key as usize);
154
155        // We expect to start with first element of table and have this
156        // be smaller that first key by one
157        let mut previous_key = 0;
158
159        // By this point, we actually iterate the map to move values to Vec
160        // and check that table represented non-sparse 1-indexed array
161        for (k, v) in dict {
162            if previous_key + 1 != k {
163                return Err(me)
164            } else {
165                // We just push, thus converting Lua 1-based indexing
166                // to Rust 0-based indexing
167                result.push(v);
168                previous_key = k;
169            }
170        }
171
172        Ok(result)
173    }
174}
175
176impl<'a, 'lua, L, T, E> Push<L> for &'a [T]
177    where L: AsMutLua<'lua>,
178          T: Clone + for<'b> Push<&'b mut L, Err = E>
179{
180    type Err = E;
181
182    #[inline]
183    fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (E, L)> {
184        push_iter(lua, self.iter().map(|e| e.clone()))
185    }
186}
187
188impl<'a, 'lua, L, T, E> PushOne<L> for &'a [T]
189    where L: AsMutLua<'lua>,
190          T: Clone + for<'b> Push<&'b mut L, Err = E>
191{
192}
193
194impl<'lua, L> LuaRead<L> for HashMap<AnyHashableLuaValue, AnyLuaValue>
195    where L: AsMutLua<'lua>
196{
197    // TODO: this should be implemented using the LuaTable API instead of raw Lua calls.
198    fn lua_read_at_position(lua: L, index: i32) -> Result<Self, L> {
199        let mut me = lua;
200        unsafe { ffi::lua_pushnil(me.as_mut_lua().0) };
201        let index = index - 1;
202        let mut result = HashMap::new();
203
204        loop {
205            if unsafe { ffi::lua_next(me.as_mut_lua().0, index) } == 0 {
206                break;
207            }
208
209            let key = {
210                let maybe_key: Option<AnyHashableLuaValue> =
211                    LuaRead::lua_read_at_position(&mut me, -2).ok();
212                match maybe_key {
213                    None => {
214                        // Cleaning up after ourselves
215                        unsafe { ffi::lua_pop(me.as_mut_lua().0, 2) };
216                        return Err(me)
217                    }
218                    Some(k) => k,
219                }
220            };
221
222            let value: AnyLuaValue =
223                LuaRead::lua_read_at_position(&mut me, -1).ok().unwrap();
224
225            unsafe { ffi::lua_pop(me.as_mut_lua().0, 1) };
226
227            result.insert(key, value);
228        }
229
230        Ok(result)
231    }
232}
233
234// TODO: use an enum for the error to allow different error types for K and V
235impl<'lua, L, K, V, E> Push<L> for HashMap<K, V>
236    where L: AsMutLua<'lua>,
237          K: for<'a, 'b> PushOne<&'a mut &'b mut L, Err = E> + Eq + Hash,
238          V: for<'a, 'b> PushOne<&'a mut &'b mut L, Err = E>
239{
240    type Err = E;
241
242    #[inline]
243    fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (E, L)> {
244        match push_rec_iter(lua, self.into_iter()) {
245            Ok(g) => Ok(g),
246            Err((TuplePushError::First(err), lua)) => Err((err, lua)),
247            Err((TuplePushError::Other(err), lua)) => Err((err, lua)),
248        }
249    }
250}
251
252impl<'lua, L, K, V, E> PushOne<L> for HashMap<K, V>
253    where L: AsMutLua<'lua>,
254          K: for<'a, 'b> PushOne<&'a mut &'b mut L, Err = E> + Eq + Hash,
255          V: for<'a, 'b> PushOne<&'a mut &'b mut L, Err = E>
256{
257}
258
259impl<'lua, L, K, E> Push<L> for HashSet<K>
260    where L: AsMutLua<'lua>,
261          K: for<'a, 'b> PushOne<&'a mut &'b mut L, Err = E> + Eq + Hash
262{
263    type Err = E;
264
265    #[inline]
266    fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (E, L)> {
267        match push_rec_iter(lua, self.into_iter().zip(iter::repeat(true))) {
268            Ok(g) => Ok(g),
269            Err((TuplePushError::First(err), lua)) => Err((err, lua)),
270            Err((TuplePushError::Other(_), _)) => unreachable!(),
271        }
272    }
273}
274
275impl<'lua, L, K, E> PushOne<L> for HashSet<K>
276    where L: AsMutLua<'lua>,
277          K: for<'a, 'b> PushOne<&'a mut &'b mut L, Err = E> + Eq + Hash
278{
279}
280
281#[cfg(test)]
282mod tests {
283    use std::collections::{HashMap, HashSet, BTreeMap};
284    use Lua;
285    use LuaTable;
286    use AnyLuaValue;
287    use AnyHashableLuaValue;
288
289    #[test]
290    fn write() {
291        let mut lua = Lua::new();
292
293        lua.set("a", vec![9, 8, 7]);
294
295        let mut table: LuaTable<_> = lua.get("a").unwrap();
296
297        let values: Vec<(i32, i32)> = table.iter().filter_map(|e| e).collect();
298        assert_eq!(values, vec![(1, 9), (2, 8), (3, 7)]);
299    }
300
301    #[test]
302    fn write_map() {
303        let mut lua = Lua::new();
304
305        let mut map = HashMap::new();
306        map.insert(5, 8);
307        map.insert(13, 21);
308        map.insert(34, 55);
309
310        lua.set("a", map.clone());
311
312        let mut table: LuaTable<_> = lua.get("a").unwrap();
313
314        let values: HashMap<i32, i32> = table.iter().filter_map(|e| e).collect();
315        assert_eq!(values, map);
316    }
317
318    #[test]
319    fn write_set() {
320        let mut lua = Lua::new();
321
322        let mut set = HashSet::new();
323        set.insert(5);
324        set.insert(8);
325        set.insert(13);
326        set.insert(21);
327        set.insert(34);
328        set.insert(55);
329
330        lua.set("a", set.clone());
331
332        let mut table: LuaTable<_> = lua.get("a").unwrap();
333
334        let values: HashSet<i32> = table.iter()
335            .filter_map(|e| e)
336            .map(|(elem, set): (i32, bool)| {
337                assert!(set);
338                elem
339            })
340            .collect();
341
342        assert_eq!(values, set);
343    }
344
345    #[test]
346    fn globals_table() {
347        let mut lua = Lua::new();
348
349        lua.globals_table().set("a", 12);
350
351        let val: i32 = lua.get("a").unwrap();
352        assert_eq!(val, 12);
353    }
354
355    #[test]
356    fn reading_vec_works() {
357        let mut lua = Lua::new();
358
359        let orig = [1., 2., 3.];
360
361        lua.set("v", &orig[..]);
362
363        let read: Vec<_> = lua.get("v").unwrap();
364        for (o, r) in orig.iter().zip(read.iter()) {
365            if let AnyLuaValue::LuaNumber(ref n) = *r {
366                assert_eq!(o, n);
367            } else {
368                panic!("Unexpected variant");
369            }
370        }
371    }
372
373    #[test]
374    fn reading_vec_from_sparse_table_doesnt_work() {
375        let mut lua = Lua::new();
376
377        lua.execute::<()>(r#"v = { [-1] = -1, [2] = 2, [42] = 42 }"#).unwrap();
378
379        let read: Option<Vec<_>> = lua.get("v");
380        if read.is_some() {
381            panic!("Unexpected success");
382        }
383    }
384
385    #[test]
386    fn reading_vec_with_empty_table_works() {
387        let mut lua = Lua::new();
388
389        lua.execute::<()>(r#"v = { }"#).unwrap();
390
391        let read: Vec<_> = lua.get("v").unwrap();
392        assert_eq!(read.len(), 0);
393    }
394
395    #[test]
396    fn reading_vec_with_complex_indexes_doesnt_work() {
397        let mut lua = Lua::new();
398
399        lua.execute::<()>(r#"v = { [-1] = -1, ["foo"] = 2, [{}] = 42 }"#).unwrap();
400
401        let read: Option<Vec<_>> = lua.get("v");
402        if read.is_some() {
403            panic!("Unexpected success");
404        }
405    }
406
407    #[test]
408    fn reading_heterogenous_vec_works() {
409        let mut lua = Lua::new();
410
411        let orig = [
412            AnyLuaValue::LuaNumber(1.),
413            AnyLuaValue::LuaBoolean(false),
414            AnyLuaValue::LuaNumber(3.),
415            // Pushing String to and reading it from makes it a number
416            //AnyLuaValue::LuaString(String::from("3"))
417        ];
418
419        lua.set("v", &orig[..]);
420
421        let read: Vec<_> = lua.get("v").unwrap();
422        assert_eq!(read, orig);
423    }
424
425    #[test]
426    fn reading_vec_set_from_lua_works() {
427        let mut lua = Lua::new();
428
429        lua.execute::<()>(r#"v = { 1, 2, 3 }"#).unwrap();
430
431        let read: Vec<_> = lua.get("v").unwrap();
432        assert_eq!(
433            read,
434            [1., 2., 3.].iter()
435                .map(|x| AnyLuaValue::LuaNumber(*x)).collect::<Vec<_>>());
436    }
437
438    #[test]
439    fn reading_hashmap_works() {
440        let mut lua = Lua::new();
441
442        let orig: HashMap<i32, f64> = [1., 2., 3.].into_iter().enumerate().map(|(k, v)| (k as i32, *v as f64)).collect();
443        let orig_copy = orig.clone();
444        // Collect to BTreeMap so that iterator yields values in order
445        let orig_btree: BTreeMap<_, _> = orig_copy.into_iter().collect();
446
447        lua.set("v", orig);
448
449        let read: HashMap<AnyHashableLuaValue, AnyLuaValue> = lua.get("v").unwrap();
450        // Same as above
451        let read_btree: BTreeMap<_, _> = read.into_iter().collect();
452        for (o, r) in orig_btree.iter().zip(read_btree.iter()) {
453            if let (&AnyHashableLuaValue::LuaNumber(i), &AnyLuaValue::LuaNumber(n)) = r {
454                let (&o_i, &o_n) = o;
455                assert_eq!(o_i, i);
456                assert_eq!(o_n, n);
457            } else {
458                panic!("Unexpected variant");
459            }
460        }
461    }
462
463    #[test]
464    fn reading_hashmap_from_sparse_table_works() {
465        let mut lua = Lua::new();
466
467        lua.execute::<()>(r#"v = { [-1] = -1, [2] = 2, [42] = 42 }"#).unwrap();
468
469        let read: HashMap<_, _> = lua.get("v").unwrap();
470        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(-1)], AnyLuaValue::LuaNumber(-1.));
471        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(2)], AnyLuaValue::LuaNumber(2.));
472        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(42)], AnyLuaValue::LuaNumber(42.));
473        assert_eq!(read.len(), 3);
474    }
475
476    #[test]
477    fn reading_hashmap_with_empty_table_works() {
478        let mut lua = Lua::new();
479
480        lua.execute::<()>(r#"v = { }"#).unwrap();
481
482        let read: HashMap<_, _> = lua.get("v").unwrap();
483        assert_eq!(read.len(), 0);
484    }
485
486    #[test]
487    fn reading_hashmap_with_complex_indexes_works() {
488        let mut lua = Lua::new();
489
490        lua.execute::<()>(r#"v = { [-1] = -1, ["foo"] = 2, [2.] = 42 }"#).unwrap();
491
492        let read: HashMap<_, _> = lua.get("v").unwrap();
493        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(-1)], AnyLuaValue::LuaNumber(-1.));
494        assert_eq!(read[&AnyHashableLuaValue::LuaString("foo".to_owned())], AnyLuaValue::LuaNumber(2.));
495        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(2)], AnyLuaValue::LuaNumber(42.));
496        assert_eq!(read.len(), 3);
497    }
498
499    #[test]
500    fn reading_hashmap_with_floating_indexes_works() {
501        let mut lua = Lua::new();
502
503        lua.execute::<()>(r#"v = { [-1.25] = -1, [2.5] = 42 }"#).unwrap();
504
505        let read: HashMap<_, _> = lua.get("v").unwrap();
506        // It works by truncating integers in some unspecified way
507        // https://www.lua.org/manual/5.2/manual.html#lua_tointegerx
508        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(-1)], AnyLuaValue::LuaNumber(-1.));
509        assert_eq!(read[&AnyHashableLuaValue::LuaNumber(2)], AnyLuaValue::LuaNumber(42.));
510        assert_eq!(read.len(), 2);
511    }
512
513    #[test]
514    fn reading_heterogenous_hashmap_works() {
515        let mut lua = Lua::new();
516
517        let mut orig = HashMap::new();
518        orig.insert(AnyHashableLuaValue::LuaNumber(42), AnyLuaValue::LuaNumber(42.));
519        orig.insert(AnyHashableLuaValue::LuaString("foo".to_owned()), AnyLuaValue::LuaString("foo".to_owned()));
520        orig.insert(AnyHashableLuaValue::LuaBoolean(true), AnyLuaValue::LuaBoolean(true));
521
522        let orig_clone = orig.clone();
523        lua.set("v", orig);
524
525        let read: HashMap<_, _> = lua.get("v").unwrap();
526        assert_eq!(read, orig_clone);
527    }
528
529    #[test]
530    fn reading_hashmap_set_from_lua_works() {
531        let mut lua = Lua::new();
532
533        lua.execute::<()>(r#"v = { [1] = 2, [2] = 3, [3] = 4 }"#).unwrap();
534
535        let read: HashMap<_, _> = lua.get("v").unwrap();
536        assert_eq!(
537            read,
538            [2., 3., 4.].iter().enumerate()
539                .map(|(k, v)| (AnyHashableLuaValue::LuaNumber((k + 1) as i32), AnyLuaValue::LuaNumber(*v))).collect::<HashMap<_, _>>());
540    }
541}