tlua/
object.rs

1use crate::{
2    impl_object, AbsoluteIndex, AsLua, LuaError, LuaRead, LuaState, Push, PushGuard, PushInto,
3    PushOneInto, ReadResult, Void,
4};
5use std::{error::Error, fmt, num::NonZeroI32};
6
7////////////////////////////////////////////////////////////////////////////////
8// Object
9////////////////////////////////////////////////////////////////////////////////
10
11/// A single value stored on the lua stack. Type parameter `L` represents a
12/// value guarding the state of the lua stack (see [`PushGuard`]).
13///
14/// Use this type to convert between different lua values, e.g. [`LuaTable`] <->
15/// [`Indexable`], etc.
16///
17/// [`LuaTable`]: crate::lua_tables::LuaTable
18#[derive(Debug)]
19pub struct Object<L> {
20    guard: L,
21    index: AbsoluteIndex,
22}
23
24impl<L: AsLua> Object<L> {
25    #[inline(always)]
26    pub(crate) fn new(guard: L, index: NonZeroI32) -> Self
27    where
28        L: AsLua,
29    {
30        Self {
31            index: AbsoluteIndex::new(index, guard.as_lua()),
32            guard,
33        }
34    }
35
36    #[inline(always)]
37    pub fn guard(&self) -> &L {
38        &self.guard
39    }
40
41    #[inline(always)]
42    pub fn into_guard(self) -> L {
43        self.guard
44    }
45
46    #[inline(always)]
47    pub fn index(&self) -> AbsoluteIndex {
48        self.index
49    }
50
51    /// Try converting to a value implementing [`LuaRead`].
52    ///
53    /// # Safety
54    ///
55    /// In some cases this function will result in a drop of `self.guard` which
56    /// is invalid in case `self.index` is not the top of the lua stack.
57    #[inline(always)]
58    pub unsafe fn try_downcast<T>(self) -> Result<T, Self>
59    where
60        T: LuaRead<L>,
61    {
62        let Self { guard, index } = self;
63        T::lua_read_at_position(guard, index.0).map_err(|(guard, _)| Self { guard, index })
64    }
65}
66
67impl<L> AsLua for Object<L>
68where
69    L: AsLua,
70{
71    fn as_lua(&self) -> LuaState {
72        self.guard.as_lua()
73    }
74}
75
76impl<L> LuaRead<L> for Object<L>
77where
78    L: AsLua,
79{
80    fn lua_read_at_position(lua: L, index: NonZeroI32) -> ReadResult<Self, L> {
81        Ok(Self::new(lua, index))
82    }
83}
84
85impl<L, K> Push<L> for Object<K>
86where
87    L: AsLua,
88    K: AsLua,
89{
90    type Err = Void;
91    fn push_to_lua(&self, lua: L) -> crate::PushResult<L, Self> {
92        unsafe {
93            crate::ffi::lua_pushvalue(lua.as_lua(), self.index.into());
94            Ok(PushGuard::new(lua, 1))
95        }
96    }
97}
98impl<L> crate::PushOne<L> for Object<L> where L: AsLua {}
99
100impl<L, K> PushInto<L> for Object<K>
101where
102    L: AsLua,
103    K: AsLua,
104{
105    type Err = Void;
106    fn push_into_lua(self, lua: L) -> crate::PushIntoResult<L, Self> {
107        unsafe {
108            crate::ffi::lua_pushvalue(lua.as_lua(), self.index.into());
109            Ok(PushGuard::new(lua, 1))
110        }
111    }
112}
113impl<L> crate::PushOneInto<L> for Object<L> where L: AsLua {}
114
115/// Types implementing this trait represent a single value stored on the lua
116/// stack. Type parameter `L` represents a value guarding the state of the lua
117/// stack (see [`PushGuard`]).
118pub trait FromObject<L: AsLua> {
119    /// Check if a value at `index` satisfies the given type's invariants
120    unsafe fn check(lua: impl AsLua, index: NonZeroI32) -> bool;
121
122    /// Duh
123    unsafe fn from_obj(inner: Object<L>) -> Self;
124
125    fn try_from_obj(inner: Object<L>) -> Result<Self, Object<L>>
126    where
127        Self: Sized,
128        L: AsLua,
129    {
130        if unsafe { Self::check(inner.guard(), inner.index().0) } {
131            Ok(unsafe { Self::from_obj(inner) })
132        } else {
133            Err(inner)
134        }
135    }
136}
137
138////////////////////////////////////////////////////////////////////////////////
139// Index
140////////////////////////////////////////////////////////////////////////////////
141
142/// Types implementing this trait represent a single lua value that can be
143/// indexed, i.e. a regular lua table or other value implementing `__index`
144/// metamethod.
145pub trait Index<L>: AsRef<Object<L>>
146where
147    L: AsLua,
148{
149    /// Loads a value from the table (or other object using the `__index`
150    /// metamethod) given its `index`.
151    ///
152    /// The index must implement the [`PushOneInto`] trait and the return type
153    /// must implement the [`LuaRead`] trait. See [the documentation at the
154    /// crate root](index.html#pushing-and-loading-values) for more information.
155    #[track_caller]
156    #[inline(always)]
157    fn get<'lua, K, R>(&'lua self, key: K) -> Option<R>
158    where
159        L: 'lua,
160        K: PushOneInto<LuaState>,
161        K::Err: Into<Void>,
162        R: LuaRead<PushGuard<&'lua L>>,
163    {
164        self.try_get(key).ok()
165    }
166
167    /// Loads a value from the table (or other object using the `__index`
168    /// metamethod) given its `index`.
169    ///
170    /// # Possible errors:
171    /// - `LuaError::ExecutionError` if an error happened during the check that
172    ///     `index` is valid in `self`
173    /// - `LuaError::WrongType` if the result lua value couldn't be read as the
174    ///     expected rust type
175    ///
176    /// The index must implement the [`PushOneInto`] trait and the return type
177    /// must implement the [`LuaRead`] trait. See [the documentation at the
178    /// crate root](index.html#pushing-and-loading-values) for more information.
179    #[track_caller]
180    #[inline]
181    fn try_get<'lua, K, R>(&'lua self, key: K) -> Result<R, LuaError>
182    where
183        L: 'lua,
184        K: PushOneInto<LuaState>,
185        K::Err: Into<Void>,
186        R: LuaRead<PushGuard<&'lua L>>,
187    {
188        let Object { guard, index } = self.as_ref();
189        unsafe { imp::try_get(guard, *index, key).map_err(|(_, e)| e) }
190    }
191
192    /// Loads a value in the table (or other object using the `__index`
193    /// metamethod) given its `index`, with the result capturing the table by
194    /// value.
195    ///
196    /// See also [`Index::get`]
197    #[track_caller]
198    #[inline(always)]
199    fn into_get<K, R>(self, key: K) -> Result<R, Self>
200    where
201        Self: AsLua + Sized,
202        K: PushOneInto<LuaState>,
203        K::Err: Into<Void>,
204        R: LuaRead<PushGuard<Self>>,
205    {
206        self.try_into_get(key).map_err(|(this, _)| this)
207    }
208
209    /// Loads a value in the table (or other object using the `__index`
210    /// metamethod) given its `index`, with the result capturing the table by
211    /// value.
212    ///
213    /// # Possible errors:
214    /// - `LuaError::ExecutionError` if an error happened during the check that
215    ///     `index` is valid in `self`
216    /// - `LuaError::WrongType` if the result lua value couldn't be read as the
217    ///     expected rust type
218    ///
219    /// See also [`Index::get`]
220    #[track_caller]
221    #[inline]
222    fn try_into_get<K, R>(self, key: K) -> Result<R, (Self, LuaError)>
223    where
224        Self: AsLua + Sized,
225        K: PushOneInto<LuaState>,
226        K::Err: Into<Void>,
227        R: LuaRead<PushGuard<Self>>,
228    {
229        let this_index = self.as_ref().index;
230        unsafe { imp::try_get(self, this_index, key) }
231    }
232
233    /// Calls the method called `name` of the table (or other indexable object)
234    /// with the provided `args`.
235    ///
236    /// Possible errors:
237    /// - `MethodCallError::NoSuchMethod` in case `self[name]` is `nil`
238    /// - `MethodCallError::PushError` if pushing `args` failed
239    /// - `MethodCallError::LuaError` if error happened during the function call
240    #[track_caller]
241    #[inline]
242    fn call_method<'lua, A, R>(
243        &'lua self,
244        name: &str,
245        args: A,
246    ) -> Result<R, MethodCallError<A::Err>>
247    where
248        L: 'lua,
249        Self: Push<LuaState>,
250        Self::Err: Into<Void>,
251        A: PushInto<LuaState>,
252        R: LuaRead<PushGuard<Callable<PushGuard<&'lua L>>>>,
253    {
254        use MethodCallError::{LuaError, NoSuchMethod, PushError};
255
256        self.get::<_, Callable<_>>(name)
257            .ok_or(NoSuchMethod)?
258            .into_call_with((self, args))
259            .map_err(|e| match e {
260                CallError::LuaError(e) => LuaError(e),
261                CallError::PushError(e) => PushError(e.other().first()),
262            })
263    }
264}
265
266#[derive(Debug)]
267pub enum MethodCallError<E> {
268    /// The corresponding method was not found (t\[k] == nil)
269    NoSuchMethod,
270    /// Error during function call
271    LuaError(LuaError),
272    /// Pushing arguments failed
273    PushError(E),
274}
275
276impl<E> From<CallError<E>> for MethodCallError<E> {
277    fn from(e: CallError<E>) -> Self {
278        match e {
279            CallError::PushError(e) => Self::PushError(e),
280            CallError::LuaError(e) => Self::LuaError(e),
281        }
282    }
283}
284
285impl<E> fmt::Display for MethodCallError<E>
286where
287    E: fmt::Display,
288{
289    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290        match self {
291            Self::NoSuchMethod => f.write_str("Method not found"),
292            Self::LuaError(lua_error) => write!(f, "Lua error: {}", lua_error),
293            Self::PushError(err) => {
294                write!(f, "Error while pushing arguments: {}", err)
295            }
296        }
297    }
298}
299
300impl<E> Error for MethodCallError<E>
301where
302    E: Error,
303{
304    fn description(&self) -> &str {
305        match self {
306            Self::NoSuchMethod => "Method not found",
307            Self::LuaError(_) => "Lua error",
308            Self::PushError(_) => "Error while pushing arguments",
309        }
310    }
311
312    fn cause(&self) -> Option<&dyn Error> {
313        match self {
314            Self::NoSuchMethod => None,
315            Self::LuaError(lua_error) => Some(lua_error),
316            Self::PushError(err) => Some(err),
317        }
318    }
319}
320
321////////////////////////////////////////////////////////////////////////////////
322// Indexable
323////////////////////////////////////////////////////////////////////////////////
324
325/// An opaque value on lua stack that can be indexed. Can represent a lua
326/// table, a lua table with a `__index` metamethod or other indexable lua
327/// value.
328///
329/// Use this type when reading return values from lua functions or getting lua
330/// function from tables.
331#[derive(Debug)]
332pub struct Indexable<L> {
333    inner: Object<L>,
334}
335
336impl_object! { Indexable,
337    check(lua, index) {
338        imp::is_indexable(&lua, index)
339    }
340    impl Index,
341}
342
343////////////////////////////////////////////////////////////////////////////////
344// NewIndex
345////////////////////////////////////////////////////////////////////////////////
346
347/// Types implementing this trait represent a single lua value that can be
348/// changed by indexed, i.e. a regular lua table or other value implementing
349/// `__newindex` metamethod.
350pub trait NewIndex<L>: AsRef<Object<L>>
351where
352    L: AsLua,
353{
354    /// Inserts or modifies a `value` of the table (or other object using the
355    /// `__index` or `__newindex` metamethod) given its `index`.
356    ///
357    /// Contrary to [`NewIndex::checked_set`], can only be called when writing
358    /// the key and value cannot fail (which is the case for most types).
359    ///
360    /// # Panic
361    ///
362    /// Will panic if an error happens during attempt to set value. Can happen
363    /// if `__index` or `__newindex` throws an error. Use [`NewIndex::try_set`]
364    /// if this is a possibility in your case.
365    ///
366    /// The index must implement the [`PushOneInto`] trait and the return type
367    /// must implement the [`LuaRead`] trait. See [the documentation at the
368    /// crate root](index.html#pushing-and-loading-values) for more information.
369    #[track_caller]
370    #[inline(always)]
371    fn set<K, V>(&self, key: K, value: V)
372    where
373        K: PushOneInto<LuaState>,
374        K::Err: Into<Void>,
375        V: PushOneInto<LuaState>,
376        V::Err: Into<Void>,
377    {
378        if let Err(e) = self.try_set(key, value) {
379            panic!("Setting value failed: {}", e)
380        }
381    }
382
383    /// Inserts or modifies a `value` of the table (or other object using the
384    /// `__index` or `__newindex` metamethod) given its `index`.
385    ///
386    /// Contrary to [`NewIndex::try_checked_set`], can only be called when
387    /// writing the key and value cannot fail (which is the case for most
388    /// types).
389    ///
390    /// Returns a `LuaError::ExecutionError` in case an error happened during an
391    /// attempt to set value.
392    ///
393    /// The index must implement the [`PushOneInto`] trait and the return type
394    /// must implement the [`LuaRead`] trait. See [the documentation at the
395    /// crate root](index.html#pushing-and-loading-values) for more information.
396    #[track_caller]
397    #[inline]
398    fn try_set<K, V>(&self, key: K, value: V) -> Result<(), LuaError>
399    where
400        K: PushOneInto<LuaState>,
401        K::Err: Into<Void>,
402        V: PushOneInto<LuaState>,
403        V::Err: Into<Void>,
404    {
405        let Object { guard, index } = self.as_ref();
406        unsafe { imp::try_checked_set(guard, *index, key, value) }.map_err(|e| match e {
407            Ok(_) => unreachable!("Void is uninstantiatable"),
408            Err(e) => e,
409        })
410    }
411
412    /// Inserts or modifies a `value` of the table (or other object using the
413    /// `__newindex` metamethod) given its `index`.
414    ///
415    /// Returns an error if pushing `index` or `value` failed. This can only
416    /// happen for a limited set of types. You are encouraged to use the
417    /// [`NewIndex::set`]
418    /// method if pushing cannot fail.
419    ///
420    /// # Panic
421    ///
422    /// Will panic if an error happens during attempt to set value. Can happen
423    /// if `__index` or `__newindex` throws an error. Use
424    /// [`NewIndex::try_checked_set`] if this is a possibility in your case.
425    #[track_caller]
426    #[inline(always)]
427    fn checked_set<K, V>(&self, key: K, value: V) -> Result<(), CheckedSetError<K::Err, V::Err>>
428    where
429        K: PushOneInto<LuaState>,
430        V: PushOneInto<LuaState>,
431    {
432        self.try_checked_set(key, value)
433            .map_err(|e| e.unwrap_or_else(|e| panic!("Setting value failed: {}", e)))
434    }
435
436    /// Inserts or modifies a `value` of the table (or other object using the
437    /// `__newindex` metamethod) given its `index`.
438    ///
439    /// # Possible errors
440    /// - Returns an error if pushing `index` or `value` failed. This can only
441    ///   happen for a limited set of types. You are encouraged to use the
442    ///   [`NewIndex::set`] method if pushing cannot fail.
443    /// - Returns a `LuaError::ExecutionError` in case an error happened during
444    ///   an attempt to set value.
445    #[track_caller]
446    #[inline(always)]
447    fn try_checked_set<K, V>(
448        &self,
449        key: K,
450        value: V,
451    ) -> Result<(), TryCheckedSetError<K::Err, V::Err>>
452    where
453        K: PushOneInto<LuaState>,
454        V: PushOneInto<LuaState>,
455    {
456        let Object { guard, index } = self.as_ref();
457        unsafe { imp::try_checked_set(guard, *index, key, value) }
458    }
459}
460
461pub type TryCheckedSetError<K, V> = Result<CheckedSetError<K, V>, LuaError>;
462
463/// Error returned by the [`NewIndex::checked_set`] function.
464#[derive(Debug, Copy, Clone)]
465pub enum CheckedSetError<K, V> {
466    /// Error while pushing the key.
467    KeyPushError(K),
468    /// Error while pushing the value.
469    ValuePushError(V),
470}
471
472////////////////////////////////////////////////////////////////////////////////
473// IndexableRW
474////////////////////////////////////////////////////////////////////////////////
475
476/// An opaque value on lua stack that can be indexed immutably as well as
477/// mutably. Can represent a lua table, a lua table with a `__index` and
478/// `__newindex` metamethods or other indexable lua value.
479///
480/// Use this type when reading return values from lua functions or getting lua
481/// function from tables.
482#[derive(Debug)]
483pub struct IndexableRW<L> {
484    inner: Object<L>,
485}
486
487impl_object! { IndexableRW,
488    check(lua, index) {
489        imp::is_rw_indexable(&lua, index)
490    }
491    impl Index,
492    impl NewIndex,
493}
494
495////////////////////////////////////////////////////////////////////////////////
496// Call
497////////////////////////////////////////////////////////////////////////////////
498
499pub trait Call<L>: AsRef<Object<L>>
500where
501    L: AsLua,
502{
503    #[track_caller]
504    #[inline]
505    fn call<'lua, R>(&'lua self) -> Result<R, LuaError>
506    where
507        L: 'lua,
508        R: LuaRead<PushGuard<&'lua L>>,
509    {
510        Ok(self.call_with(())?)
511    }
512
513    #[track_caller]
514    #[inline]
515    fn call_with<'lua, A, R>(&'lua self, args: A) -> Result<R, CallError<A::Err>>
516    where
517        L: 'lua,
518        A: PushInto<LuaState>,
519        R: LuaRead<PushGuard<&'lua L>>,
520    {
521        let Object { guard, index } = self.as_ref();
522        imp::call(guard, *index, args)
523    }
524
525    #[track_caller]
526    #[inline]
527    fn into_call<R>(self) -> Result<R, LuaError>
528    where
529        Self: AsLua + Sized,
530        R: LuaRead<PushGuard<Self>>,
531    {
532        Ok(self.into_call_with(())?)
533    }
534
535    #[track_caller]
536    #[inline]
537    fn into_call_with<A, R>(self, args: A) -> Result<R, CallError<A::Err>>
538    where
539        Self: AsLua + Sized,
540        A: PushInto<LuaState>,
541        R: LuaRead<PushGuard<Self>>,
542    {
543        let index = self.as_ref().index;
544        imp::call(self, index, args)
545    }
546}
547
548/// Error that can happen when calling a type implementing [`Call`].
549#[derive(Debug)]
550pub enum CallError<E> {
551    /// Error while executing the function.
552    LuaError(LuaError),
553    /// Error while pushing one of the parameters.
554    PushError(E),
555}
556
557impl<E> CallError<E> {
558    pub fn map<F, R>(self, f: F) -> CallError<R>
559    where
560        F: FnOnce(E) -> R,
561    {
562        match self {
563            CallError::LuaError(e) => CallError::LuaError(e),
564            CallError::PushError(e) => CallError::PushError(f(e)),
565        }
566    }
567}
568
569impl<E> From<LuaError> for CallError<E> {
570    fn from(e: LuaError) -> Self {
571        Self::LuaError(e)
572    }
573}
574
575impl<E> From<CallError<E>> for LuaError
576where
577    E: Into<Void>,
578{
579    fn from(e: CallError<E>) -> Self {
580        match e {
581            CallError::LuaError(le) => le,
582            CallError::PushError(_) => {
583                unreachable!("no way to create instance of Void")
584            }
585        }
586    }
587}
588
589impl<E> fmt::Display for CallError<E>
590where
591    E: fmt::Display,
592{
593    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
594        match self {
595            Self::LuaError(lua_error) => write!(f, "Lua error: {}", lua_error),
596            Self::PushError(err) => {
597                write!(f, "Error while pushing arguments: {}", err)
598            }
599        }
600    }
601}
602
603impl<E> Error for CallError<E>
604where
605    E: Error,
606{
607    fn description(&self) -> &str {
608        match self {
609            Self::LuaError(_) => "Lua error",
610            Self::PushError(_) => "error while pushing arguments",
611        }
612    }
613
614    fn cause(&self) -> Option<&dyn Error> {
615        match self {
616            Self::LuaError(lua_error) => Some(lua_error),
617            Self::PushError(err) => Some(err),
618        }
619    }
620}
621
622////////////////////////////////////////////////////////////////////////////////
623// Callable
624////////////////////////////////////////////////////////////////////////////////
625
626/// An opaque value on lua stack that can be called. Can represent a lua
627/// function, a lua table with a `__call` metamethod or other callable lua
628/// value.
629///
630/// Use this type when reading return values from lua functions or getting lua
631/// function from tables.
632#[derive(Debug)]
633pub struct Callable<L> {
634    inner: Object<L>,
635}
636
637impl_object! { Callable,
638    check(lua, index) {
639        imp::is_callable(&lua, index)
640    }
641    impl Call,
642}
643
644////////////////////////////////////////////////////////////////////////////////
645// imp
646////////////////////////////////////////////////////////////////////////////////
647
648mod imp {
649    use super::{CallError, CheckedSetError, TryCheckedSetError};
650    use crate::{
651        c_ptr, ffi, nzi32, AbsoluteIndex, AsLua, LuaError, LuaRead, LuaState, PushGuard, PushInto,
652        PushOneInto, ToString, Void, WrongType,
653    };
654    use std::num::NonZeroI32;
655
656    ////////////////////////////////////////////////////////////////////////////
657    // try_get
658    ////////////////////////////////////////////////////////////////////////////
659
660    #[track_caller]
661    pub(super) unsafe fn try_get<T, K, R>(
662        this: T,
663        this_index: AbsoluteIndex,
664        key: K,
665    ) -> Result<R, (T, LuaError)>
666    where
667        T: AsLua,
668        K: PushOneInto<LuaState>,
669        K::Err: Into<Void>,
670        R: LuaRead<PushGuard<T>>,
671    {
672        let raw_lua = this.as_lua();
673        let this_index = this_index.into();
674
675        if ffi::lua_istable(raw_lua, this_index)
676            && !ffi::luaL_hasmetafield(raw_lua, this_index, c_ptr!("__index"))
677        {
678            // push key
679            raw_lua.push_one(key).assert_one_and_forget();
680            // replace key with value
681            ffi::lua_rawget(raw_lua, this_index);
682        } else {
683            // push index onto the stack
684            raw_lua.push_one(key).assert_one_and_forget();
685            // move index into registry
686            let index_ref = ffi::luaL_ref(raw_lua, ffi::LUA_REGISTRYINDEX);
687            // push indexable onto the stack
688            ffi::lua_pushvalue(raw_lua, this_index);
689            // move indexable into registry
690            let table_ref = ffi::luaL_ref(raw_lua, ffi::LUA_REGISTRYINDEX);
691
692            let res = raw_lua.pcall(|l| {
693                let raw_lua = l.as_lua();
694                // push indexable
695                ffi::lua_rawgeti(raw_lua, ffi::LUA_REGISTRYINDEX, table_ref);
696                // push index
697                ffi::lua_rawgeti(raw_lua, ffi::LUA_REGISTRYINDEX, index_ref);
698                // pop index, push value
699                ffi::lua_gettable(raw_lua, -2);
700                // save value
701                ffi::luaL_ref(raw_lua, ffi::LUA_REGISTRYINDEX)
702                // stack is temporary so indexable is discarded after return
703            });
704            let value_ref = match res {
705                Ok(value_ref) => value_ref,
706                Err(e) => return Err((this, e)),
707            };
708
709            // move value from registry to stack
710            ffi::lua_rawgeti(raw_lua, ffi::LUA_REGISTRYINDEX, value_ref);
711
712            // unref temporaries
713            ffi::luaL_unref(raw_lua, ffi::LUA_REGISTRYINDEX, value_ref);
714            ffi::luaL_unref(raw_lua, ffi::LUA_REGISTRYINDEX, index_ref);
715            ffi::luaL_unref(raw_lua, ffi::LUA_REGISTRYINDEX, table_ref);
716        }
717
718        R::lua_read_at_position(PushGuard::new(this, 1), nzi32!(-1)).map_err(|(g, e)| {
719            let e = WrongType::info("reading value from Lua table")
720                .expected_type::<R>()
721                .actual_single_lua(raw_lua, nzi32!(-1))
722                .subtype(e);
723            (g.into_inner(), e.into())
724        })
725    }
726
727    ////////////////////////////////////////////////////////////////////////////
728    // try_checked_set
729    ////////////////////////////////////////////////////////////////////////////
730
731    #[track_caller]
732    pub(super) unsafe fn try_checked_set<T, K, V>(
733        this: T,
734        this_index: AbsoluteIndex,
735        key: K,
736        value: V,
737    ) -> Result<(), TryCheckedSetError<K::Err, V::Err>>
738    where
739        T: AsLua,
740        K: PushOneInto<LuaState>,
741        V: PushOneInto<LuaState>,
742    {
743        let raw_lua = this.as_lua();
744        let this_index = this_index.into();
745        if ffi::lua_istable(raw_lua, this_index)
746            && !ffi::luaL_hasmetafield(raw_lua, this_index, c_ptr!("__index"))
747            && !ffi::luaL_hasmetafield(raw_lua, this_index, c_ptr!("__newindex"))
748        {
749            // push key
750            raw_lua
751                .try_push_one(key)
752                .map_err(|(e, _)| Ok(CheckedSetError::KeyPushError(e)))?
753                .assert_one_and_forget();
754            // push value
755            raw_lua
756                .try_push_one(value)
757                .map_err(|(e, _)| Ok(CheckedSetError::ValuePushError(e)))?
758                .assert_one_and_forget();
759            // remove key and value
760            ffi::lua_rawset(raw_lua, this_index);
761        } else {
762            // push value onto the stack
763            raw_lua
764                .try_push_one(value)
765                .map_err(|(e, _)| Ok(CheckedSetError::ValuePushError(e)))?
766                .assert_one_and_forget();
767            // move value into registry
768            let value_ref = ffi::luaL_ref(raw_lua, ffi::LUA_REGISTRYINDEX);
769
770            // push index onto the stack
771            raw_lua
772                .try_push_one(key)
773                .map_err(|(e, _)| Ok(CheckedSetError::KeyPushError(e)))?
774                .assert_one_and_forget();
775            // move index into registry
776            let index_ref = ffi::luaL_ref(raw_lua, ffi::LUA_REGISTRYINDEX);
777
778            // push indexable onto the stack
779            ffi::lua_pushvalue(raw_lua, this_index);
780            // move indexable into registry
781            let table_ref = ffi::luaL_ref(raw_lua, ffi::LUA_REGISTRYINDEX);
782
783            raw_lua
784                .pcall(|l| {
785                    let raw_lua = l.as_lua();
786                    // push indexable
787                    ffi::lua_rawgeti(raw_lua, ffi::LUA_REGISTRYINDEX, table_ref);
788                    // push index
789                    ffi::lua_rawgeti(raw_lua, ffi::LUA_REGISTRYINDEX, index_ref);
790                    // push value
791                    ffi::lua_rawgeti(raw_lua, ffi::LUA_REGISTRYINDEX, value_ref);
792                    // pop index, push value
793                    ffi::lua_settable(raw_lua, -3);
794                    // stack is temporary so indexable is discarded after return
795                })
796                .map_err(Err)?;
797
798            // unref temporaries
799            ffi::luaL_unref(raw_lua, ffi::LUA_REGISTRYINDEX, value_ref);
800            ffi::luaL_unref(raw_lua, ffi::LUA_REGISTRYINDEX, index_ref);
801            ffi::luaL_unref(raw_lua, ffi::LUA_REGISTRYINDEX, table_ref);
802        }
803        Ok(())
804    }
805
806    ////////////////////////////////////////////////////////////////////////////
807    // call
808    ////////////////////////////////////////////////////////////////////////////
809
810    #[track_caller]
811    #[inline]
812    pub(super) fn call<T, A, R>(
813        this: T,
814        index: AbsoluteIndex,
815        args: A,
816    ) -> Result<R, CallError<A::Err>>
817    where
818        T: AsLua,
819        A: PushInto<LuaState>,
820        R: LuaRead<PushGuard<T>>,
821    {
822        let raw_lua = this.as_lua();
823        // calling pcall pops the parameters and pushes output
824        let (pcall_return_value, pushed_value) = unsafe {
825            let old_top = ffi::lua_gettop(raw_lua);
826            // lua_pcall pops the function, so we have to make a copy of it
827            ffi::lua_pushvalue(raw_lua, index.into());
828            let num_pushed = match this.as_lua().try_push(args) {
829                Ok(g) => g.forget_internal(),
830                Err((err, _)) => return Err(CallError::PushError(err)),
831            };
832            let pcall_return_value = ffi::lua_pcall(raw_lua, num_pushed, ffi::LUA_MULTRET, 0);
833            let n_results = ffi::lua_gettop(raw_lua) - old_top;
834            (pcall_return_value, PushGuard::new(this, n_results))
835        };
836
837        match pcall_return_value {
838            ffi::LUA_ERRMEM => panic!("lua_pcall returned LUA_ERRMEM"),
839            ffi::LUA_ERRRUN => {
840                let error_msg = ToString::lua_read(pushed_value)
841                    .ok()
842                    .expect("can't find error message at the top of the Lua stack");
843                return Err(LuaError::ExecutionError(error_msg.into()).into());
844            }
845            0 => {}
846            _ => panic!(
847                "Unknown error code returned by lua_pcall: {}",
848                pcall_return_value
849            ),
850        }
851
852        let n_results = pushed_value.size;
853        LuaRead::lua_read_at_maybe_zero_position(pushed_value, -n_results).map_err(|(lua, e)| {
854            WrongType::info("reading value(s) returned by Lua")
855                .expected_type::<R>()
856                .actual_multiple_lua(lua, n_results)
857                .subtype(e)
858                .into()
859        })
860    }
861
862    ////////////////////////////////////////////////////////////////////////////
863    // checks
864    ////////////////////////////////////////////////////////////////////////////
865
866    #[inline(always)]
867    pub(super) fn is_callable(lua: impl AsLua, index: NonZeroI32) -> bool {
868        let raw_lua = lua.as_lua();
869        let i = index.into();
870        unsafe {
871            // luaL_iscallable doesn't work for `ffi`
872            ffi::lua_isfunction(raw_lua, i) || ffi::luaL_hasmetafield(raw_lua, i, c_ptr!("__call"))
873        }
874    }
875
876    #[inline(always)]
877    pub(super) fn is_indexable(lua: impl AsLua, index: NonZeroI32) -> bool {
878        let raw_lua = lua.as_lua();
879        let i = index.into();
880        unsafe {
881            ffi::lua_istable(raw_lua, i) || ffi::luaL_hasmetafield(raw_lua, i, c_ptr!("__index"))
882        }
883    }
884
885    #[inline(always)]
886    pub(super) fn is_rw_indexable(lua: impl AsLua, index: NonZeroI32) -> bool {
887        let raw_lua = lua.as_lua();
888        let i = index.into();
889        unsafe {
890            ffi::lua_istable(raw_lua, i)
891                || ffi::luaL_hasmetafield(raw_lua, i, c_ptr!("__index"))
892                    && ffi::luaL_hasmetafield(raw_lua, i, c_ptr!("__newindex"))
893        }
894    }
895}
896
897////////////////////////////////////////////////////////////////////////////////
898// impl_object
899////////////////////////////////////////////////////////////////////////////////
900
901#[macro_export]
902macro_rules! impl_object {
903    (
904        $this:ident,
905        check($lua:ident, $index:ident) { $($check:tt)* }
906        $( impl $trait:ident, )*
907    ) => {
908        impl<L> $crate::object::FromObject<L> for $this<L>
909        where
910            L: $crate::AsLua,
911        {
912            /// # Safety
913            /// `index` must correspond to a valid value in `lua`
914            #[inline(always)]
915            unsafe fn check($lua: impl $crate::AsLua, $index: ::std::num::NonZeroI32) -> bool {
916                $($check)*
917            }
918
919            /// # Safety
920            /// `inner` must satisfy the neccessary invariants of `Self`. See
921            /// [`check`]
922            #[inline(always)]
923            unsafe fn from_obj(inner: $crate::object::Object<L>) -> Self {
924                Self { inner }
925            }
926        }
927
928        impl<L> $crate::AsLua for $this<L>
929        where
930            L: $crate::AsLua,
931        {
932            #[inline(always)]
933            fn as_lua(&self) -> $crate::LuaState {
934                self.inner.as_lua()
935            }
936        }
937
938        impl<L> ::std::convert::AsRef<$crate::object::Object<L>> for $this<L>
939        where
940            L: $crate::AsLua,
941        {
942            #[inline(always)]
943            fn as_ref(&self) -> &$crate::object::Object<L> {
944                &self.inner
945            }
946        }
947
948        impl<L> ::std::convert::From<$this<L>> for $crate::object::Object<L>
949        where
950            L: $crate::AsLua,
951        {
952            #[inline(always)]
953            fn from(o: $this<L>) -> Self {
954                o.inner
955            }
956        }
957
958        impl<L> ::std::convert::TryFrom<$crate::object::Object<L>> for $this<L>
959        where
960            L: $crate::AsLua,
961        {
962            type Error = $crate::object::Object<L>;
963
964            #[inline(always)]
965            fn try_from(o: $crate::object::Object<L>) -> ::std::result::Result<Self, Self::Error> {
966                Self::try_from_obj(o)
967            }
968        }
969
970        $(
971            impl<L> $trait<L> for $this<L>
972            where
973                L: $crate::AsLua,
974            {}
975        )*
976
977        impl<L> $crate::LuaRead<L> for $this<L>
978        where
979            L: $crate::AsLua,
980        {
981            #[inline(always)]
982            fn lua_read_at_position(
983                lua: L,
984                index: ::std::num::NonZeroI32,
985            ) -> $crate::ReadResult<Self, L> {
986                ::std::convert::TryFrom::try_from($crate::object::Object::new(lua, index))
987                    .map_err(|l| {
988                        let g = $crate::object::Object::into_guard(l);
989                        let e = $crate::WrongType::info("reading lua value from stack")
990                            .expected_type::<Self>()
991                            .actual_single_lua(&g, index);
992                        (g, e)
993                    })
994            }
995        }
996
997        impl<L, T> $crate::Push<L> for $this<T>
998        where
999            L: $crate::AsLua,
1000            T: $crate::AsLua,
1001        {
1002            type Err = $crate::Void;
1003
1004            #[inline(always)]
1005            fn push_to_lua(&self, lua: L) -> $crate::PushResult<L, Self> {
1006                unsafe {
1007                    $crate::ffi::lua_pushvalue(lua.as_lua(), self.as_ref().index().into());
1008                    Ok(PushGuard::new(lua, 1))
1009                }
1010            }
1011        }
1012
1013        impl<L, T> $crate::PushOne<L> for $this<T>
1014        where
1015            L: $crate::AsLua,
1016            T: $crate::AsLua,
1017        {}
1018    }
1019}