mlua/
table.rs

1use std::collections::HashSet;
2use std::fmt;
3use std::marker::PhantomData;
4use std::os::raw::c_void;
5
6#[cfg(feature = "serialize")]
7use {
8    rustc_hash::FxHashSet,
9    serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
10    std::{cell::RefCell, rc::Rc, result::Result as StdResult},
11};
12
13use crate::error::{Error, Result};
14use crate::function::Function;
15use crate::private::Sealed;
16use crate::types::{Integer, LuaRef};
17use crate::util::{assert_stack, check_stack, StackGuard};
18use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
19
20#[cfg(feature = "async")]
21use futures_util::future::{self, LocalBoxFuture};
22
23/// Handle to an internal Lua table.
24#[derive(Clone)]
25pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
26
27/// Owned handle to an internal Lua table.
28///
29/// The owned handle holds a *strong* reference to the current Lua instance.
30/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
31/// to accidentally cause reference cycles that would prevent destroying Lua instance.
32///
33/// [`UserData`]: crate::UserData
34#[cfg(feature = "unstable")]
35#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
36#[derive(Clone, Debug)]
37pub struct OwnedTable(pub(crate) crate::types::LuaOwnedRef);
38
39#[cfg(feature = "unstable")]
40impl OwnedTable {
41    /// Get borrowed handle to the underlying Lua table.
42    #[cfg_attr(feature = "send", allow(unused))]
43    pub const fn to_ref(&self) -> Table {
44        Table(self.0.to_ref())
45    }
46}
47
48impl<'lua> Table<'lua> {
49    /// Sets a key-value pair in the table.
50    ///
51    /// If the value is `nil`, this will effectively remove the pair.
52    ///
53    /// This might invoke the `__newindex` metamethod. Use the [`raw_set`] method if that is not
54    /// desired.
55    ///
56    /// # Examples
57    ///
58    /// Export a value as a global to make it usable from Lua:
59    ///
60    /// ```
61    /// # use mlua::{Lua, Result};
62    /// # fn main() -> Result<()> {
63    /// # let lua = Lua::new();
64    /// let globals = lua.globals();
65    ///
66    /// globals.set("assertions", cfg!(debug_assertions))?;
67    ///
68    /// lua.load(r#"
69    ///     if assertions == true then
70    ///         -- ...
71    ///     elseif assertions == false then
72    ///         -- ...
73    ///     else
74    ///         error("assertions neither on nor off?")
75    ///     end
76    /// "#).exec()?;
77    /// # Ok(())
78    /// # }
79    /// ```
80    ///
81    /// [`raw_set`]: #method.raw_set
82    pub fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
83        // Fast track
84        if !self.has_metatable() {
85            return self.raw_set(key, value);
86        }
87
88        let lua = self.0.lua;
89        let state = lua.state();
90        unsafe {
91            let _sg = StackGuard::new(state);
92            check_stack(state, 5)?;
93
94            lua.push_ref(&self.0);
95            key.push_into_stack(lua)?;
96            value.push_into_stack(lua)?;
97            protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
98        }
99    }
100
101    /// Gets the value associated to `key` from the table.
102    ///
103    /// If no value is associated to `key`, returns the `nil` value.
104    ///
105    /// This might invoke the `__index` metamethod. Use the [`raw_get`] method if that is not
106    /// desired.
107    ///
108    /// # Examples
109    ///
110    /// Query the version of the Lua interpreter:
111    ///
112    /// ```
113    /// # use mlua::{Lua, Result};
114    /// # fn main() -> Result<()> {
115    /// # let lua = Lua::new();
116    /// let globals = lua.globals();
117    ///
118    /// let version: String = globals.get("_VERSION")?;
119    /// println!("Lua version: {}", version);
120    /// # Ok(())
121    /// # }
122    /// ```
123    ///
124    /// [`raw_get`]: #method.raw_get
125    pub fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
126        // Fast track
127        if !self.has_metatable() {
128            return self.raw_get(key);
129        }
130
131        let lua = self.0.lua;
132        let state = lua.state();
133        unsafe {
134            let _sg = StackGuard::new(state);
135            check_stack(state, 4)?;
136
137            lua.push_ref(&self.0);
138            key.push_into_stack(lua)?;
139            protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
140
141            V::from_stack(-1, lua)
142        }
143    }
144
145    /// Checks whether the table contains a non-nil value for `key`.
146    ///
147    /// This might invoke the `__index` metamethod.
148    pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
149        Ok(self.get::<_, Value>(key)? != Value::Nil)
150    }
151
152    /// Appends a value to the back of the table.
153    ///
154    /// This might invoke the `__len` and `__newindex` metamethods.
155    pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
156        // Fast track
157        if !self.has_metatable() {
158            return self.raw_push(value);
159        }
160
161        let lua = self.0.lua;
162        let state = lua.state();
163        unsafe {
164            let _sg = StackGuard::new(state);
165            check_stack(state, 4)?;
166
167            lua.push_ref(&self.0);
168            value.push_into_stack(lua)?;
169            protect_lua!(state, 2, 0, fn(state) {
170                let len = ffi::luaL_len(state, -2) as Integer;
171                ffi::lua_seti(state, -2, len + 1);
172            })?
173        }
174        Ok(())
175    }
176
177    /// Removes the last element from the table and returns it.
178    ///
179    /// This might invoke the `__len` and `__newindex` metamethods.
180    pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
181        // Fast track
182        if !self.has_metatable() {
183            return self.raw_pop();
184        }
185
186        let lua = self.0.lua;
187        let state = lua.state();
188        unsafe {
189            let _sg = StackGuard::new(state);
190            check_stack(state, 4)?;
191
192            lua.push_ref(&self.0);
193            protect_lua!(state, 1, 1, fn(state) {
194                let len = ffi::luaL_len(state, -1) as Integer;
195                ffi::lua_geti(state, -1, len);
196                ffi::lua_pushnil(state);
197                ffi::lua_seti(state, -3, len);
198            })?;
199            V::from_stack(-1, lua)
200        }
201    }
202
203    /// Compares two tables for equality.
204    ///
205    /// Tables are compared by reference first.
206    /// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
207    /// mlua will check `self` first for the metamethod, then `other` if not found.
208    ///
209    /// # Examples
210    ///
211    /// Compare two tables using `__eq` metamethod:
212    ///
213    /// ```
214    /// # use mlua::{Lua, Result, Table};
215    /// # fn main() -> Result<()> {
216    /// # let lua = Lua::new();
217    /// let table1 = lua.create_table()?;
218    /// table1.set(1, "value")?;
219    ///
220    /// let table2 = lua.create_table()?;
221    /// table2.set(2, "value")?;
222    ///
223    /// let always_equals_mt = lua.create_table()?;
224    /// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
225    /// table2.set_metatable(Some(always_equals_mt));
226    ///
227    /// assert!(table1.equals(&table1.clone())?);
228    /// assert!(table1.equals(&table2)?);
229    /// # Ok(())
230    /// # }
231    /// ```
232    pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
233        let other = other.as_ref();
234        if self == other {
235            return Ok(true);
236        }
237
238        // Compare using __eq metamethod if exists
239        // First, check the self for the metamethod.
240        // If self does not define it, then check the other table.
241        if let Some(mt) = self.get_metatable() {
242            if mt.contains_key("__eq")? {
243                return mt
244                    .get::<_, Function>("__eq")?
245                    .call((self.clone(), other.clone()));
246            }
247        }
248        if let Some(mt) = other.get_metatable() {
249            if mt.contains_key("__eq")? {
250                return mt
251                    .get::<_, Function>("__eq")?
252                    .call((self.clone(), other.clone()));
253            }
254        }
255
256        Ok(false)
257    }
258
259    /// Sets a key-value pair without invoking metamethods.
260    pub fn raw_set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
261        #[cfg(feature = "luau")]
262        self.check_readonly_write()?;
263
264        let lua = self.0.lua;
265        let state = lua.state();
266        unsafe {
267            let _sg = StackGuard::new(state);
268            check_stack(state, 5)?;
269
270            lua.push_ref(&self.0);
271            key.push_into_stack(lua)?;
272            value.push_into_stack(lua)?;
273
274            if lua.unlikely_memory_error() {
275                ffi::lua_rawset(state, -3);
276                ffi::lua_pop(state, 1);
277                Ok(())
278            } else {
279                protect_lua!(state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
280            }
281        }
282    }
283
284    /// Gets the value associated to `key` without invoking metamethods.
285    pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
286        let lua = self.0.lua;
287        let state = lua.state();
288        unsafe {
289            let _sg = StackGuard::new(state);
290            check_stack(state, 3)?;
291
292            lua.push_ref(&self.0);
293            key.push_into_stack(lua)?;
294            ffi::lua_rawget(state, -2);
295
296            V::from_stack(-1, lua)
297        }
298    }
299
300    /// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
301    /// The worst case complexity is O(n), where n is the table length.
302    pub fn raw_insert<V: IntoLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
303        let size = self.raw_len() as Integer;
304        if idx < 1 || idx > size + 1 {
305            return Err(Error::runtime("index out of bounds"));
306        }
307
308        let lua = self.0.lua;
309        let state = lua.state();
310        unsafe {
311            let _sg = StackGuard::new(state);
312            check_stack(state, 5)?;
313
314            lua.push_ref(&self.0);
315            value.push_into_stack(lua)?;
316            protect_lua!(state, 2, 0, |state| {
317                for i in (idx..=size).rev() {
318                    // table[i+1] = table[i]
319                    ffi::lua_rawgeti(state, -2, i);
320                    ffi::lua_rawseti(state, -3, i + 1);
321                }
322                ffi::lua_rawseti(state, -2, idx)
323            })
324        }
325    }
326
327    /// Appends a value to the back of the table without invoking metamethods.
328    pub fn raw_push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
329        #[cfg(feature = "luau")]
330        self.check_readonly_write()?;
331
332        let lua = self.0.lua;
333        let state = lua.state();
334        unsafe {
335            let _sg = StackGuard::new(state);
336            check_stack(state, 4)?;
337
338            lua.push_ref(&self.0);
339            value.push_into_stack(lua)?;
340
341            unsafe fn callback(state: *mut ffi::lua_State) {
342                let len = ffi::lua_rawlen(state, -2) as Integer;
343                ffi::lua_rawseti(state, -2, len + 1);
344            }
345
346            if lua.unlikely_memory_error() {
347                callback(state);
348            } else {
349                protect_lua!(state, 2, 0, fn(state) callback(state))?;
350            }
351        }
352        Ok(())
353    }
354
355    /// Removes the last element from the table and returns it, without invoking metamethods.
356    pub fn raw_pop<V: FromLua<'lua>>(&self) -> Result<V> {
357        #[cfg(feature = "luau")]
358        self.check_readonly_write()?;
359
360        let lua = self.0.lua;
361        let state = lua.state();
362        unsafe {
363            let _sg = StackGuard::new(state);
364            check_stack(state, 3)?;
365
366            lua.push_ref(&self.0);
367            let len = ffi::lua_rawlen(state, -1) as Integer;
368            ffi::lua_rawgeti(state, -1, len);
369            // Set slot to nil (it must be safe to do)
370            ffi::lua_pushnil(state);
371            ffi::lua_rawseti(state, -3, len);
372
373            V::from_stack(-1, lua)
374        }
375    }
376
377    /// Removes a key from the table.
378    ///
379    /// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
380    /// and erases element `table[key]`. The complexity is O(n) in the worst case,
381    /// where n is the table length.
382    ///
383    /// For other key types this is equivalent to setting `table[key] = nil`.
384    pub fn raw_remove<K: IntoLua<'lua>>(&self, key: K) -> Result<()> {
385        let lua = self.0.lua;
386        let state = lua.state();
387        let key = key.into_lua(lua)?;
388        match key {
389            Value::Integer(idx) => {
390                let size = self.raw_len() as Integer;
391                if idx < 1 || idx > size {
392                    return Err(Error::runtime("index out of bounds"));
393                }
394                unsafe {
395                    let _sg = StackGuard::new(state);
396                    check_stack(state, 4)?;
397
398                    lua.push_ref(&self.0);
399                    protect_lua!(state, 1, 0, |state| {
400                        for i in idx..size {
401                            ffi::lua_rawgeti(state, -1, i + 1);
402                            ffi::lua_rawseti(state, -2, i);
403                        }
404                        ffi::lua_pushnil(state);
405                        ffi::lua_rawseti(state, -2, size);
406                    })
407                }
408            }
409            _ => self.raw_set(key, Nil),
410        }
411    }
412
413    /// Clears the table, removing all keys and values from array and hash parts,
414    /// without invoking metamethods.
415    ///
416    /// This method is useful to clear the table while keeping its capacity.
417    pub fn clear(&self) -> Result<()> {
418        #[cfg(feature = "luau")]
419        self.check_readonly_write()?;
420
421        let lua = self.0.lua;
422        unsafe {
423            #[cfg(feature = "luau")]
424            ffi::lua_cleartable(lua.ref_thread(), self.0.index);
425
426            #[cfg(not(feature = "luau"))]
427            {
428                let state = lua.state();
429                check_stack(state, 4)?;
430
431                lua.push_ref(&self.0);
432
433                // Clear array part
434                for i in 1..=ffi::lua_rawlen(state, -1) {
435                    ffi::lua_pushnil(state);
436                    ffi::lua_rawseti(state, -2, i as Integer);
437                }
438
439                // Clear hash part
440                // It must be safe as long as we don't use invalid keys
441                ffi::lua_pushnil(state);
442                while ffi::lua_next(state, -2) != 0 {
443                    ffi::lua_pop(state, 1); // pop value
444                    ffi::lua_pushvalue(state, -1); // copy key
445                    ffi::lua_pushnil(state);
446                    ffi::lua_rawset(state, -4);
447                }
448            }
449        }
450
451        Ok(())
452    }
453
454    /// Returns the result of the Lua `#` operator.
455    ///
456    /// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
457    ///
458    /// [`raw_len`]: #method.raw_len
459    pub fn len(&self) -> Result<Integer> {
460        // Fast track
461        if !self.has_metatable() {
462            return Ok(self.raw_len() as Integer);
463        }
464
465        let lua = self.0.lua;
466        let state = lua.state();
467        unsafe {
468            let _sg = StackGuard::new(state);
469            check_stack(state, 4)?;
470
471            lua.push_ref(&self.0);
472            protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1))
473        }
474    }
475
476    /// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
477    pub fn raw_len(&self) -> usize {
478        let ref_thread = self.0.lua.ref_thread();
479        unsafe { ffi::lua_rawlen(ref_thread, self.0.index) }
480    }
481
482    /// Returns `true` if the table is empty, without invoking metamethods.
483    ///
484    /// It checks both the array part and the hash part.
485    pub fn is_empty(&self) -> bool {
486        // Check array part
487        if self.raw_len() != 0 {
488            return false;
489        }
490
491        // Check hash part
492        let lua = self.0.lua;
493        let state = lua.state();
494        unsafe {
495            let _sg = StackGuard::new(state);
496            assert_stack(state, 4);
497
498            lua.push_ref(&self.0);
499            ffi::lua_pushnil(state);
500            if ffi::lua_next(state, -2) != 0 {
501                return false;
502            }
503        }
504
505        true
506    }
507
508    /// Returns a reference to the metatable of this table, or `None` if no metatable is set.
509    ///
510    /// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
511    pub fn get_metatable(&self) -> Option<Table<'lua>> {
512        let lua = self.0.lua;
513        let state = lua.state();
514        unsafe {
515            let _sg = StackGuard::new(state);
516            assert_stack(state, 2);
517
518            lua.push_ref(&self.0);
519            if ffi::lua_getmetatable(state, -1) == 0 {
520                None
521            } else {
522                Some(Table(lua.pop_ref()))
523            }
524        }
525    }
526
527    /// Sets or removes the metatable of this table.
528    ///
529    /// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
530    /// nothing).
531    pub fn set_metatable(&self, metatable: Option<Table<'lua>>) {
532        // Workaround to throw readonly error without returning Result
533        #[cfg(feature = "luau")]
534        if self.is_readonly() {
535            panic!("attempt to modify a readonly table");
536        }
537
538        let lua = self.0.lua;
539        let state = lua.state();
540        unsafe {
541            let _sg = StackGuard::new(state);
542            assert_stack(state, 2);
543
544            lua.push_ref(&self.0);
545            if let Some(metatable) = metatable {
546                lua.push_ref(&metatable.0);
547            } else {
548                ffi::lua_pushnil(state);
549            }
550            ffi::lua_setmetatable(state, -2);
551        }
552    }
553
554    /// Returns true if the table has metatable attached.
555    #[doc(hidden)]
556    #[inline]
557    pub fn has_metatable(&self) -> bool {
558        let ref_thread = self.0.lua.ref_thread();
559        unsafe {
560            if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
561                ffi::lua_pop(ref_thread, 1);
562                return true;
563            }
564        }
565        false
566    }
567
568    /// Sets `readonly` attribute on the table.
569    ///
570    /// Requires `feature = "luau"`
571    #[cfg(any(feature = "luau", doc))]
572    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
573    pub fn set_readonly(&self, enabled: bool) {
574        let ref_thread = self.0.lua.ref_thread();
575        unsafe {
576            ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
577            if !enabled {
578                // Reset "safeenv" flag
579                ffi::lua_setsafeenv(ref_thread, self.0.index, 0);
580            }
581        }
582    }
583
584    /// Returns `readonly` attribute of the table.
585    ///
586    /// Requires `feature = "luau"`
587    #[cfg(any(feature = "luau", doc))]
588    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
589    pub fn is_readonly(&self) -> bool {
590        let ref_thread = self.0.lua.ref_thread();
591        unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
592    }
593
594    /// Converts this table to a generic C pointer.
595    ///
596    /// Different tables will give different pointers.
597    /// There is no way to convert the pointer back to its original value.
598    ///
599    /// Typically this function is used only for hashing and debug information.
600    #[inline]
601    pub fn to_pointer(&self) -> *const c_void {
602        self.0.to_pointer()
603    }
604
605    /// Convert this handle to owned version.
606    #[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
607    #[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
608    #[inline]
609    pub fn into_owned(self) -> OwnedTable {
610        OwnedTable(self.0.into_owned())
611    }
612
613    /// Consume this table and return an iterator over the pairs of the table.
614    ///
615    /// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
616    ///
617    /// The pairs are wrapped in a [`Result`], since they are lazily converted to `K` and `V` types.
618    ///
619    /// # Note
620    ///
621    /// While this method consumes the `Table` object, it can not prevent code from mutating the
622    /// table while the iteration is in progress. Refer to the [Lua manual] for information about
623    /// the consequences of such mutation.
624    ///
625    /// # Examples
626    ///
627    /// Iterate over all globals:
628    ///
629    /// ```
630    /// # use mlua::{Lua, Result, Value};
631    /// # fn main() -> Result<()> {
632    /// # let lua = Lua::new();
633    /// let globals = lua.globals();
634    ///
635    /// for pair in globals.pairs::<Value, Value>() {
636    ///     let (key, value) = pair?;
637    /// #   let _ = (key, value);   // used
638    ///     // ...
639    /// }
640    /// # Ok(())
641    /// # }
642    /// ```
643    ///
644    /// [`Result`]: crate::Result
645    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
646    pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
647        TablePairs {
648            table: self.0,
649            key: Some(Nil),
650            _phantom: PhantomData,
651        }
652    }
653
654    /// Iterates over the pairs of the table, invoking the given closure on each pair.
655    ///
656    /// This method is similar to [`Table::pairs`], but optimized for performance.
657    /// It does not invoke the `__pairs` metamethod.
658    pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
659    where
660        K: FromLua<'lua>,
661        V: FromLua<'lua>,
662    {
663        let lua = self.0.lua;
664        let state = lua.state();
665        unsafe {
666            let _sg = StackGuard::new(state);
667            check_stack(state, 5)?;
668
669            lua.push_ref(&self.0);
670            ffi::lua_pushnil(state);
671            while ffi::lua_next(state, -2) != 0 {
672                let k = K::from_stack(-2, lua)?;
673                let v = V::from_stack(-1, lua)?;
674                f(k, v)?;
675                // Keep key for next iteration
676                ffi::lua_pop(state, 1);
677            }
678        }
679        Ok(())
680    }
681
682    /// Consume this table and return an iterator over all values in the sequence part of the table.
683    ///
684    /// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
685    /// encountered. This mirrors the behavior of Lua's `ipairs` function but does not invoke
686    /// any metamethods.
687    ///
688    /// # Note
689    ///
690    /// While this method consumes the `Table` object, it can not prevent code from mutating the
691    /// table while the iteration is in progress. Refer to the [Lua manual] for information about
692    /// the consequences of such mutation.
693    ///
694    /// # Examples
695    ///
696    /// ```
697    /// # use mlua::{Lua, Result, Table};
698    /// # fn main() -> Result<()> {
699    /// # let lua = Lua::new();
700    /// let my_table: Table = lua.load(r#"
701    ///     {
702    ///         [1] = 4,
703    ///         [2] = 5,
704    ///         [4] = 7,
705    ///         key = 2
706    ///     }
707    /// "#).eval()?;
708    ///
709    /// let expected = [4, 5];
710    /// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
711    ///     assert_eq!(expected, got?);
712    /// }
713    /// # Ok(())
714    /// # }
715    /// ```
716    ///
717    /// [`pairs`]: #method.pairs
718    /// [`Result`]: crate::Result
719    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
720    pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
721        TableSequence {
722            table: self.0,
723            index: 1,
724            _phantom: PhantomData,
725        }
726    }
727
728    #[doc(hidden)]
729    #[deprecated(since = "0.9.0", note = "use `sequence_values` instead")]
730    pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
731        self.sequence_values()
732    }
733
734    #[cfg(feature = "serialize")]
735    pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
736    where
737        V: FromLua<'lua>,
738    {
739        let lua = self.0.lua;
740        let state = lua.state();
741        unsafe {
742            let _sg = StackGuard::new(state);
743            check_stack(state, 4)?;
744
745            lua.push_ref(&self.0);
746            let len = ffi::lua_rawlen(state, -1);
747            for i in 1..=len {
748                ffi::lua_rawgeti(state, -1, i as _);
749                f(V::from_stack(-1, lua)?)?;
750                ffi::lua_pop(state, 1);
751            }
752        }
753        Ok(())
754    }
755
756    /// Sets element value at position `idx` without invoking metamethods.
757    #[doc(hidden)]
758    pub fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
759        #[cfg(feature = "luau")]
760        self.check_readonly_write()?;
761
762        let lua = self.0.lua;
763        let state = lua.state();
764        unsafe {
765            let _sg = StackGuard::new(state);
766            check_stack(state, 5)?;
767
768            lua.push_ref(&self.0);
769            value.push_into_stack(lua)?;
770
771            let idx = idx.try_into().unwrap();
772            if lua.unlikely_memory_error() {
773                ffi::lua_rawseti(state, -2, idx);
774            } else {
775                protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
776            }
777        }
778        Ok(())
779    }
780
781    #[cfg(feature = "serialize")]
782    pub(crate) fn is_array(&self) -> bool {
783        let lua = self.0.lua;
784        let state = lua.state();
785        unsafe {
786            let _sg = StackGuard::new(state);
787            assert_stack(state, 3);
788
789            lua.push_ref(&self.0);
790            if ffi::lua_getmetatable(state, -1) == 0 {
791                return false;
792            }
793            crate::serde::push_array_metatable(state);
794            ffi::lua_rawequal(state, -1, -2) != 0
795        }
796    }
797
798    #[cfg(feature = "luau")]
799    #[inline(always)]
800    pub(crate) fn check_readonly_write(&self) -> Result<()> {
801        if self.is_readonly() {
802            return Err(Error::runtime("attempt to modify a readonly table"));
803        }
804        Ok(())
805    }
806
807    pub(crate) fn fmt_pretty(
808        &self,
809        fmt: &mut fmt::Formatter,
810        ident: usize,
811        visited: &mut HashSet<*const c_void>,
812    ) -> fmt::Result {
813        visited.insert(self.to_pointer());
814
815        let t = self.clone();
816        // Collect key/value pairs into a vector so we can sort them
817        let mut pairs = t.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
818        // Sort keys
819        pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
820        if pairs.is_empty() {
821            return write!(fmt, "{{}}");
822        }
823        writeln!(fmt, "{{")?;
824        for (key, value) in pairs {
825            write!(fmt, "{}[", " ".repeat(ident + 2))?;
826            key.fmt_pretty(fmt, false, ident + 2, visited)?;
827            write!(fmt, "] = ")?;
828            value.fmt_pretty(fmt, true, ident + 2, visited)?;
829            writeln!(fmt, ",")?;
830        }
831        write!(fmt, "{}}}", " ".repeat(ident))
832    }
833}
834
835impl fmt::Debug for Table<'_> {
836    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
837        if fmt.alternate() {
838            return self.fmt_pretty(fmt, 0, &mut HashSet::new());
839        }
840        fmt.write_fmt(format_args!("Table({:?})", self.0))
841    }
842}
843
844impl<'lua> PartialEq for Table<'lua> {
845    fn eq(&self, other: &Self) -> bool {
846        self.0 == other.0
847    }
848}
849
850impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
851    #[inline]
852    fn as_ref(&self) -> &Self {
853        self
854    }
855}
856
857impl<'lua, T> PartialEq<[T]> for Table<'lua>
858where
859    T: IntoLua<'lua> + Clone,
860{
861    fn eq(&self, other: &[T]) -> bool {
862        let lua = self.0.lua;
863        let state = lua.state();
864        unsafe {
865            let _sg = StackGuard::new(state);
866            assert_stack(state, 4);
867
868            lua.push_ref(&self.0);
869
870            let len = ffi::lua_rawlen(state, -1);
871            for i in 0..len {
872                ffi::lua_rawgeti(state, -1, (i + 1) as _);
873                let val = lua.pop_value();
874                if val == Nil {
875                    return i == other.len();
876                }
877                match other.get(i).map(|v| v.clone().into_lua(lua)) {
878                    Some(Ok(other_val)) if val == other_val => continue,
879                    _ => return false,
880                }
881            }
882        }
883        true
884    }
885}
886
887impl<'lua, T> PartialEq<&[T]> for Table<'lua>
888where
889    T: IntoLua<'lua> + Clone,
890{
891    #[inline]
892    fn eq(&self, other: &&[T]) -> bool {
893        self == *other
894    }
895}
896
897impl<'lua, T, const N: usize> PartialEq<[T; N]> for Table<'lua>
898where
899    T: IntoLua<'lua> + Clone,
900{
901    #[inline]
902    fn eq(&self, other: &[T; N]) -> bool {
903        self == &other[..]
904    }
905}
906
907/// An extension trait for `Table`s that provides a variety of convenient functionality.
908pub trait TableExt<'lua>: Sealed {
909    /// Calls the table as function assuming it has `__call` metamethod.
910    ///
911    /// The metamethod is called with the table as its first argument, followed by the passed arguments.
912    fn call<A, R>(&self, args: A) -> Result<R>
913    where
914        A: IntoLuaMulti<'lua>,
915        R: FromLuaMulti<'lua>;
916
917    /// Asynchronously calls the table as function assuming it has `__call` metamethod.
918    ///
919    /// The metamethod is called with the table as its first argument, followed by the passed arguments.
920    #[cfg(feature = "async")]
921    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
922    fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
923    where
924        A: IntoLuaMulti<'lua>,
925        R: FromLuaMulti<'lua> + 'lua;
926
927    /// Gets the function associated to `key` from the table and executes it,
928    /// passing the table itself along with `args` as function arguments.
929    ///
930    /// This is a shortcut for
931    /// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
932    ///
933    /// This might invoke the `__index` metamethod.
934    fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
935    where
936        A: IntoLuaMulti<'lua>,
937        R: FromLuaMulti<'lua>;
938
939    /// Gets the function associated to `key` from the table and executes it,
940    /// passing `args` as function arguments.
941    ///
942    /// This is a shortcut for
943    /// `table.get::<_, Function>(key)?.call(args)`
944    ///
945    /// This might invoke the `__index` metamethod.
946    fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
947    where
948        A: IntoLuaMulti<'lua>,
949        R: FromLuaMulti<'lua>;
950
951    /// Gets the function associated to `key` from the table and asynchronously executes it,
952    /// passing the table itself along with `args` as function arguments and returning Future.
953    ///
954    /// Requires `feature = "async"`
955    ///
956    /// This might invoke the `__index` metamethod.
957    #[cfg(feature = "async")]
958    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
959    fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
960    where
961        A: IntoLuaMulti<'lua>,
962        R: FromLuaMulti<'lua> + 'lua;
963
964    /// Gets the function associated to `key` from the table and asynchronously executes it,
965    /// passing `args` as function arguments and returning Future.
966    ///
967    /// Requires `feature = "async"`
968    ///
969    /// This might invoke the `__index` metamethod.
970    #[cfg(feature = "async")]
971    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
972    fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
973    where
974        A: IntoLuaMulti<'lua>,
975        R: FromLuaMulti<'lua> + 'lua;
976}
977
978impl<'lua> TableExt<'lua> for Table<'lua> {
979    fn call<A, R>(&self, args: A) -> Result<R>
980    where
981        A: IntoLuaMulti<'lua>,
982        R: FromLuaMulti<'lua>,
983    {
984        // Convert table to a function and call via pcall that respects the `__call` metamethod.
985        Function(self.0.clone()).call(args)
986    }
987
988    #[cfg(feature = "async")]
989    fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
990    where
991        A: IntoLuaMulti<'lua>,
992        R: FromLuaMulti<'lua> + 'lua,
993    {
994        let args = match args.into_lua_multi(self.0.lua) {
995            Ok(args) => args,
996            Err(e) => return Box::pin(future::err(e)),
997        };
998        let func = Function(self.0.clone());
999        Box::pin(async move { func.call_async(args).await })
1000    }
1001
1002    fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
1003    where
1004        A: IntoLuaMulti<'lua>,
1005        R: FromLuaMulti<'lua>,
1006    {
1007        let lua = self.0.lua;
1008        let mut args = args.into_lua_multi(lua)?;
1009        args.push_front(Value::Table(self.clone()));
1010        self.get::<_, Function>(name)?.call(args)
1011    }
1012
1013    fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
1014    where
1015        A: IntoLuaMulti<'lua>,
1016        R: FromLuaMulti<'lua>,
1017    {
1018        self.get::<_, Function>(name)?.call(args)
1019    }
1020
1021    #[cfg(feature = "async")]
1022    fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
1023    where
1024        A: IntoLuaMulti<'lua>,
1025        R: FromLuaMulti<'lua> + 'lua,
1026    {
1027        let lua = self.0.lua;
1028        let mut args = match args.into_lua_multi(lua) {
1029            Ok(args) => args,
1030            Err(e) => return Box::pin(future::err(e)),
1031        };
1032        args.push_front(Value::Table(self.clone()));
1033        self.call_async_function(name, args)
1034    }
1035
1036    #[cfg(feature = "async")]
1037    fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
1038    where
1039        A: IntoLuaMulti<'lua>,
1040        R: FromLuaMulti<'lua> + 'lua,
1041    {
1042        let lua = self.0.lua;
1043        let args = match args.into_lua_multi(lua) {
1044            Ok(args) => args,
1045            Err(e) => return Box::pin(future::err(e)),
1046        };
1047        match self.get::<_, Function>(name) {
1048            Ok(func) => Box::pin(async move { func.call_async(args).await }),
1049            Err(e) => Box::pin(future::err(e)),
1050        }
1051    }
1052}
1053
1054/// A wrapped [`Table`] with customized serialization behavior.
1055#[cfg(feature = "serialize")]
1056pub(crate) struct SerializableTable<'a, 'lua> {
1057    table: &'a Table<'lua>,
1058    options: crate::serde::de::Options,
1059    visited: Rc<RefCell<FxHashSet<*const c_void>>>,
1060}
1061
1062#[cfg(feature = "serialize")]
1063impl<'lua> Serialize for Table<'lua> {
1064    #[inline]
1065    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
1066        SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
1067    }
1068}
1069
1070#[cfg(feature = "serialize")]
1071impl<'a, 'lua> SerializableTable<'a, 'lua> {
1072    #[inline]
1073    pub(crate) fn new(
1074        table: &'a Table<'lua>,
1075        options: crate::serde::de::Options,
1076        visited: Rc<RefCell<FxHashSet<*const c_void>>>,
1077    ) -> Self {
1078        Self {
1079            table,
1080            options,
1081            visited,
1082        }
1083    }
1084}
1085
1086#[cfg(feature = "serialize")]
1087impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
1088    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
1089    where
1090        S: Serializer,
1091    {
1092        use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
1093        use crate::value::SerializableValue;
1094
1095        let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
1096            Ok(v) => Ok(v),
1097            Err(Error::SerializeError(_)) if serialize_err.is_some() => Err(serialize_err.unwrap()),
1098            Err(Error::SerializeError(msg)) => Err(serde::ser::Error::custom(msg)),
1099            Err(err) => Err(serde::ser::Error::custom(err.to_string())),
1100        };
1101
1102        let options = self.options;
1103        let visited = &self.visited;
1104        let _guard = RecursionGuard::new(self.table, visited);
1105
1106        // Array
1107        let len = self.table.raw_len();
1108        if len > 0 || self.table.is_array() {
1109            let mut seq = serializer.serialize_seq(Some(len))?;
1110            let mut serialize_err = None;
1111            let res = self.table.for_each_value::<Value>(|value| {
1112                let skip = check_value_for_skip(&value, self.options, visited)
1113                    .map_err(|err| Error::SerializeError(err.to_string()))?;
1114                if skip {
1115                    // continue iteration
1116                    return Ok(());
1117                }
1118                seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
1119                    .map_err(|err| {
1120                        serialize_err = Some(err);
1121                        Error::SerializeError(String::new())
1122                    })
1123            });
1124            convert_result(res, serialize_err)?;
1125            return seq.end();
1126        }
1127
1128        // HashMap
1129        let mut map = serializer.serialize_map(None)?;
1130        let mut serialize_err = None;
1131        let mut process_pair = |key, value| {
1132            let skip_key = check_value_for_skip(&key, self.options, visited)
1133                .map_err(|err| Error::SerializeError(err.to_string()))?;
1134            let skip_value = check_value_for_skip(&value, self.options, visited)
1135                .map_err(|err| Error::SerializeError(err.to_string()))?;
1136            if skip_key || skip_value {
1137                // continue iteration
1138                return Ok(());
1139            }
1140            map.serialize_entry(
1141                &SerializableValue::new(&key, options, Some(visited)),
1142                &SerializableValue::new(&value, options, Some(visited)),
1143            )
1144            .map_err(|err| {
1145                serialize_err = Some(err);
1146                Error::SerializeError(String::new())
1147            })
1148        };
1149
1150        let res = if !self.options.sort_keys {
1151            // Fast track
1152            self.table.for_each(process_pair)
1153        } else {
1154            MapPairs::new(self.table.clone(), self.options.sort_keys)
1155                .map_err(serde::ser::Error::custom)?
1156                .try_for_each(|kv| {
1157                    let (key, value) = kv?;
1158                    process_pair(key, value)
1159                })
1160        };
1161        convert_result(res, serialize_err)?;
1162        map.end()
1163    }
1164}
1165
1166/// An iterator over the pairs of a Lua table.
1167///
1168/// This struct is created by the [`Table::pairs`] method.
1169///
1170/// [`Table::pairs`]: crate::Table::pairs
1171pub struct TablePairs<'lua, K, V> {
1172    table: LuaRef<'lua>,
1173    key: Option<Value<'lua>>,
1174    _phantom: PhantomData<(K, V)>,
1175}
1176
1177impl<'lua, K, V> Iterator for TablePairs<'lua, K, V>
1178where
1179    K: FromLua<'lua>,
1180    V: FromLua<'lua>,
1181{
1182    type Item = Result<(K, V)>;
1183
1184    fn next(&mut self) -> Option<Self::Item> {
1185        if let Some(prev_key) = self.key.take() {
1186            let lua = self.table.lua;
1187            let state = lua.state();
1188
1189            let res = (|| unsafe {
1190                let _sg = StackGuard::new(state);
1191                check_stack(state, 5)?;
1192
1193                lua.push_ref(&self.table);
1194                lua.push_value(prev_key)?;
1195
1196                // It must be safe to call `lua_next` unprotected as deleting a key from a table is
1197                // a permitted operation.
1198                // It fails only if the key is not found (never existed) which seems impossible scenario.
1199                if ffi::lua_next(state, -2) != 0 {
1200                    let key = lua.stack_value(-2);
1201                    Ok(Some((
1202                        key.clone(),
1203                        K::from_lua(key, lua)?,
1204                        V::from_stack(-1, lua)?,
1205                    )))
1206                } else {
1207                    Ok(None)
1208                }
1209            })();
1210
1211            match res {
1212                Ok(Some((key, ret_key, value))) => {
1213                    self.key = Some(key);
1214                    Some(Ok((ret_key, value)))
1215                }
1216                Ok(None) => None,
1217                Err(e) => Some(Err(e)),
1218            }
1219        } else {
1220            None
1221        }
1222    }
1223}
1224
1225/// An iterator over the sequence part of a Lua table.
1226///
1227/// This struct is created by the [`Table::sequence_values`] method.
1228///
1229/// [`Table::sequence_values`]: crate::Table::sequence_values
1230pub struct TableSequence<'lua, V> {
1231    // TODO: Use `&Table`
1232    table: LuaRef<'lua>,
1233    index: Integer,
1234    _phantom: PhantomData<V>,
1235}
1236
1237impl<'lua, V> Iterator for TableSequence<'lua, V>
1238where
1239    V: FromLua<'lua>,
1240{
1241    type Item = Result<V>;
1242
1243    fn next(&mut self) -> Option<Self::Item> {
1244        let lua = self.table.lua;
1245        let state = lua.state();
1246        unsafe {
1247            let _sg = StackGuard::new(state);
1248            if let Err(err) = check_stack(state, 1) {
1249                return Some(Err(err));
1250            }
1251
1252            lua.push_ref(&self.table);
1253            match ffi::lua_rawgeti(state, -1, self.index) {
1254                ffi::LUA_TNIL => None,
1255                _ => {
1256                    self.index += 1;
1257                    Some(V::from_stack(-1, lua))
1258                }
1259            }
1260        }
1261    }
1262}
1263
1264#[cfg(test)]
1265mod assertions {
1266    use super::*;
1267
1268    static_assertions::assert_not_impl_any!(Table: Send);
1269
1270    #[cfg(feature = "unstable")]
1271    static_assertions::assert_not_impl_any!(OwnedTable: Send);
1272}