hlua_badtouch/
any.rs

1use ffi;
2
3use AsLua;
4use AsMutLua;
5
6use Push;
7use PushGuard;
8use PushOne;
9use LuaRead;
10use LuaTable;
11use Void;
12
13#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct AnyLuaString(pub Vec<u8>);
15
16/// Represents any value that can be stored by Lua
17#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub enum AnyHashableLuaValue {
19    LuaString(String),
20    LuaAnyString(AnyLuaString),
21    LuaNumber(i32),
22    LuaBoolean(bool),
23    LuaArray(Vec<(AnyHashableLuaValue, AnyHashableLuaValue)>),
24    LuaNil,
25
26    /// The "Other" element is (hopefully) temporary and will be replaced by "Function" and "Userdata".
27    /// A panic! will trigger if you try to push a Other.
28    LuaOther,
29}
30
31/// Represents any value that can be stored by Lua
32#[derive(Clone, Debug, PartialEq)]
33pub enum AnyLuaValue {
34    LuaString(String),
35    LuaAnyString(AnyLuaString),
36    LuaNumber(f64),
37    LuaBoolean(bool),
38    LuaArray(Vec<(AnyLuaValue, AnyLuaValue)>),
39    LuaNil,
40
41    /// The "Other" element is (hopefully) temporary and will be replaced by "Function" and "Userdata".
42    /// A panic! will trigger if you try to push a Other.
43    LuaOther,
44}
45
46impl<'lua, L> Push<L> for AnyLuaValue
47    where L: AsMutLua<'lua>
48{
49    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
50
51    #[inline]
52    fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
53        let raw_lua = lua.as_lua();
54        match self {
55            AnyLuaValue::LuaString(val) => val.push_to_lua(lua),
56            AnyLuaValue::LuaAnyString(val) => val.push_to_lua(lua),
57            AnyLuaValue::LuaNumber(val) => val.push_to_lua(lua),
58            AnyLuaValue::LuaBoolean(val) => val.push_to_lua(lua),
59            AnyLuaValue::LuaArray(val) => {
60                // Pushing a `Vec<(AnyLuaValue, AnyLuaValue)>` on a `L` requires calling the
61                // function that pushes a `AnyLuaValue` on a `&mut L`, which in turns requires
62                // calling the function that pushes a `AnyLuaValue` on a `&mut &mut L`, and so on.
63                // In order to avoid this infinite recursion, we push the array on a
64                // `&mut AsMutLua` instead.
65
66                // We also need to destroy and recreate the push guard, otherwise the type parameter
67                // doesn't match.
68                let size = val.push_no_err(&mut lua as &mut AsMutLua<'lua>).forget_internal();
69
70                Ok(PushGuard {
71                    lua: lua,
72                    size: size,
73                    raw_lua: raw_lua,
74                })
75            }
76            AnyLuaValue::LuaNil => {
77                unsafe {
78                    ffi::lua_pushnil(lua.as_mut_lua().0);
79                }
80                Ok(PushGuard {
81                    lua: lua,
82                    size: 1,
83                    raw_lua: raw_lua,
84                })
85            } // Use ffi::lua_pushnil.
86            AnyLuaValue::LuaOther => panic!("can't push a AnyLuaValue of type Other"),
87        }
88    }
89}
90
91impl<'lua, L> PushOne<L> for AnyLuaValue where L: AsMutLua<'lua> {}
92
93impl<'lua, L> LuaRead<L> for AnyLuaValue
94    where L: AsMutLua<'lua>
95{
96    #[inline]
97    fn lua_read_at_position(mut lua: L, index: i32) -> Result<AnyLuaValue, L> {
98
99        // If we know that the value on the stack is a string, we should try
100        // to parse it as a string instead of a number or boolean, so that
101        // values such as '1.10' don't become `AnyLuaValue::LuaNumber(1.1)`.
102        let data_type = unsafe { ffi::lua_type(lua.as_lua().0, index) };
103        if data_type == ffi::LUA_TSTRING {
104
105            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
106                Ok(v) => return Ok(AnyLuaValue::LuaString(v)),
107                Err(lua) => lua,
108            };
109
110            let _lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
111                Ok(v) => return Ok(AnyLuaValue::LuaAnyString(v)),
112                Err(lua) => lua,
113            };
114
115            Ok(AnyLuaValue::LuaOther)
116
117        } else {
118
119            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
120                Ok(v) => return Ok(AnyLuaValue::LuaNumber(v)),
121                Err(lua) => lua,
122            };
123
124            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
125                Ok(v) => return Ok(AnyLuaValue::LuaBoolean(v)),
126                Err(lua) => lua,
127            };
128
129            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
130                Ok(v) => return Ok(AnyLuaValue::LuaString(v)),
131                Err(lua) => lua,
132            };
133
134            let lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
135                Ok(v) => return Ok(AnyLuaValue::LuaAnyString(v)),
136                Err(lua) => lua,
137            };
138
139            if unsafe { ffi::lua_isnil(lua.as_lua().0, index) } {
140                return Ok(AnyLuaValue::LuaNil);
141            }
142
143            let table: Result<LuaTable<_>, _> = LuaRead::lua_read_at_position(lua, index);
144            let _lua = match table {
145                Ok(mut v) => return Ok(AnyLuaValue::LuaArray(v.iter::<AnyLuaValue, AnyLuaValue>()
146                                                              .filter_map(|e| e).collect())),
147                Err(lua) => lua,
148            };
149
150            Ok(AnyLuaValue::LuaOther)
151        }
152    }
153}
154
155impl<'lua, L> Push<L> for AnyHashableLuaValue
156    where L: AsMutLua<'lua>
157{
158    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
159
160    #[inline]
161    fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
162        let raw_lua = lua.as_lua();
163        match self {
164            AnyHashableLuaValue::LuaString(val) => val.push_to_lua(lua),
165            AnyHashableLuaValue::LuaAnyString(val) => val.push_to_lua(lua),
166            AnyHashableLuaValue::LuaNumber(val) => val.push_to_lua(lua),
167            AnyHashableLuaValue::LuaBoolean(val) => val.push_to_lua(lua),
168            AnyHashableLuaValue::LuaArray(val) => {
169                // Pushing a `Vec<(AnyHashableLuaValue, AnyHashableLuaValue)>` on a `L` requires calling the
170                // function that pushes a `AnyHashableLuaValue` on a `&mut L`, which in turns requires
171                // calling the function that pushes a `AnyHashableLuaValue` on a `&mut &mut L`, and so on.
172                // In order to avoid this infinite recursion, we push the array on a
173                // `&mut AsMutLua` instead.
174
175                // We also need to destroy and recreate the push guard, otherwise the type parameter
176                // doesn't match.
177                let size = val.push_no_err(&mut lua as &mut AsMutLua<'lua>).forget_internal();
178
179                Ok(PushGuard {
180                    lua: lua,
181                    size: size,
182                    raw_lua: raw_lua,
183                })
184            }
185            AnyHashableLuaValue::LuaNil => {
186                unsafe {
187                    ffi::lua_pushnil(lua.as_mut_lua().0);
188                }
189                Ok(PushGuard {
190                    lua: lua,
191                    size: 1,
192                    raw_lua: raw_lua,
193                })
194            } // Use ffi::lua_pushnil.
195            AnyHashableLuaValue::LuaOther => panic!("can't push a AnyHashableLuaValue of type Other"),
196        }
197    }
198}
199
200impl<'lua, L> PushOne<L> for AnyHashableLuaValue where L: AsMutLua<'lua> {}
201
202impl<'lua, L> LuaRead<L> for AnyHashableLuaValue
203    where L: AsMutLua<'lua>
204{
205    #[inline]
206    fn lua_read_at_position(mut lua: L, index: i32) -> Result<AnyHashableLuaValue, L> {
207        let data_type = unsafe { ffi::lua_type(lua.as_lua().0, index) };
208        if data_type == ffi::LUA_TSTRING {
209
210            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
211                Ok(v) => return Ok(AnyHashableLuaValue::LuaString(v)),
212                Err(lua) => lua,
213            };
214
215            let _lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
216                Ok(v) => return Ok(AnyHashableLuaValue::LuaAnyString(v)),
217                Err(lua) => lua,
218            };
219            
220            Ok(AnyHashableLuaValue::LuaOther)
221
222        } else {
223
224            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
225                Ok(v) => return Ok(AnyHashableLuaValue::LuaNumber(v)),
226                Err(lua) => lua,
227            };
228
229            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
230                Ok(v) => return Ok(AnyHashableLuaValue::LuaBoolean(v)),
231                Err(lua) => lua,
232            };
233
234            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
235                Ok(v) => return Ok(AnyHashableLuaValue::LuaString(v)),
236                Err(lua) => lua,
237            };
238
239            let mut lua = match LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index) {
240                Ok(v) => return Ok(AnyHashableLuaValue::LuaAnyString(v)),
241                Err(lua) => lua,
242            };
243
244            if unsafe { ffi::lua_isnil(lua.as_lua().0, index) } {
245                return Ok(AnyHashableLuaValue::LuaNil);
246            }
247
248            let table: Result<LuaTable<_>, _> = LuaRead::lua_read_at_position(&mut lua as &mut AsMutLua<'lua>, index);
249            let _lua = match table {
250                Ok(mut v) => return Ok(AnyHashableLuaValue::LuaArray(v
251                    .iter::<AnyHashableLuaValue, AnyHashableLuaValue>()
252                    .filter_map(|e| e).collect())),
253                Err(lua) => lua,
254            };
255
256            Ok(AnyHashableLuaValue::LuaOther)
257        }
258    }
259}
260
261#[cfg(test)]
262mod tests {
263    use Lua;
264    use AnyLuaValue;
265    use AnyHashableLuaValue;
266    use AnyLuaString;
267
268    #[test]
269    fn read_numbers() {
270        let mut lua = Lua::new();
271
272        lua.set("a", "-2");
273        lua.set("b", 3.5f32);
274        lua.set("c", -2.0f32);
275
276        let x: AnyLuaValue = lua.get("a").unwrap();
277        assert_eq!(x, AnyLuaValue::LuaString("-2".to_owned()));
278
279        let y: AnyLuaValue = lua.get("b").unwrap();
280        assert_eq!(y, AnyLuaValue::LuaNumber(3.5));
281
282        let z: AnyLuaValue = lua.get("c").unwrap();
283        assert_eq!(z, AnyLuaValue::LuaNumber(-2.0));
284    }
285
286    #[test]
287    fn read_hashable_numbers() {
288        let mut lua = Lua::new();
289
290        lua.set("a", -2.0f32);
291        lua.set("b", 4.0f32);
292        lua.set("c", "4");
293
294        let x: AnyHashableLuaValue = lua.get("a").unwrap();
295        assert_eq!(x, AnyHashableLuaValue::LuaNumber(-2));
296
297        let y: AnyHashableLuaValue = lua.get("b").unwrap();
298        assert_eq!(y, AnyHashableLuaValue::LuaNumber(4));
299
300        let z: AnyHashableLuaValue = lua.get("c").unwrap();
301        assert_eq!(z, AnyHashableLuaValue::LuaString("4".to_owned()));
302    }
303
304    #[test]
305    fn read_strings() {
306        let mut lua = Lua::new();
307
308        lua.set("a", "hello");
309        lua.set("b", "3x");
310        lua.set("c", "false");
311
312        let x: AnyLuaValue = lua.get("a").unwrap();
313        assert_eq!(x, AnyLuaValue::LuaString("hello".to_string()));
314
315        let y: AnyLuaValue = lua.get("b").unwrap();
316        assert_eq!(y, AnyLuaValue::LuaString("3x".to_string()));
317
318        let z: AnyLuaValue = lua.get("c").unwrap();
319        assert_eq!(z, AnyLuaValue::LuaString("false".to_string()));
320    }
321
322    #[test]
323    fn read_hashable_strings() {
324        let mut lua = Lua::new();
325
326        lua.set("a", "hello");
327        lua.set("b", "3x");
328        lua.set("c", "false");
329
330        let x: AnyHashableLuaValue = lua.get("a").unwrap();
331        assert_eq!(x, AnyHashableLuaValue::LuaString("hello".to_string()));
332
333        let y: AnyHashableLuaValue = lua.get("b").unwrap();
334        assert_eq!(y, AnyHashableLuaValue::LuaString("3x".to_string()));
335
336        let z: AnyHashableLuaValue = lua.get("c").unwrap();
337        assert_eq!(z, AnyHashableLuaValue::LuaString("false".to_string()));
338    }
339
340    #[test]
341    fn read_booleans() {
342        let mut lua = Lua::new();
343
344        lua.set("a", true);
345        lua.set("b", false);
346
347        let x: AnyLuaValue = lua.get("a").unwrap();
348        assert_eq!(x, AnyLuaValue::LuaBoolean(true));
349
350        let y: AnyLuaValue = lua.get("b").unwrap();
351        assert_eq!(y, AnyLuaValue::LuaBoolean(false));
352    }
353
354    #[test]
355    fn read_hashable_booleans() {
356        let mut lua = Lua::new();
357
358        lua.set("a", true);
359        lua.set("b", false);
360
361        let x: AnyHashableLuaValue = lua.get("a").unwrap();
362        assert_eq!(x, AnyHashableLuaValue::LuaBoolean(true));
363
364        let y: AnyHashableLuaValue = lua.get("b").unwrap();
365        assert_eq!(y, AnyHashableLuaValue::LuaBoolean(false));
366    }
367
368    #[test]
369    fn read_tables() {
370        let mut lua = Lua::new();
371        lua.execute::<()>("
372        a = {x = 12, y = 19}
373        b = {z = a, w = 'test string'}
374        c = {'first', 'second'}
375        ").unwrap();
376
377        fn get<'a>(table: &'a AnyLuaValue, key: &str) -> &'a AnyLuaValue {
378            let test_key = AnyLuaValue::LuaString(key.to_owned());
379            match table {
380                &AnyLuaValue::LuaArray(ref vec) => {
381                    let &(_, ref value) = vec.iter().find(|&&(ref key, _)| key == &test_key).expect("key not found");
382                    value
383                },
384                _ => panic!("not a table")
385            }
386        }
387
388        fn get_numeric<'a>(table: &'a AnyLuaValue, key: usize) -> &'a AnyLuaValue {
389            let test_key = AnyLuaValue::LuaNumber(key as f64);
390            match table {
391                &AnyLuaValue::LuaArray(ref vec) => {
392                    let &(_, ref value) = vec.iter().find(|&&(ref key, _)| key == &test_key).expect("key not found");
393                    value
394                },
395                _ => panic!("not a table")
396            }
397        }
398
399        let a: AnyLuaValue = lua.get("a").unwrap();
400        assert_eq!(get(&a, "x"), &AnyLuaValue::LuaNumber(12.0));
401        assert_eq!(get(&a, "y"), &AnyLuaValue::LuaNumber(19.0));
402
403        let b: AnyLuaValue = lua.get("b").unwrap();
404        assert_eq!(get(&get(&b, "z"), "x"), get(&a, "x"));
405        assert_eq!(get(&get(&b, "z"), "y"), get(&a, "y"));
406
407        let c: AnyLuaValue = lua.get("c").unwrap();
408        assert_eq!(get_numeric(&c, 1), &AnyLuaValue::LuaString("first".to_owned()));
409        assert_eq!(get_numeric(&c, 2), &AnyLuaValue::LuaString("second".to_owned()));
410    }
411
412    #[test]
413    fn read_hashable_tables() {
414        let mut lua = Lua::new();
415        lua.execute::<()>("
416        a = {x = 12, y = 19}
417        b = {z = a, w = 'test string'}
418        c = {'first', 'second'}
419        ").unwrap();
420
421        fn get<'a>(table: &'a AnyHashableLuaValue, key: &str) -> &'a AnyHashableLuaValue {
422            let test_key = AnyHashableLuaValue::LuaString(key.to_owned());
423            match table {
424                &AnyHashableLuaValue::LuaArray(ref vec) => {
425                    let &(_, ref value) = vec.iter().find(|&&(ref key, _)| key == &test_key).expect("key not found");
426                    value
427                },
428                _ => panic!("not a table")
429            }
430        }
431
432        fn get_numeric<'a>(table: &'a AnyHashableLuaValue, key: usize) -> &'a AnyHashableLuaValue {
433            let test_key = AnyHashableLuaValue::LuaNumber(key as i32);
434            match table {
435                &AnyHashableLuaValue::LuaArray(ref vec) => {
436                    let &(_, ref value) = vec.iter().find(|&&(ref key, _)| key == &test_key).expect("key not found");
437                    value
438                },
439                _ => panic!("not a table")
440            }
441        }
442
443        let a: AnyHashableLuaValue = lua.get("a").unwrap();
444        assert_eq!(get(&a, "x"), &AnyHashableLuaValue::LuaNumber(12));
445        assert_eq!(get(&a, "y"), &AnyHashableLuaValue::LuaNumber(19));
446
447        let b: AnyHashableLuaValue = lua.get("b").unwrap();
448        assert_eq!(get(&get(&b, "z"), "x"), get(&a, "x"));
449        assert_eq!(get(&get(&b, "z"), "y"), get(&a, "y"));
450
451        let c: AnyHashableLuaValue = lua.get("c").unwrap();
452        assert_eq!(get_numeric(&c, 1), &AnyHashableLuaValue::LuaString("first".to_owned()));
453        assert_eq!(get_numeric(&c, 2), &AnyHashableLuaValue::LuaString("second".to_owned()));
454    }
455
456    #[test]
457    fn push_numbers() {
458        let mut lua = Lua::new();
459
460        lua.set("a", AnyLuaValue::LuaNumber(3.0));
461
462        let x: i32 = lua.get("a").unwrap();
463        assert_eq!(x, 3);
464    }
465
466    #[test]
467    fn push_hashable_numbers() {
468        let mut lua = Lua::new();
469
470        lua.set("a", AnyHashableLuaValue::LuaNumber(3));
471
472        let x: i32 = lua.get("a").unwrap();
473        assert_eq!(x, 3);
474    }
475
476    #[test]
477    fn push_strings() {
478        let mut lua = Lua::new();
479
480        lua.set("a", AnyLuaValue::LuaString("hello".to_string()));
481
482        let x: String = lua.get("a").unwrap();
483        assert_eq!(x, "hello");
484    }
485
486    #[test]
487    fn push_hashable_strings() {
488        let mut lua = Lua::new();
489
490        lua.set("a", AnyHashableLuaValue::LuaString("hello".to_string()));
491
492        let x: String = lua.get("a").unwrap();
493        assert_eq!(x, "hello");
494    }
495
496    #[test]
497    fn push_booleans() {
498        let mut lua = Lua::new();
499
500        lua.set("a", AnyLuaValue::LuaBoolean(true));
501
502        let x: bool = lua.get("a").unwrap();
503        assert_eq!(x, true);
504    }
505
506    #[test]
507    fn push_hashable_booleans() {
508        let mut lua = Lua::new();
509
510        lua.set("a", AnyHashableLuaValue::LuaBoolean(true));
511
512        let x: bool = lua.get("a").unwrap();
513        assert_eq!(x, true);
514    }
515
516    #[test]
517    fn push_nil() {
518        let mut lua = Lua::new();
519
520        lua.set("a", AnyLuaValue::LuaNil);
521
522        let x: Option<i32> = lua.get("a");
523        assert!(x.is_none(),
524                "x is a Some value when it should be a None value. X: {:?}",
525                x);
526    }
527
528    #[test]
529    fn push_hashable_nil() {
530        let mut lua = Lua::new();
531
532        lua.set("a", AnyHashableLuaValue::LuaNil);
533
534        let x: Option<i32> = lua.get("a");
535        assert!(x.is_none(),
536                "x is a Some value when it should be a None value. X: {:?}",
537                x);
538    }
539
540    #[test]
541    fn non_utf_8_string() {
542        let mut lua = Lua::new();
543        let a = lua.execute::<AnyLuaValue>(r"return '\xff\xfe\xff\xfe'").unwrap();
544        match a {
545            AnyLuaValue::LuaAnyString(AnyLuaString(v)) => {
546                assert_eq!(Vec::from(&b"\xff\xfe\xff\xfe"[..]), v);
547            },
548            _ => panic!("Decoded to wrong variant"),
549        }
550    }
551}