factorio_mlua/
table.rs

1use std::marker::PhantomData;
2
3#[cfg(feature = "serialize")]
4use {
5    rustc_hash::FxHashSet,
6    serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
7    std::{cell::RefCell, os::raw::c_void, result::Result as StdResult},
8};
9
10use crate::error::{Error, Result};
11use crate::ffi;
12use crate::function::Function;
13use crate::types::{Integer, LuaRef};
14use crate::util::{assert_stack, check_stack, StackGuard};
15use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
16
17#[cfg(feature = "async")]
18use {futures_core::future::LocalBoxFuture, futures_util::future};
19
20/// Handle to an internal Lua table.
21#[derive(Clone, Debug)]
22pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
23
24#[allow(clippy::len_without_is_empty)]
25impl<'lua> Table<'lua> {
26    /// Sets a key-value pair in the table.
27    ///
28    /// If the value is `nil`, this will effectively remove the pair.
29    ///
30    /// This might invoke the `__newindex` metamethod. Use the [`raw_set`] method if that is not
31    /// desired.
32    ///
33    /// # Examples
34    ///
35    /// Export a value as a global to make it usable from Lua:
36    ///
37    /// ```
38    /// # use mlua::{Lua, Result};
39    /// # fn main() -> Result<()> {
40    /// # let lua = Lua::new();
41    /// let globals = lua.globals();
42    ///
43    /// globals.set("assertions", cfg!(debug_assertions))?;
44    ///
45    /// lua.load(r#"
46    ///     if assertions == true then
47    ///         -- ...
48    ///     elseif assertions == false then
49    ///         -- ...
50    ///     else
51    ///         error("assertions neither on nor off?")
52    ///     end
53    /// "#).exec()?;
54    /// # Ok(())
55    /// # }
56    /// ```
57    ///
58    /// [`raw_set`]: #method.raw_set
59    pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
60        let lua = self.0.lua;
61        let key = key.to_lua(lua)?;
62        let value = value.to_lua(lua)?;
63
64        unsafe {
65            let _sg = StackGuard::new(lua.state);
66            check_stack(lua.state, 5)?;
67
68            lua.push_ref(&self.0);
69            lua.push_value(key)?;
70            lua.push_value(value)?;
71            protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
72        }
73    }
74
75    /// Gets the value associated to `key` from the table.
76    ///
77    /// If no value is associated to `key`, returns the `nil` value.
78    ///
79    /// This might invoke the `__index` metamethod. Use the [`raw_get`] method if that is not
80    /// desired.
81    ///
82    /// # Examples
83    ///
84    /// Query the version of the Lua interpreter:
85    ///
86    /// ```
87    /// # use mlua::{Lua, Result};
88    /// # fn main() -> Result<()> {
89    /// # let lua = Lua::new();
90    /// let globals = lua.globals();
91    ///
92    /// let version: String = globals.get("_VERSION")?;
93    /// println!("Lua version: {}", version);
94    /// # Ok(())
95    /// # }
96    /// ```
97    ///
98    /// [`raw_get`]: #method.raw_get
99    pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
100        let lua = self.0.lua;
101        let key = key.to_lua(lua)?;
102
103        let value = unsafe {
104            let _sg = StackGuard::new(lua.state);
105            check_stack(lua.state, 4)?;
106
107            lua.push_ref(&self.0);
108            lua.push_value(key)?;
109            protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
110
111            lua.pop_value()
112        };
113        V::from_lua(value, lua)
114    }
115
116    /// Checks whether the table contains a non-nil value for `key`.
117    pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
118        let lua = self.0.lua;
119        let key = key.to_lua(lua)?;
120
121        unsafe {
122            let _sg = StackGuard::new(lua.state);
123            check_stack(lua.state, 4)?;
124
125            lua.push_ref(&self.0);
126            lua.push_value(key)?;
127            protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
128            Ok(ffi::lua_isnil(lua.state, -1) == 0)
129        }
130    }
131
132    /// Compares two tables for equality.
133    ///
134    /// Tables are compared by reference first.
135    /// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
136    /// mlua will check `self` first for the metamethod, then `other` if not found.
137    ///
138    /// # Examples
139    ///
140    /// Compare two tables using `__eq` metamethod:
141    ///
142    /// ```
143    /// # use mlua::{Lua, Result, Table};
144    /// # fn main() -> Result<()> {
145    /// # let lua = Lua::new();
146    /// let table1 = lua.create_table()?;
147    /// table1.set(1, "value")?;
148    ///
149    /// let table2 = lua.create_table()?;
150    /// table2.set(2, "value")?;
151    ///
152    /// let always_equals_mt = lua.create_table()?;
153    /// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
154    /// table2.set_metatable(Some(always_equals_mt));
155    ///
156    /// assert!(table1.equals(&table1.clone())?);
157    /// assert!(table1.equals(&table2)?);
158    /// # Ok(())
159    /// # }
160    /// ```
161    pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
162        let other = other.as_ref();
163        if self == other {
164            return Ok(true);
165        }
166
167        // Compare using __eq metamethod if exists
168        // First, check the self for the metamethod.
169        // If self does not define it, then check the other table.
170        if let Some(mt) = self.get_metatable() {
171            if mt.contains_key("__eq")? {
172                return mt
173                    .get::<_, Function>("__eq")?
174                    .call((self.clone(), other.clone()));
175            }
176        }
177        if let Some(mt) = other.get_metatable() {
178            if mt.contains_key("__eq")? {
179                return mt
180                    .get::<_, Function>("__eq")?
181                    .call((self.clone(), other.clone()));
182            }
183        }
184
185        Ok(false)
186    }
187
188    /// Sets a key-value pair without invoking metamethods.
189    pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
190        let lua = self.0.lua;
191        let key = key.to_lua(lua)?;
192        let value = value.to_lua(lua)?;
193
194        unsafe {
195            let _sg = StackGuard::new(lua.state);
196            check_stack(lua.state, 5)?;
197
198            lua.push_ref(&self.0);
199            lua.push_value(key)?;
200            lua.push_value(value)?;
201            if lua.unlikely_memory_error() {
202                ffi::lua_rawset(lua.state, -3);
203                ffi::lua_pop(lua.state, 1);
204                Ok(())
205            } else {
206                protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
207            }
208        }
209    }
210
211    /// Gets the value associated to `key` without invoking metamethods.
212    pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
213        let lua = self.0.lua;
214        let key = key.to_lua(lua)?;
215
216        let value = unsafe {
217            let _sg = StackGuard::new(lua.state);
218            check_stack(lua.state, 3)?;
219
220            lua.push_ref(&self.0);
221            lua.push_value(key)?;
222            ffi::lua_rawget(lua.state, -2);
223
224            lua.pop_value()
225        };
226        V::from_lua(value, lua)
227    }
228
229    /// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
230    /// The worst case complexity is O(n), where n is the table length.
231    pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
232        let lua = self.0.lua;
233        let size = self.raw_len();
234        if idx < 1 || idx > size + 1 {
235            return Err(Error::RuntimeError("index out of bounds".to_string()));
236        }
237
238        let value = value.to_lua(lua)?;
239        unsafe {
240            let _sg = StackGuard::new(lua.state);
241            check_stack(lua.state, 5)?;
242
243            lua.push_ref(&self.0);
244            lua.push_value(value)?;
245            protect_lua!(lua.state, 2, 0, |state| {
246                for i in (idx..=size).rev() {
247                    // table[i+1] = table[i]
248                    ffi::lua_rawgeti(state, -2, i);
249                    ffi::lua_rawseti(state, -3, i + 1);
250                }
251                ffi::lua_rawseti(state, -2, idx)
252            })
253        }
254    }
255
256    /// Removes a key from the table.
257    ///
258    /// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
259    /// and erases element `table[key]`. The complexity is O(n) in the worst case,
260    /// where n is the table length.
261    ///
262    /// For other key types this is equivalent to setting `table[key] = nil`.
263    pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
264        let lua = self.0.lua;
265        let key = key.to_lua(lua)?;
266        match key {
267            Value::Integer(idx) => {
268                let size = self.raw_len();
269                if idx < 1 || idx > size {
270                    return Err(Error::RuntimeError("index out of bounds".to_string()));
271                }
272                unsafe {
273                    let _sg = StackGuard::new(lua.state);
274                    check_stack(lua.state, 4)?;
275
276                    lua.push_ref(&self.0);
277                    protect_lua!(lua.state, 1, 0, |state| {
278                        for i in idx..size {
279                            ffi::lua_rawgeti(state, -1, i + 1);
280                            ffi::lua_rawseti(state, -2, i);
281                        }
282                        ffi::lua_pushnil(state);
283                        ffi::lua_rawseti(state, -2, size);
284                    })
285                }
286            }
287            _ => self.raw_set(key, Nil),
288        }
289    }
290
291    /// Returns the result of the Lua `#` operator.
292    ///
293    /// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
294    ///
295    /// [`raw_len`]: #method.raw_len
296    pub fn len(&self) -> Result<Integer> {
297        let lua = self.0.lua;
298        unsafe {
299            let _sg = StackGuard::new(lua.state);
300            check_stack(lua.state, 4)?;
301
302            lua.push_ref(&self.0);
303            protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
304        }
305    }
306
307    /// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
308    pub fn raw_len(&self) -> Integer {
309        let lua = self.0.lua;
310        unsafe {
311            let _sg = StackGuard::new(lua.state);
312            assert_stack(lua.state, 1);
313
314            lua.push_ref(&self.0);
315            ffi::lua_rawlen(lua.state, -1) as Integer
316        }
317    }
318
319    /// Returns a reference to the metatable of this table, or `None` if no metatable is set.
320    ///
321    /// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
322    pub fn get_metatable(&self) -> Option<Table<'lua>> {
323        let lua = self.0.lua;
324        unsafe {
325            let _sg = StackGuard::new(lua.state);
326            assert_stack(lua.state, 2);
327
328            lua.push_ref(&self.0);
329            if ffi::lua_getmetatable(lua.state, -1) == 0 {
330                None
331            } else {
332                Some(Table(lua.pop_ref()))
333            }
334        }
335    }
336
337    /// Sets or removes the metatable of this table.
338    ///
339    /// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
340    /// nothing).
341    pub fn set_metatable(&self, metatable: Option<Table<'lua>>) {
342        let lua = self.0.lua;
343        unsafe {
344            let _sg = StackGuard::new(lua.state);
345            assert_stack(lua.state, 2);
346
347            lua.push_ref(&self.0);
348            if let Some(metatable) = metatable {
349                lua.push_ref(&metatable.0);
350            } else {
351                ffi::lua_pushnil(lua.state);
352            }
353            ffi::lua_setmetatable(lua.state, -2);
354        }
355    }
356
357    /// Sets `readonly` attribute on the table.
358    ///
359    /// Requires `feature = "luau"`
360    #[cfg(any(feature = "luau", doc))]
361    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
362    pub fn set_readonly(&self, enabled: bool) {
363        let lua = self.0.lua;
364        unsafe {
365            lua.ref_thread_exec(|refthr| {
366                ffi::lua_setreadonly(refthr, self.0.index, enabled as _);
367                if !enabled {
368                    // Reset "safeenv" flag
369                    ffi::lua_setsafeenv(refthr, self.0.index, 0);
370                }
371            });
372        }
373    }
374
375    /// Returns `readonly` attribute of the table.
376    ///
377    /// Requires `feature = "luau"`
378    #[cfg(any(feature = "luau", doc))]
379    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
380    pub fn is_readonly(&self) -> bool {
381        let lua = self.0.lua;
382        unsafe { lua.ref_thread_exec(|refthr| ffi::lua_getreadonly(refthr, self.0.index) != 0) }
383    }
384
385    /// Consume this table and return an iterator over the pairs of the table.
386    ///
387    /// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
388    ///
389    /// The pairs are wrapped in a [`Result`], since they are lazily converted to `K` and `V` types.
390    ///
391    /// # Note
392    ///
393    /// While this method consumes the `Table` object, it can not prevent code from mutating the
394    /// table while the iteration is in progress. Refer to the [Lua manual] for information about
395    /// the consequences of such mutation.
396    ///
397    /// # Examples
398    ///
399    /// Iterate over all globals:
400    ///
401    /// ```
402    /// # use mlua::{Lua, Result, Value};
403    /// # fn main() -> Result<()> {
404    /// # let lua = Lua::new();
405    /// let globals = lua.globals();
406    ///
407    /// for pair in globals.pairs::<Value, Value>() {
408    ///     let (key, value) = pair?;
409    /// #   let _ = (key, value);   // used
410    ///     // ...
411    /// }
412    /// # Ok(())
413    /// # }
414    /// ```
415    ///
416    /// [`Result`]: crate::Result
417    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
418    pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
419        TablePairs {
420            table: self.0,
421            key: Some(Nil),
422            _phantom: PhantomData,
423        }
424    }
425
426    /// Consume this table and return an iterator over all values in the sequence part of the table.
427    ///
428    /// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
429    /// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
430    /// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
431    /// metatable will not be called.
432    ///
433    /// Just like [`pairs`], the values are wrapped in a [`Result`].
434    ///
435    /// # Note
436    ///
437    /// While this method consumes the `Table` object, it can not prevent code from mutating the
438    /// table while the iteration is in progress. Refer to the [Lua manual] for information about
439    /// the consequences of such mutation.
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// # use mlua::{Lua, Result, Table};
445    /// # fn main() -> Result<()> {
446    /// # let lua = Lua::new();
447    /// let my_table: Table = lua.load(r#"
448    ///     {
449    ///         [1] = 4,
450    ///         [2] = 5,
451    ///         [4] = 7,
452    ///         key = 2
453    ///     }
454    /// "#).eval()?;
455    ///
456    /// let expected = [4, 5];
457    /// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
458    ///     assert_eq!(expected, got?);
459    /// }
460    /// # Ok(())
461    /// # }
462    /// ```
463    ///
464    /// [`pairs`]: #method.pairs
465    /// [`Result`]: crate::Result
466    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
467    pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
468        TableSequence {
469            table: self.0,
470            index: Some(1),
471            len: None,
472            raw: false,
473            _phantom: PhantomData,
474        }
475    }
476
477    /// Consume this table and return an iterator over all values in the sequence part of the table.
478    ///
479    /// Unlike the `sequence_values`, does not invoke `__index` metamethod when iterating.
480    ///
481    /// [`sequence_values`]: #method.sequence_values
482    pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
483        TableSequence {
484            table: self.0,
485            index: Some(1),
486            len: None,
487            raw: true,
488            _phantom: PhantomData,
489        }
490    }
491
492    /// C++ side implementation of table size calculating function.
493    /// For more info on function see <https://lua-api.factorio.com/latest/Libraries.html>,
494    /// `table_size()` section.
495    /// Factorio uses this with `fuzzy` set to false
496    #[cfg(any(feature = "lua-factorio", doc))]
497    #[cfg_attr(docsrs, doc(cfg(feature = "lua-factorio")))]
498    pub fn table_size(&self, fuzzy: bool) -> Integer {
499        let lua = self.0.lua;
500        unsafe {
501            let _sg = StackGuard::new(lua.state);
502            assert_stack(lua.state, 1);
503
504            lua.push_ref(&self.0);
505            ffi::lua_tablesize(lua.state, -1, fuzzy as i32) as Integer
506        }
507    }
508
509    #[cfg(any(feature = "serialize"))]
510    pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
511        self,
512        len: Option<Integer>,
513    ) -> TableSequence<'lua, V> {
514        let len = len.unwrap_or_else(|| self.raw_len());
515        TableSequence {
516            table: self.0,
517            index: Some(1),
518            len: Some(len),
519            raw: true,
520            _phantom: PhantomData,
521        }
522    }
523
524    #[cfg(feature = "serialize")]
525    pub(crate) fn is_array(&self) -> bool {
526        let lua = self.0.lua;
527        unsafe {
528            let _sg = StackGuard::new(lua.state);
529            assert_stack(lua.state, 3);
530
531            lua.push_ref(&self.0);
532            if ffi::lua_getmetatable(lua.state, -1) == 0 {
533                return false;
534            }
535            crate::serde::push_array_metatable(lua.state);
536            ffi::lua_rawequal(lua.state, -1, -2) != 0
537        }
538    }
539}
540
541impl<'lua> PartialEq for Table<'lua> {
542    fn eq(&self, other: &Self) -> bool {
543        self.0 == other.0
544    }
545}
546
547impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
548    #[inline]
549    fn as_ref(&self) -> &Self {
550        self
551    }
552}
553
554/// An extension trait for `Table`s that provides a variety of convenient functionality.
555pub trait TableExt<'lua> {
556    /// Calls the table as function assuming it has `__call` metamethod.
557    ///
558    /// The metamethod is called with the table as its first argument, followed by the passed arguments.
559    fn call<A, R>(&self, args: A) -> Result<R>
560    where
561        A: ToLuaMulti<'lua>,
562        R: FromLuaMulti<'lua>;
563
564    /// Asynchronously calls the table as function assuming it has `__call` metamethod.
565    ///
566    /// The metamethod is called with the table as its first argument, followed by the passed arguments.
567    #[cfg(feature = "async")]
568    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
569    fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
570    where
571        'lua: 'fut,
572        A: ToLuaMulti<'lua>,
573        R: FromLuaMulti<'lua> + 'fut;
574
575    /// Gets the function associated to `key` from the table and executes it,
576    /// passing the table itself along with `args` as function arguments.
577    ///
578    /// This is a shortcut for
579    /// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
580    ///
581    /// This might invoke the `__index` metamethod.
582    fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
583    where
584        K: ToLua<'lua>,
585        A: ToLuaMulti<'lua>,
586        R: FromLuaMulti<'lua>;
587
588    /// Gets the function associated to `key` from the table and executes it,
589    /// passing `args` as function arguments.
590    ///
591    /// This is a shortcut for
592    /// `table.get::<_, Function>(key)?.call(args)`
593    ///
594    /// This might invoke the `__index` metamethod.
595    fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
596    where
597        K: ToLua<'lua>,
598        A: ToLuaMulti<'lua>,
599        R: FromLuaMulti<'lua>;
600
601    /// Gets the function associated to `key` from the table and asynchronously executes it,
602    /// passing the table itself along with `args` as function arguments and returning Future.
603    ///
604    /// Requires `feature = "async"`
605    ///
606    /// This might invoke the `__index` metamethod.
607    #[cfg(feature = "async")]
608    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
609    fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
610    where
611        'lua: 'fut,
612        K: ToLua<'lua>,
613        A: ToLuaMulti<'lua>,
614        R: FromLuaMulti<'lua> + 'fut;
615
616    /// Gets the function associated to `key` from the table and asynchronously executes it,
617    /// passing `args` as function arguments and returning Future.
618    ///
619    /// Requires `feature = "async"`
620    ///
621    /// This might invoke the `__index` metamethod.
622    #[cfg(feature = "async")]
623    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
624    fn call_async_function<'fut, K, A, R>(
625        &self,
626        key: K,
627        args: A,
628    ) -> LocalBoxFuture<'fut, Result<R>>
629    where
630        'lua: 'fut,
631        K: ToLua<'lua>,
632        A: ToLuaMulti<'lua>,
633        R: FromLuaMulti<'lua> + 'fut;
634}
635
636impl<'lua> TableExt<'lua> for Table<'lua> {
637    fn call<A, R>(&self, args: A) -> Result<R>
638    where
639        A: ToLuaMulti<'lua>,
640        R: FromLuaMulti<'lua>,
641    {
642        // Convert table to a function and call via pcall that respects the `__call` metamethod.
643        Function(self.0.clone()).call(args)
644    }
645
646    #[cfg(feature = "async")]
647    fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
648    where
649        'lua: 'fut,
650        A: ToLuaMulti<'lua>,
651        R: FromLuaMulti<'lua> + 'fut,
652    {
653        Function(self.0.clone()).call_async(args)
654    }
655
656    fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
657    where
658        K: ToLua<'lua>,
659        A: ToLuaMulti<'lua>,
660        R: FromLuaMulti<'lua>,
661    {
662        let lua = self.0.lua;
663        let mut args = args.to_lua_multi(lua)?;
664        args.push_front(Value::Table(self.clone()));
665        self.get::<_, Function>(key)?.call(args)
666    }
667
668    fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
669    where
670        K: ToLua<'lua>,
671        A: ToLuaMulti<'lua>,
672        R: FromLuaMulti<'lua>,
673    {
674        self.get::<_, Function>(key)?.call(args)
675    }
676
677    #[cfg(feature = "async")]
678    fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
679    where
680        'lua: 'fut,
681        K: ToLua<'lua>,
682        A: ToLuaMulti<'lua>,
683        R: FromLuaMulti<'lua> + 'fut,
684    {
685        let lua = self.0.lua;
686        let mut args = match args.to_lua_multi(lua) {
687            Ok(args) => args,
688            Err(e) => return Box::pin(future::err(e)),
689        };
690        args.push_front(Value::Table(self.clone()));
691        self.call_async_function(key, args)
692    }
693
694    #[cfg(feature = "async")]
695    fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
696    where
697        'lua: 'fut,
698        K: ToLua<'lua>,
699        A: ToLuaMulti<'lua>,
700        R: FromLuaMulti<'lua> + 'fut,
701    {
702        match self.get::<_, Function>(key) {
703            Ok(func) => func.call_async(args),
704            Err(e) => Box::pin(future::err(e)),
705        }
706    }
707}
708
709#[cfg(feature = "serialize")]
710impl<'lua> Serialize for Table<'lua> {
711    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
712    where
713        S: Serializer,
714    {
715        thread_local! {
716            static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
717        }
718
719        let lua = self.0.lua;
720        let ptr = unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) };
721        let res = VISITED.with(|visited| {
722            {
723                let mut visited = visited.borrow_mut();
724                if visited.contains(&ptr) {
725                    return Err(ser::Error::custom("recursive table detected"));
726                }
727                visited.insert(ptr);
728            }
729
730            let len = self.raw_len() as usize;
731            if len > 0 || self.is_array() {
732                let mut seq = serializer.serialize_seq(Some(len))?;
733                for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
734                    let v = v.map_err(serde::ser::Error::custom)?;
735                    seq.serialize_element(&v)?;
736                }
737                return seq.end();
738            }
739
740            let mut map = serializer.serialize_map(None)?;
741            for kv in self.clone().pairs::<Value, Value>() {
742                let (k, v) = kv.map_err(serde::ser::Error::custom)?;
743                map.serialize_entry(&k, &v)?;
744            }
745            map.end()
746        });
747        VISITED.with(|visited| {
748            visited.borrow_mut().remove(&ptr);
749        });
750        res
751    }
752}
753
754/// An iterator over the pairs of a Lua table.
755///
756/// This struct is created by the [`Table::pairs`] method.
757///
758/// [`Table::pairs`]: crate::Table::pairs
759pub struct TablePairs<'lua, K, V> {
760    table: LuaRef<'lua>,
761    key: Option<Value<'lua>>,
762    _phantom: PhantomData<(K, V)>,
763}
764
765impl<'lua, K, V> Iterator for TablePairs<'lua, K, V>
766where
767    K: FromLua<'lua>,
768    V: FromLua<'lua>,
769{
770    type Item = Result<(K, V)>;
771
772    fn next(&mut self) -> Option<Self::Item> {
773        if let Some(prev_key) = self.key.take() {
774            let lua = self.table.lua;
775
776            let res = (|| unsafe {
777                let _sg = StackGuard::new(lua.state);
778                check_stack(lua.state, 5)?;
779
780                lua.push_ref(&self.table);
781                lua.push_value(prev_key)?;
782
783                let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
784                    ffi::lua_next(state, -2)
785                })?;
786                if next != 0 {
787                    let value = lua.pop_value();
788                    let key = lua.pop_value();
789                    Ok(Some((
790                        key.clone(),
791                        K::from_lua(key, lua)?,
792                        V::from_lua(value, lua)?,
793                    )))
794                } else {
795                    Ok(None)
796                }
797            })();
798
799            match res {
800                Ok(Some((key, ret_key, value))) => {
801                    self.key = Some(key);
802                    Some(Ok((ret_key, value)))
803                }
804                Ok(None) => None,
805                Err(e) => Some(Err(e)),
806            }
807        } else {
808            None
809        }
810    }
811}
812
813/// An iterator over the sequence part of a Lua table.
814///
815/// This struct is created by the [`Table::sequence_values`] method.
816///
817/// [`Table::sequence_values`]: crate::Table::sequence_values
818pub struct TableSequence<'lua, V> {
819    table: LuaRef<'lua>,
820    index: Option<Integer>,
821    len: Option<Integer>,
822    raw: bool,
823    _phantom: PhantomData<V>,
824}
825
826impl<'lua, V> Iterator for TableSequence<'lua, V>
827where
828    V: FromLua<'lua>,
829{
830    type Item = Result<V>;
831
832    fn next(&mut self) -> Option<Self::Item> {
833        if let Some(index) = self.index.take() {
834            let lua = self.table.lua;
835
836            let res = (|| unsafe {
837                let _sg = StackGuard::new(lua.state);
838                check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?;
839
840                lua.push_ref(&self.table);
841                let res = if self.raw {
842                    ffi::lua_rawgeti(lua.state, -1, index)
843                } else {
844                    protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
845                };
846                match res {
847                    ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
848                    _ => Ok(Some((index, lua.pop_value()))),
849                }
850            })();
851
852            match res {
853                Ok(Some((index, r))) => {
854                    self.index = Some(index + 1);
855                    Some(V::from_lua(r, lua))
856                }
857                Ok(None) => None,
858                Err(err) => Some(Err(err)),
859            }
860        } else {
861            None
862        }
863    }
864}