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