nvim_oxi_luajit/
poppable.rs

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