hlua_badtouch/
values.rs

1use std::mem;
2use std::slice;
3use std::str;
4use std::ops::Deref;
5
6use ffi;
7use libc;
8
9use AnyLuaValue;
10use AnyLuaString;
11use AsLua;
12use AsMutLua;
13use LuaRead;
14use Push;
15use PushGuard;
16use PushOne;
17use Void;
18
19macro_rules! integer_impl(
20    ($t:ident) => (
21        impl<'lua, L> Push<L> for $t where L: AsMutLua<'lua> {
22            type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
23
24            #[inline]
25            fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
26                unsafe { ffi::lua_pushinteger(lua.as_mut_lua().0, self as ffi::lua_Integer) };
27                let raw_lua = lua.as_lua();
28                Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
29            }
30        }
31        
32        impl<'lua, L> PushOne<L> for $t where L: AsMutLua<'lua> {
33        }
34
35        impl<'lua, L> LuaRead<L> for $t where L: AsLua<'lua> {
36            #[inline]
37            fn lua_read_at_position(lua: L, index: i32) -> Result<$t, L> {
38                let mut success = unsafe { mem::uninitialized() };
39                let val = unsafe { ffi::lua_tointegerx(lua.as_lua().0, index, &mut success) };
40                match success {
41                    0 => Err(lua),
42                    _ => Ok(val as $t)
43                }
44            }
45        }
46    );
47);
48
49integer_impl!(i8);
50integer_impl!(i16);
51integer_impl!(i32);
52// integer_impl!(i64)   // data loss
53
54macro_rules! unsigned_impl(
55    ($t:ident) => (
56        impl<'lua, L> Push<L> for $t where L: AsMutLua<'lua> {
57            type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
58
59            #[inline]
60            fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
61                unsafe { ffi::lua_pushunsigned(lua.as_mut_lua().0, self as ffi::lua_Unsigned) };
62                let raw_lua = lua.as_lua();
63                Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
64            }
65        }
66        
67        impl<'lua, L> PushOne<L> for $t where L: AsMutLua<'lua> {
68        }
69
70        impl<'lua, L> LuaRead<L> for $t where L: AsLua<'lua> {
71            #[inline]
72            fn lua_read_at_position(lua: L, index: i32) -> Result<$t, L> {
73                let mut success = unsafe { mem::uninitialized() };
74                let val = unsafe { ffi::lua_tounsignedx(lua.as_lua().0, index, &mut success) };
75                match success {
76                    0 => Err(lua),
77                    _ => Ok(val as $t)
78                }
79            }
80        }
81    );
82);
83
84unsigned_impl!(u8);
85unsigned_impl!(u16);
86unsigned_impl!(u32);
87// unsigned_impl!(u64);   // data loss
88
89macro_rules! numeric_impl(
90    ($t:ident) => (
91        impl<'lua, L> Push<L> for $t where L: AsMutLua<'lua> {
92            type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
93
94            #[inline]
95            fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
96                unsafe { ffi::lua_pushnumber(lua.as_mut_lua().0, self as f64) };
97                let raw_lua = lua.as_lua();
98                Ok(PushGuard { lua: lua, size: 1, raw_lua: raw_lua })
99            }
100        }
101        
102        impl<'lua, L> PushOne<L> for $t where L: AsMutLua<'lua> {
103        }
104
105        impl<'lua, L> LuaRead<L> for $t where L: AsLua<'lua> {
106            #[inline]
107            fn lua_read_at_position(lua: L, index: i32) -> Result<$t, L> {
108                let mut success = unsafe { mem::uninitialized() };
109                let val = unsafe { ffi::lua_tonumberx(lua.as_lua().0, index, &mut success) };
110                match success {
111                    0 => Err(lua),
112                    _ => Ok(val as $t)
113                }
114            }
115        }
116    );
117);
118
119numeric_impl!(f32);
120numeric_impl!(f64);
121
122impl<'lua, L> Push<L> for String
123    where L: AsMutLua<'lua>
124{
125    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
126
127    #[inline]
128    fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
129        unsafe {
130            ffi::lua_pushlstring(lua.as_mut_lua().0,
131                                 self.as_bytes().as_ptr() as *const _,
132                                 self.as_bytes().len() as libc::size_t);
133
134            let raw_lua = lua.as_lua();
135            Ok(PushGuard {
136                lua: lua,
137                size: 1,
138                raw_lua: raw_lua,
139            })
140        }
141    }
142}
143
144impl<'lua, L> PushOne<L> for String where L: AsMutLua<'lua> {}
145
146impl<'lua, L> LuaRead<L> for String
147    where L: AsLua<'lua>
148{
149    #[inline]
150    fn lua_read_at_position(lua: L, index: i32) -> Result<String, L> {
151        let mut size: libc::size_t = unsafe { mem::uninitialized() };
152        let c_str_raw = unsafe { ffi::lua_tolstring(lua.as_lua().0, index, &mut size) };
153        if c_str_raw.is_null() {
154            return Err(lua);
155        }
156
157        let c_slice = unsafe { slice::from_raw_parts(c_str_raw as *const u8, size) };
158        let maybe_string = String::from_utf8(c_slice.to_vec());
159        match maybe_string {
160            Ok(string) => Ok(string),
161            Err(_) => Err(lua),
162        }
163    }
164}
165
166impl<'lua, L> Push<L> for AnyLuaString
167    where L: AsMutLua<'lua>
168{
169    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
170
171    #[inline]
172    fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
173        let AnyLuaString(v) = self;
174        unsafe {
175            ffi::lua_pushlstring(lua.as_mut_lua().0,
176                                 v[..].as_ptr() as *const _,
177                                 v[..].len() as libc::size_t);
178
179            let raw_lua = lua.as_lua();
180            Ok(PushGuard {
181                lua: lua,
182                size: 1,
183                raw_lua: raw_lua,
184            })
185        }
186    }
187}
188
189impl<'lua, L> LuaRead<L> for AnyLuaString
190    where L: AsLua<'lua>
191{
192    #[inline]
193    fn lua_read_at_position(lua: L, index: i32) -> Result<AnyLuaString, L> {
194        let mut size: libc::size_t = unsafe { mem::uninitialized() };
195        let c_str_raw = unsafe { ffi::lua_tolstring(lua.as_lua().0, index, &mut size) };
196        if c_str_raw.is_null() {
197            return Err(lua);
198        }
199
200        let c_slice = unsafe { slice::from_raw_parts(c_str_raw as *const u8, size) };
201        Ok(AnyLuaString(c_slice.to_vec()))
202    }
203}
204
205impl<'lua, 's, L> Push<L> for &'s str
206    where L: AsMutLua<'lua>
207{
208    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
209
210    #[inline]
211    fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
212        unsafe {
213            ffi::lua_pushlstring(lua.as_mut_lua().0,
214                                 self.as_bytes().as_ptr() as *const _,
215                                 self.as_bytes().len() as libc::size_t);
216
217            let raw_lua = lua.as_lua();
218            Ok(PushGuard {
219                lua: lua,
220                size: 1,
221                raw_lua: raw_lua,
222            })
223        }
224    }
225}
226
227impl<'lua, 's, L> PushOne<L> for &'s str where L: AsMutLua<'lua> {}
228
229/// String on the Lua stack.
230///
231/// It is faster -but less convenient- to read a `StringInLua` rather than a `String` because you
232/// avoid any allocation.
233///
234/// The `StringInLua` derefs to `str`.
235///
236/// # Example
237///
238/// ```
239/// let mut lua = hlua::Lua::new();
240/// lua.set("a", "hello");
241///
242/// let s: hlua::StringInLua<_> = lua.get("a").unwrap();
243/// println!("{}", &*s);    // Prints "hello".
244/// ```
245#[derive(Debug)]
246pub struct StringInLua<L> {
247    lua: L,
248    c_str_raw: *const libc::c_char,
249    size: libc::size_t,
250}
251
252impl<'lua, L> LuaRead<L> for StringInLua<L>
253    where L: AsLua<'lua>
254{
255    #[inline]
256    fn lua_read_at_position(lua: L, index: i32) -> Result<StringInLua<L>, L> {
257        let mut size: libc::size_t = unsafe { mem::uninitialized() };
258        let c_str_raw = unsafe { ffi::lua_tolstring(lua.as_lua().0, index, &mut size) };
259        if c_str_raw.is_null() {
260            return Err(lua);
261        }
262
263        let c_slice = unsafe { slice::from_raw_parts(c_str_raw as *const u8, size) };
264        match str::from_utf8(c_slice) {
265            Ok(_) => (),
266            Err(_) => return Err(lua)
267        };
268
269        Ok(StringInLua {
270            lua: lua,
271            c_str_raw: c_str_raw,
272            size: size,
273        })
274    }
275}
276
277impl<L> Deref for StringInLua<L> {
278    type Target = str;
279
280    #[inline]
281    fn deref(&self) -> &str {
282        let c_slice = unsafe { slice::from_raw_parts(self.c_str_raw as *const u8, self.size) };
283        match str::from_utf8(c_slice) {
284            Ok(s) => s,
285            Err(_) => unreachable!()        // Checked earlier
286        }
287    }
288}
289
290impl<'lua, L> Push<L> for bool
291    where L: AsMutLua<'lua>
292{
293    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
294
295    #[inline]
296    fn push_to_lua(self, mut lua: L) -> Result<PushGuard<L>, (Void, L)> {
297        unsafe { ffi::lua_pushboolean(lua.as_mut_lua().0, self.clone() as libc::c_int) };
298        let raw_lua = lua.as_lua();
299        Ok(PushGuard {
300            lua: lua,
301            size: 1,
302            raw_lua: raw_lua,
303        })
304    }
305}
306
307impl<'lua, L> PushOne<L> for bool where L: AsMutLua<'lua> {}
308
309impl<'lua, L> LuaRead<L> for bool
310    where L: AsLua<'lua>
311{
312    #[inline]
313    fn lua_read_at_position(lua: L, index: i32) -> Result<bool, L> {
314        if unsafe { ffi::lua_isboolean(lua.as_lua().0, index) } != true {
315            return Err(lua);
316        }
317
318        Ok(unsafe { ffi::lua_toboolean(lua.as_lua().0, index) != 0 })
319    }
320}
321
322impl<'lua, L> Push<L> for ()
323    where L: AsMutLua<'lua>
324{
325    type Err = Void;      // TODO: use `!` instead (https://github.com/rust-lang/rust/issues/35121)
326
327    #[inline]
328    fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (Void, L)> {
329        let raw_lua = lua.as_lua();
330
331        Ok(PushGuard {
332            lua: lua,
333            size: 0,
334            raw_lua: raw_lua,
335        })
336    }
337}
338
339impl<'lua, L> LuaRead<L> for ()
340    where L: AsLua<'lua>
341{
342    #[inline]
343    fn lua_read_at_position(_: L, _: i32) -> Result<(), L> {
344        Ok(())
345    }
346}
347
348impl<'lua, L, T, E> Push<L> for Option<T>
349where T: Push<L, Err = E>,
350      L: AsMutLua<'lua>
351{
352    type Err = E;
353
354    #[inline]
355    fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (E, L)> {
356        match self {
357            Some(val) => val.push_to_lua(lua),
358            None => Ok(AnyLuaValue::LuaNil.push_no_err(lua)),
359        }
360    }
361}
362
363impl<'lua, L, T, E> PushOne<L> for Option<T>
364where T: PushOne<L, Err = E>,
365      L: AsMutLua<'lua>
366{
367}
368
369#[cfg(test)]
370mod tests {
371    use AnyLuaValue;
372    use AnyLuaString;
373    use Lua;
374    use StringInLua;
375
376    #[test]
377    fn read_i32s() {
378        let mut lua = Lua::new();
379
380        lua.set("a", 2);
381
382        let x: i32 = lua.get("a").unwrap();
383        assert_eq!(x, 2);
384
385        let y: i8 = lua.get("a").unwrap();
386        assert_eq!(y, 2);
387
388        let z: i16 = lua.get("a").unwrap();
389        assert_eq!(z, 2);
390
391        let w: i32 = lua.get("a").unwrap();
392        assert_eq!(w, 2);
393
394        let a: u32 = lua.get("a").unwrap();
395        assert_eq!(a, 2);
396
397        let b: u8 = lua.get("a").unwrap();
398        assert_eq!(b, 2);
399
400        let c: u16 = lua.get("a").unwrap();
401        assert_eq!(c, 2);
402
403        let d: u32 = lua.get("a").unwrap();
404        assert_eq!(d, 2);
405    }
406
407    #[test]
408    fn write_i32s() {
409        // TODO:
410
411        let mut lua = Lua::new();
412
413        lua.set("a", 2);
414        let x: i32 = lua.get("a").unwrap();
415        assert_eq!(x, 2);
416    }
417
418    #[test]
419    fn readwrite_floats() {
420        let mut lua = Lua::new();
421
422        lua.set("a", 2.51234 as f32);
423        lua.set("b", 3.4123456789 as f64);
424
425        let x: f32 = lua.get("a").unwrap();
426        assert!(x - 2.51234 < 0.000001);
427
428        let y: f64 = lua.get("a").unwrap();
429        assert!(y - 2.51234 < 0.000001);
430
431        let z: f32 = lua.get("b").unwrap();
432        assert!(z - 3.4123456789 < 0.000001);
433
434        let w: f64 = lua.get("b").unwrap();
435        assert!(w - 3.4123456789 < 0.000001);
436    }
437
438    #[test]
439    fn readwrite_bools() {
440        let mut lua = Lua::new();
441
442        lua.set("a", true);
443        lua.set("b", false);
444
445        let x: bool = lua.get("a").unwrap();
446        assert_eq!(x, true);
447
448        let y: bool = lua.get("b").unwrap();
449        assert_eq!(y, false);
450    }
451
452    #[test]
453    fn readwrite_strings() {
454        let mut lua = Lua::new();
455
456        lua.set("a", "hello");
457        lua.set("b", "hello".to_string());
458
459        let x: String = lua.get("a").unwrap();
460        assert_eq!(x, "hello");
461
462        let y: String = lua.get("b").unwrap();
463        assert_eq!(y, "hello");
464
465        assert_eq!(lua.execute::<String>("return 'abc'").unwrap(), "abc");
466        assert_eq!(lua.execute::<u32>("return #'abc'").unwrap(), 3);
467        assert_eq!(lua.execute::<u32>("return #'a\\x00c'").unwrap(), 3);
468        assert_eq!(lua.execute::<AnyLuaString>("return 'a\\x00c'").unwrap().0, vec!(97, 0, 99));
469        assert_eq!(lua.execute::<AnyLuaString>("return 'a\\x00c'").unwrap().0.len(), 3);
470        assert_eq!(lua.execute::<AnyLuaString>("return '\\x01\\xff'").unwrap().0, vec!(1, 255));
471        lua.execute::<String>("return 'a\\x00\\xc0'").unwrap_err();
472    }
473
474    #[test]
475    fn i32_to_string() {
476        let mut lua = Lua::new();
477
478        lua.set("a", 2);
479
480        let x: String = lua.get("a").unwrap();
481        assert_eq!(x, "2");
482    }
483
484    #[test]
485    fn string_to_i32() {
486        let mut lua = Lua::new();
487
488        lua.set("a", "2");
489        lua.set("b", "aaa");
490
491        let x: i32 = lua.get("a").unwrap();
492        assert_eq!(x, 2);
493
494        let y: Option<i32> = lua.get("b");
495        assert!(y.is_none());
496    }
497
498    #[test]
499    fn string_on_lua() {
500        let mut lua = Lua::new();
501
502        lua.set("a", "aaa");
503        {
504            let x: StringInLua<_> = lua.get("a").unwrap();
505            assert_eq!(&*x, "aaa");
506        }
507        
508        lua.set("a", 18);
509        {
510            let x: StringInLua<_> = lua.get("a").unwrap();
511            assert_eq!(&*x, "18");
512        }
513    }
514
515    #[test]
516    fn push_opt() {
517        let mut lua = Lua::new();
518
519        lua.set("some", ::function0(|| Some(123)));
520        lua.set("none", ::function0(|| Option::None::<i32>));
521
522        match lua.execute::<i32>("return some()") {
523            Ok(123) => {}
524            unexpected => panic!("{:?}", unexpected),
525        }
526
527        match lua.execute::<AnyLuaValue>("return none()") {
528            Ok(AnyLuaValue::LuaNil) => {}
529            unexpected => panic!("{:?}", unexpected),
530        }
531
532        lua.set("no_value", None::<i32>);
533        lua.set("some_value", Some("Hello!"));
534
535        assert_eq!(lua.get("no_value"), None::<String>);
536        assert_eq!(lua.get("some_value"), Some("Hello!".to_string()));
537    }
538}