luajit_bindings/
poppable.rs

1use std::collections::HashMap;
2use std::hash::Hash;
3
4use crate::ffi::*;
5use crate::macros::count;
6use crate::Error;
7
8/// Trait implemented for types that can be popped off the Lua stack.
9pub trait Poppable: Sized {
10    /// Pops the value at the top of the stack.
11    unsafe fn pop(lua_state: *mut lua_State) -> Result<Self, Error>;
12}
13
14impl Poppable for () {
15    unsafe fn pop(state: *mut lua_State) -> Result<Self, crate::Error> {
16        if lua_gettop(state) == 0 {
17            return Ok(());
18        }
19
20        match lua_type(state, -1) {
21            LUA_TNIL => {
22                lua_pop(state, 1);
23                Ok(())
24            },
25            other => Err(Error::pop_wrong_type::<Self>(LUA_TNIL, other)),
26        }
27    }
28}
29
30impl Poppable for bool {
31    unsafe fn pop(state: *mut lua_State) -> Result<Self, Error> {
32        if lua_gettop(state) == 0 {
33            return Err(Error::PopEmptyStack);
34        }
35
36        match lua_type(state, -1) {
37            LUA_TBOOLEAN => {
38                let b = lua_toboolean(state, -1) == 1;
39                lua_pop(state, 1);
40                Ok(b)
41            },
42            other => Err(Error::pop_wrong_type::<Self>(LUA_TBOOLEAN, other)),
43        }
44    }
45}
46
47impl Poppable for lua_Integer {
48    unsafe fn pop(state: *mut lua_State) -> Result<Self, crate::Error> {
49        if lua_gettop(state) == 0 {
50            return Err(Error::PopEmptyStack);
51        }
52
53        match lua_type(state, -1) {
54            LUA_TNUMBER => {
55                let n = lua_tointeger(state, -1);
56                lua_pop(state, 1);
57                Ok(n)
58            },
59            other => Err(Error::pop_wrong_type::<Self>(LUA_TNUMBER, other)),
60        }
61    }
62}
63
64/// Implements `Poppable` for a integer types that implement
65/// `TryFrom<lua_Integer>`.
66macro_rules! pop_try_from_integer {
67    ($integer:ty) => {
68        impl Poppable for $integer {
69            unsafe fn pop(
70                lstate: *mut lua_State,
71            ) -> Result<Self, crate::Error> {
72                lua_Integer::pop(lstate)?
73                    .try_into()
74                    .map_err(Error::pop_error_from_err::<Self, _>)
75            }
76        }
77    };
78}
79
80pop_try_from_integer!(i8);
81pop_try_from_integer!(u8);
82pop_try_from_integer!(i16);
83pop_try_from_integer!(u16);
84pop_try_from_integer!(i32);
85pop_try_from_integer!(u32);
86pop_try_from_integer!(i64);
87pop_try_from_integer!(u64);
88pop_try_from_integer!(usize);
89
90impl Poppable for lua_Number {
91    unsafe fn pop(state: *mut lua_State) -> Result<Self, crate::Error> {
92        if lua_gettop(state) == 0 {
93            return Err(Error::PopEmptyStack);
94        }
95
96        match lua_type(state, -1) {
97            LUA_TNUMBER => {
98                let n = lua_tonumber(state, -1);
99                lua_pop(state, 1);
100                Ok(n)
101            },
102            other => Err(Error::pop_wrong_type::<Self>(LUA_TNUMBER, other)),
103        }
104    }
105}
106
107impl Poppable for f32 {
108    unsafe fn pop(state: *mut lua_State) -> Result<Self, crate::Error> {
109        lua_Number::pop(state).map(|n| n as f32)
110    }
111}
112
113impl Poppable for String {
114    unsafe fn pop(state: *mut lua_State) -> Result<Self, Error> {
115        if lua_gettop(state) == 0 {
116            return Err(Error::PopEmptyStack);
117        }
118
119        match lua_type(state, -1) {
120            LUA_TSTRING | LUA_TNUMBER => {
121                let mut len = 0;
122                let ptr = lua_tolstring(state, -1, &mut len);
123
124                // NOTE: `ptr` should never be null if the value at the top of
125                // the stack is a string or a number.
126                assert!(!ptr.is_null());
127
128                let slice = std::slice::from_raw_parts(ptr as *const u8, len);
129                let str = String::from_utf8_lossy(slice).to_string();
130
131                lua_pop(state, 1);
132
133                Ok(str)
134            },
135            other => Err(Error::pop_wrong_type::<Self>(LUA_TSTRING, other)),
136        }
137    }
138}
139
140impl<T> Poppable for Option<T>
141where
142    T: Poppable,
143{
144    unsafe fn pop(state: *mut lua_State) -> Result<Self, Error> {
145        if lua_gettop(state) == 0 {
146            return Err(Error::PopEmptyStack);
147        }
148
149        match lua_type(state, -1) {
150            LUA_TNIL => {
151                lua_pop(state, 1);
152                Ok(None)
153            },
154            _ => T::pop(state).map(Some),
155        }
156    }
157}
158
159impl<T> Poppable for Vec<T>
160where
161    T: Poppable,
162{
163    unsafe fn pop(state: *mut lua_State) -> Result<Self, Error> {
164        if lua_gettop(state) == 0 {
165            return Err(Error::PopEmptyStack);
166        }
167
168        match lua_type(state, -1) {
169            LUA_TTABLE => {
170                // TODO: check that the table is an array-like table and not a
171                // dictionary-like one.
172
173                let mut vec = Vec::with_capacity(lua_objlen(state, -1));
174
175                lua_pushnil(state);
176
177                while lua_next(state, -2) != 0 {
178                    vec.push(T::pop(state)?);
179                }
180
181                // Pop the table.
182                lua_pop(state, 1);
183
184                Ok(vec)
185            },
186
187            other => Err(Error::pop_wrong_type::<Self>(LUA_TTABLE, other)),
188        }
189    }
190}
191
192impl<K, V> Poppable for HashMap<K, V>
193where
194    K: Poppable + Eq + Hash,
195    V: Poppable,
196{
197    unsafe fn pop(state: *mut lua_State) -> Result<Self, Error> {
198        if lua_gettop(state) == 0 {
199            return Err(Error::PopEmptyStack);
200        }
201
202        match lua_type(state, -1) {
203            LUA_TTABLE => {
204                // TODO: check that the table is an dictionary-like table and
205                // not an array-like one.
206
207                let mut map = HashMap::with_capacity(lua_objlen(state, -1));
208
209                lua_pushnil(state);
210
211                while lua_next(state, -2) != 0 {
212                    let value = V::pop(state)?;
213
214                    // NOTE: the following `K::pop` will pop the key, so we
215                    // push another copy of the key on the stack for the next
216                    // iteration.
217                    lua_pushvalue(state, -1);
218
219                    let key = K::pop(state)?;
220
221                    map.insert(key, value);
222                }
223
224                // Pop the table.
225                lua_pop(state, 1);
226
227                Ok(map)
228            },
229
230            other => Err(Error::pop_wrong_type::<Self>(LUA_TTABLE, other)),
231        }
232    }
233}
234
235/// Implements `Poppable` for a tuple `(a, b, c, ..)` where all the elements
236/// in the tuple implement `Poppable`.
237macro_rules! pop_tuple {
238    ($($name:ident)*) => (
239        impl<$($name,)*> Poppable for ($($name,)*)
240        where
241            $($name: Poppable,)*
242        {
243            #[allow(non_snake_case)]
244            unsafe fn pop(state: *mut lua_State) -> Result<Self, crate::Error> {
245                crate::utils::grow_stack(state, count!($($name)*));
246                pop_reverse!(state, $($name)*);
247                Ok(($($name,)*))
248            }
249        }
250    );
251}
252
253macro_rules! pop_reverse {
254    ($lua_state:expr, $x:ident $($xs:ident)*) => {
255        pop_reverse!($lua_state, $($xs)*);
256        let $x = $x::pop($lua_state)?;
257    };
258
259    ($lstate:expr,) => ();
260}
261
262pop_tuple!(A);
263pop_tuple!(A B);
264pop_tuple!(A B C);
265pop_tuple!(A B C D);
266pop_tuple!(A B C D E);
267pop_tuple!(A B C D E F);
268pop_tuple!(A B C D E F G);
269pop_tuple!(A B C D E F G H);
270pop_tuple!(A B C D E F G H I);
271pop_tuple!(A B C D E F G H I J);
272pop_tuple!(A B C D E F G H I J K);
273pop_tuple!(A B C D E F G H I J K L);
274pop_tuple!(A B C D E F G H I J K L M);
275pop_tuple!(A B C D E F G H I J K L M N);
276pop_tuple!(A B C D E F G H I J K L M N O);
277pop_tuple!(A B C D E F G H I J K L M N O P);