1use std::collections::HashSet;
155use std::fmt;
156use std::marker::PhantomData;
157use std::os::raw::{c_int, c_void};
158
159use crate::error::{Error, Result};
160use crate::function::Function;
161use crate::state::{LuaGuard, RawLua, WeakLua};
162use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
163use crate::types::{Integer, ValueRef};
164use crate::util::{StackGuard, assert_stack, check_stack, get_metatable_ptr};
165use crate::value::{Nil, Value};
166
167#[cfg(feature = "async")]
168use crate::function::AsyncCallFuture;
169
170#[cfg(feature = "serde")]
171use {
172 rustc_hash::FxHashSet,
173 serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
174 std::{cell::RefCell, rc::Rc, result::Result as StdResult},
175};
176
177#[derive(Clone, PartialEq)]
179pub struct Table(pub(crate) ValueRef);
180
181impl Table {
182 pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
216 self.set_impl(key, value, false)
217 }
218
219 pub(crate) fn set_impl(&self, key: impl IntoLua, value: impl IntoLua, protect: bool) -> Result<()> {
220 let lua = self.0.lua.lock();
221 let state = lua.state();
222 unsafe {
223 let _sg = StackGuard::new(state);
224 check_stack(state, 5)?;
225
226 lua.push_ref(&self.0);
227 key.push_into_stack(&lua)?;
228 value.push_into_stack(&lua)?;
229 if protect || self.has_metatable() {
230 protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
231 } else {
232 #[cfg(feature = "luau")]
233 self.check_readonly_write(&lua)?;
234
235 protect_lua_mem!(lua, or !Self::is_valid_key(state, -2), 3, 0, fn(state) {
236 ffi::lua_rawset(state, -3)
237 })
238 }
239 }
240 }
241
242 pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
267 self.get_impl(key, false)
268 }
269
270 pub(crate) fn get_impl<V: FromLua>(&self, key: impl IntoLua, protect: bool) -> Result<V> {
271 let lua = self.0.lua.lock();
272 let state = lua.state();
273 unsafe {
274 let _sg = StackGuard::new(state);
275 check_stack(state, 4)?;
276
277 lua.push_ref(&self.0);
278 key.push_into_stack(&lua)?;
279 if protect || self.has_metatable() {
280 protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
281 } else {
282 ffi::lua_rawget(state, -2);
283 }
284
285 V::from_stack(-1, &lua)
286 }
287 }
288
289 pub fn contains_key(&self, key: impl IntoLua) -> Result<bool> {
293 Ok(self.get::<Value>(key)? != Value::Nil)
294 }
295
296 pub fn push(&self, value: impl IntoLua) -> Result<()> {
300 let lua = self.0.lua.lock();
301 let state = lua.state();
302 unsafe {
303 let _sg = StackGuard::new(state);
304 check_stack(state, 4)?;
305
306 lua.push_ref(&self.0);
307 value.push_into_stack(&lua)?;
308 if !self.has_metatable() {
309 #[cfg(feature = "luau")]
310 self.check_readonly_write(&lua)?;
311
312 return protect_lua_mem!(lua, 2, 0, fn(state) {
313 let len = ffi::lua_rawlen(state, -2) as Integer;
314 ffi::lua_rawseti(state, -2, len + 1);
315 });
316 }
317 protect_lua!(state, 2, 0, fn(state) {
318 let len = ffi::luaL_len(state, -2) as Integer;
319 if len == Integer::MAX {
320 ffi::luaL_error(state, cstr!("table length overflow"));
321 }
322 ffi::lua_seti(state, -2, len + 1);
323 })?
324 }
325 Ok(())
326 }
327
328 pub fn pop<V: FromLua>(&self) -> Result<V> {
332 if !self.has_metatable() {
334 return self.raw_pop();
335 }
336
337 let lua = self.0.lua.lock();
338 let state = lua.state();
339 unsafe {
340 let _sg = StackGuard::new(state);
341 check_stack(state, 4)?;
342
343 lua.push_ref(&self.0);
344 protect_lua!(state, 1, 1, fn(state) {
345 let len = ffi::luaL_len(state, -1) as Integer;
346 if len == 0 {
347 ffi::lua_pushnil(state);
348 } else {
349 ffi::lua_geti(state, -1, len);
350 ffi::lua_pushnil(state);
351 ffi::lua_seti(state, -3, len);
352 }
353 })?;
354 V::from_stack(-1, &lua)
355 }
356 }
357
358 pub fn remove(&self, key: impl IntoLua) -> Result<()> {
371 let lua = self.0.lua.lock();
372 let key = key.into_lua(lua.lua())?;
373
374 if !self.has_metatable() {
376 return self.raw_remove(key);
377 }
378
379 match key {
380 Value::Integer(idx) => {
381 let size = self.len()?;
382 if idx < 1 || idx > size {
383 return Err(Error::runtime("index out of bounds"));
384 }
385
386 let state = lua.state();
387 unsafe {
388 let _sg = StackGuard::new(state);
389 check_stack(state, 4)?;
390
391 lua.push_ref(&self.0);
392 protect_lua!(state, 1, 0, |state| {
393 for i in idx..size {
394 ffi::lua_geti(state, -1, i + 1);
396 ffi::lua_seti(state, -2, i);
397 }
398 ffi::lua_pushnil(state);
399 ffi::lua_seti(state, -2, size);
400 })
401 }
402 }
403 _ => self.set(key, Nil),
404 }
405 }
406
407 pub fn equals(&self, other: &Self) -> Result<bool> {
437 if self == other {
438 return Ok(true);
439 }
440
441 if let Some(mt) = self.try_metatable()?
445 && let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
446 {
447 return eq_func.call((self, other));
448 }
449 if let Some(mt) = other.try_metatable()?
450 && let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
451 {
452 return eq_func.call((self, other));
453 }
454
455 Ok(false)
456 }
457
458 pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
460 let lua = self.0.lua.lock();
461 let state = lua.state();
462 unsafe {
463 let _sg = StackGuard::new(state);
464 check_stack(state, 5)?;
465
466 lua.push_ref(&self.0);
467 key.push_into_stack(&lua)?;
468 value.push_into_stack(&lua)?;
469
470 #[cfg(feature = "luau")]
471 self.check_readonly_write(&lua)?;
472
473 protect_lua_mem!(lua, or !Self::is_valid_key(state, -2), 3, 1, fn(state) {
474 ffi::lua_rawset(state, -3)
475 })?;
476 ffi::lua_pop(state, 1);
477 Ok(())
478 }
479 }
480
481 pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
483 let lua = self.0.lua.lock();
484 let state = lua.state();
485 unsafe {
486 let _sg = StackGuard::new(state);
487 check_stack(state, 3)?;
488
489 lua.push_ref(&self.0);
490 key.push_into_stack(&lua)?;
491 ffi::lua_rawget(state, -2);
492
493 V::from_stack(-1, &lua)
494 }
495 }
496
497 pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
502 let size = self.raw_len() as Integer;
503 if idx < 1 || idx > size + 1 {
504 return Err(Error::runtime("index out of bounds"));
505 }
506
507 let lua = self.0.lua.lock();
508 let state = lua.state();
509 unsafe {
510 let _sg = StackGuard::new(state);
511 check_stack(state, 5)?;
512
513 lua.push_ref(&self.0);
514 value.push_into_stack(&lua)?;
515 protect_lua!(state, 2, 0, |state| {
516 for i in (idx..=size).rev() {
517 ffi::lua_rawgeti(state, -2, i);
519 ffi::lua_rawseti(state, -3, i + 1);
520 }
521 ffi::lua_rawseti(state, -2, idx)
522 })
523 }
524 }
525
526 pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
528 let lua = self.0.lua.lock();
529 let state = lua.state();
530 unsafe {
531 let _sg = StackGuard::new(state);
532 check_stack(state, 4)?;
533
534 lua.push_ref(&self.0);
535 value.push_into_stack(&lua)?;
536
537 #[cfg(feature = "luau")]
538 self.check_readonly_write(&lua)?;
539
540 protect_lua_mem!(lua, 2, 0, fn(state) {
541 let len = ffi::lua_rawlen(state, -2) as Integer;
542 ffi::lua_rawseti(state, -2, len + 1);
543 })
544 }
545 }
546
547 pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
549 let lua = self.0.lua.lock();
550 let state = lua.state();
551 unsafe {
552 #[cfg(feature = "luau")]
553 self.check_readonly_write(&lua)?;
554
555 let _sg = StackGuard::new(state);
556 check_stack(state, 3)?;
557
558 lua.push_ref(&self.0);
559 let len = ffi::lua_rawlen(state, -1) as Integer;
560 if len == 0 {
561 ffi::lua_pushnil(state);
562 } else {
563 ffi::lua_rawgeti(state, -1, len);
564 ffi::lua_pushnil(state);
566 ffi::lua_rawseti(state, -3, len);
567 }
568
569 V::from_stack(-1, &lua)
570 }
571 }
572
573 pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
581 let lua = self.0.lua.lock();
582 let state = lua.state();
583 let key = key.into_lua(lua.lua())?;
584 match key {
585 Value::Integer(idx) => {
586 let size = self.raw_len() as Integer;
587 if idx < 1 || idx > size {
588 return Err(Error::runtime("index out of bounds"));
589 }
590 unsafe {
591 let _sg = StackGuard::new(state);
592 check_stack(state, 4)?;
593
594 lua.push_ref(&self.0);
595 protect_lua!(state, 1, 0, |state| {
596 for i in idx..size {
597 ffi::lua_rawgeti(state, -1, i + 1);
598 ffi::lua_rawseti(state, -2, i);
599 }
600 ffi::lua_pushnil(state);
601 ffi::lua_rawseti(state, -2, size);
602 })
603 }
604 }
605 _ => self.raw_set(key, Nil),
606 }
607 }
608
609 pub fn clear(&self) -> Result<()> {
614 let lua = self.0.lua.lock();
615 unsafe {
616 #[cfg(feature = "luau")]
617 {
618 self.check_readonly_write(&lua)?;
619 ffi::lua_cleartable(lua.ref_thread(), self.0.index);
620 }
621
622 #[cfg(not(feature = "luau"))]
623 {
624 let state = lua.state();
625 let _sg = StackGuard::new(state);
626 check_stack(state, 4)?;
627
628 lua.push_ref(&self.0);
629
630 ffi::lua_pushnil(state);
632 while ffi::lua_next(state, -2) != 0 {
633 ffi::lua_pop(state, 1); ffi::lua_pushvalue(state, -1); ffi::lua_pushnil(state);
636 ffi::lua_rawset(state, -4);
637 }
638 }
639 }
640
641 Ok(())
642 }
643
644 pub fn len(&self) -> Result<Integer> {
649 if !self.has_metatable() {
651 return Ok(self.raw_len() as Integer);
652 }
653
654 let lua = self.0.lua.lock();
655 let state = lua.state();
656 unsafe {
657 let _sg = StackGuard::new(state);
658 check_stack(state, 4)?;
659
660 lua.push_ref(&self.0);
661 protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1))
662 }
663 }
664
665 pub fn raw_len(&self) -> usize {
667 let lua = self.0.lua.lock();
668 unsafe { ffi::lua_rawlen(lua.ref_thread(), self.0.index) }
669 }
670
671 pub fn is_empty(&self) -> bool {
675 let lua = self.0.lua.lock();
676 let ref_thread = lua.ref_thread();
677 unsafe {
678 ffi::lua_pushnil(ref_thread);
679 if ffi::lua_next(ref_thread, self.0.index) == 0 {
680 return true;
681 }
682 ffi::lua_pop(ref_thread, 2);
683 }
684 false
685 }
686
687 fn try_metatable(&self) -> Result<Option<Table>> {
689 let lua = self.0.lua.lock();
690 let ref_thread = lua.ref_thread();
691 unsafe {
692 Ok(if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
693 None
694 } else {
695 Some(Table(lua.try_pop_ref_thread()?))
696 })
697 }
698 }
699
700 pub fn metatable(&self) -> Option<Table> {
706 let lua = self.0.lua.lock();
707 let ref_thread = lua.ref_thread();
708 unsafe {
709 if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
710 None
711 } else {
712 Some(Table(lua.pop_ref_thread()))
713 }
714 }
715 }
716
717 pub fn set_metatable(&self, metatable: Option<Table>) -> Result<()> {
722 #[cfg(feature = "luau")]
723 if self.is_readonly() {
724 return Err(Error::runtime("attempt to modify a readonly table"));
725 }
726
727 let lua = self.0.lua.lock();
728 let ref_thread = lua.ref_thread();
729 unsafe {
730 if let Some(metatable) = &metatable {
731 assert!(
732 lua.weak() == &metatable.0.lua,
733 "Lua instance passed Value created from a different main Lua state"
734 );
735 ffi::lua_pushvalue(ref_thread, metatable.0.index);
736 } else {
737 ffi::lua_pushnil(ref_thread);
738 }
739 ffi::lua_setmetatable(ref_thread, self.0.index);
740 }
741 Ok(())
742 }
743
744 #[doc(hidden)]
746 #[inline]
747 pub fn has_metatable(&self) -> bool {
748 let lua = self.0.lua.lock();
749 unsafe { !get_metatable_ptr(lua.ref_thread(), self.0.index).is_null() }
750 }
751
752 #[cfg(any(feature = "luau", doc))]
754 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
755 pub fn set_readonly(&self, enabled: bool) {
756 let lua = self.0.lua.lock();
757 let ref_thread = lua.ref_thread();
758 unsafe {
759 ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
760 if !enabled {
761 ffi::lua_setsafeenv(ref_thread, self.0.index, 0);
763 }
764 }
765 }
766
767 #[cfg(any(feature = "luau", doc))]
769 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
770 pub fn is_readonly(&self) -> bool {
771 let lua = self.0.lua.lock();
772 let ref_thread = lua.ref_thread();
773 unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
774 }
775
776 #[cfg(any(feature = "luau", doc))]
786 #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
787 pub fn set_safeenv(&self, enabled: bool) {
788 let lua = self.0.lua.lock();
789 unsafe { ffi::lua_setsafeenv(lua.ref_thread(), self.0.index, enabled as _) };
790 }
791
792 #[inline]
799 pub fn to_pointer(&self) -> *const c_void {
800 self.0.to_pointer()
801 }
802
803 pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<'_, K, V> {
830 TablePairs {
831 guard: self.0.lua.lock(),
832 table: self,
833 key: Some(Nil),
834 #[cfg(feature = "luau")]
835 index: 0,
836 _phantom: PhantomData,
837 }
838 }
839
840 pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
845 where
846 K: FromLua,
847 V: FromLua,
848 {
849 let lua = self.0.lua.lock();
850 let state = lua.state();
851 unsafe {
852 let _sg = StackGuard::new(state);
853 check_stack(state, 5)?;
854
855 lua.push_ref(&self.0);
856 let mut callback = || {
857 let k = K::from_stack(-2, &lua)?;
858 let v = V::from_stack(-1, &lua)?;
859 ffi::lua_pop(state, if cfg!(feature = "luau") { 2 } else { 1 });
860 f(k, v)
861 };
862
863 #[cfg(feature = "luau")]
864 {
865 let mut index = ffi::lua_rawiter(state, -1, 0);
866 while index >= 0 {
867 callback()?;
868 index = ffi::lua_rawiter(state, -1, index);
869 }
870 }
871
872 #[cfg(not(feature = "luau"))]
873 {
874 use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
875
876 let mut result = Ok(Ok(()));
877 let lua_result = protect_lua!(state, 1, 0, |state| {
878 ffi::lua_pushnil(state);
879 while matches!(result, Ok(Ok(()))) && ffi::lua_next(state, -2) != 0 {
880 match catch_unwind(AssertUnwindSafe(&mut callback)) {
882 Ok(Ok(())) => {}
883 err => result = err,
884 }
885 }
886 });
887 result.unwrap_or_else(|panic| resume_unwind(panic))?;
888 lua_result?;
889 }
890 }
891 Ok(())
892 }
893
894 pub fn sequence_values<V: FromLua>(&self) -> TableSequence<'_, V> {
923 TableSequence {
924 guard: self.0.lua.lock(),
925 table: self,
926 index: 1,
927 len: None,
928 _phantom: PhantomData,
929 }
930 }
931
932 #[doc(hidden)]
936 pub fn for_each_value<V: FromLua>(&self, f: impl FnMut(V) -> Result<()>) -> Result<()> {
937 self.for_each_value_by_len(None, f)
938 }
939
940 fn for_each_value_by_len<V: FromLua>(
941 &self,
942 len: impl Into<Option<usize>>,
943 mut f: impl FnMut(V) -> Result<()>,
944 ) -> Result<()> {
945 let len = len.into();
946 let lua = self.0.lua.lock();
947 let state = lua.state();
948 unsafe {
949 let _sg = StackGuard::new(state);
950 check_stack(state, 4)?;
951
952 lua.push_ref(&self.0);
953 for i in 1.. {
954 if len.map(|len| i > len).unwrap_or(false) {
955 break;
956 }
957 let t = ffi::lua_rawgeti(state, -1, i as _);
958 if len.is_none() && t == ffi::LUA_TNIL {
959 break;
960 }
961 f(lua.pop::<V>()?)?;
962 }
963 }
964 Ok(())
965 }
966
967 #[doc(hidden)]
969 pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
970 let lua = self.0.lua.lock();
971 let state = lua.state();
972 unsafe {
973 let _sg = StackGuard::new(state);
974 check_stack(state, 5)?;
975
976 lua.push_ref(&self.0);
977 value.push_into_stack(&lua)?;
978
979 #[cfg(feature = "luau")]
980 self.check_readonly_write(&lua)?;
981
982 let idx = idx.try_into().unwrap();
983 protect_lua_mem!(lua, 2, 0, |state| {
984 ffi::lua_rawseti(state, -2, idx);
985 })
986 }
987 }
988
989 #[cfg(feature = "serde")]
991 fn has_array_metatable(&self) -> bool {
992 let lua = self.0.lua.lock();
993 let ref_thread = lua.ref_thread();
994 unsafe {
995 let _sg = StackGuard::new(ref_thread);
996
997 if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
998 return false;
999 }
1000 crate::serde::push_array_metatable(ref_thread);
1001 ffi::lua_rawequal(ref_thread, -1, -2) != 0
1002 }
1003 }
1004
1005 #[cfg(feature = "serde")]
1011 fn find_array_len(&self) -> Option<(usize, usize)> {
1012 let lua = self.0.lua.lock();
1013 let ref_thread = lua.ref_thread();
1014 unsafe {
1015 let _sg = StackGuard::new(ref_thread);
1016
1017 let (mut count, mut max_index) = (0, 0);
1018 ffi::lua_pushnil(ref_thread);
1019 while ffi::lua_next(ref_thread, self.0.index) != 0 {
1020 if ffi::lua_type(ref_thread, -2) != ffi::LUA_TNUMBER {
1021 return None;
1022 }
1023
1024 let k = ffi::lua_tonumber(ref_thread, -2);
1025 if k.trunc() != k || k < 1.0 {
1026 return None;
1027 }
1028 max_index = std::cmp::max(max_index, k as usize);
1029 count += 1;
1030 ffi::lua_pop(ref_thread, 1);
1031 }
1032 Some((count, max_index))
1033 }
1034 }
1035
1036 #[cfg(feature = "serde")]
1051 pub(crate) fn encode_as_array(&self, options: crate::serde::de::Options) -> Option<usize> {
1052 if self.has_array_metatable() {
1053 return Some(self.raw_len());
1054 }
1055 if options.detect_mixed_tables {
1056 if let Some((len, max_idx)) = self.find_array_len() {
1057 if max_idx < 10 || len * 2 >= max_idx {
1059 return Some(max_idx);
1060 }
1061 }
1062 } else {
1063 let len = self.raw_len();
1064 if len > 0 {
1065 return Some(len);
1066 }
1067 if options.encode_empty_tables_as_array && self.is_empty() {
1068 return Some(0);
1069 }
1070 }
1071 None
1072 }
1073
1074 #[cfg(feature = "serde")]
1075 pub(crate) fn collect_pairs(&self) -> Result<Vec<(Value, Value)>> {
1076 let mut pairs = Vec::new();
1077
1078 #[cfg(not(feature = "luau"))]
1079 unsafe {
1080 const LIMIT: c_int = 8;
1081 let lua = self.0.lua.lock();
1082 let state = lua.state();
1083 let _sg = StackGuard::new(state);
1084 check_stack(state, 2 * LIMIT + 2)?;
1085
1086 lua.push_ref(&self.0);
1087 let table_index = ffi::lua_gettop(state);
1088 ffi::lua_pushnil(state);
1089 for count in 0..LIMIT {
1091 if ffi::lua_next(state, table_index) == 0 {
1092 pairs.reserve_exact(count as usize);
1093 for i in 0..count {
1094 let index = table_index + 1 + 2 * i;
1095 let key = lua.try_stack_value(index, None)?;
1096 pairs.push((key, lua.try_stack_value(index + 1, None)?));
1097 }
1098 return Ok(pairs);
1099 }
1100 ffi::lua_pushvalue(state, -2);
1101 }
1102 }
1103
1104 self.for_each(|key, value| {
1105 pairs.push((key, value));
1106 Ok(())
1107 })?;
1108 Ok(pairs)
1109 }
1110
1111 #[cfg(not(feature = "luau"))]
1112 #[inline]
1113 unsafe fn next(state: *mut ffi::lua_State) -> Result<bool> {
1114 let protect = ffi::lua_isnil(state, -1) == 0 && {
1115 ffi::lua_pushvalue(state, -1);
1116 let missing = ffi::lua_rawget(state, -3) == ffi::LUA_TNIL;
1117 ffi::lua_pop(state, 1);
1118 missing
1119 };
1120 protect_lua_mem!(state, if protect, 2, ffi::LUA_MULTRET, |state| ffi::lua_next(state, -2) != 0)
1122 }
1123
1124 #[inline]
1125 pub(crate) unsafe fn is_valid_key(state: *mut ffi::lua_State, idx: c_int) -> bool {
1126 match ffi::lua_type(state, idx) {
1127 ffi::LUA_TNIL => false,
1128 ffi::LUA_TNUMBER => !ffi::lua_tonumber(state, idx).is_nan(),
1129 #[cfg(feature = "luau")]
1131 ffi::LUA_TVECTOR => ffi::lua_rawequal(state, idx, idx) != 0,
1132 _ => true,
1133 }
1134 }
1135
1136 #[cfg(feature = "luau")]
1137 #[inline(always)]
1138 fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
1139 if unsafe { ffi::lua_getreadonly(lua.ref_thread(), self.0.index) != 0 } {
1140 return Err(Error::runtime("attempt to modify a readonly table"));
1141 }
1142 Ok(())
1143 }
1144
1145 pub(crate) fn fmt_pretty(
1146 &self,
1147 fmt: &mut fmt::Formatter,
1148 ident: usize,
1149 visited: &mut HashSet<*const c_void>,
1150 ) -> fmt::Result {
1151 visited.insert(self.to_pointer());
1152
1153 let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
1155 pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
1157 let is_sequence = (pairs.iter().enumerate())
1158 .all(|(i, (k, _))| matches!(k, Value::Integer(n) if *n == (i + 1) as Integer));
1159 if pairs.is_empty() {
1160 return write!(fmt, "{{}}");
1161 }
1162 writeln!(fmt, "{{")?;
1163 if is_sequence {
1164 for (_, value) in pairs {
1166 write!(fmt, "{}", " ".repeat(ident + 2))?;
1167 value.fmt_pretty(fmt, true, ident + 2, visited)?;
1168 writeln!(fmt, ",")?;
1169 }
1170 } else {
1171 fn is_simple_key(key: &[u8]) -> bool {
1172 key.iter().take(1).all(|c| c.is_ascii_alphabetic() || *c == b'_')
1173 && key.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'_')
1174 }
1175
1176 for (key, value) in pairs {
1177 match key {
1178 Value::String(key) if is_simple_key(&key.as_bytes()) => {
1179 write!(fmt, "{}{}", " ".repeat(ident + 2), key.display())?;
1180 write!(fmt, " = ")?;
1181 }
1182 _ => {
1183 write!(fmt, "{}[", " ".repeat(ident + 2))?;
1184 key.fmt_pretty(fmt, false, ident + 2, visited)?;
1185 write!(fmt, "] = ")?;
1186 }
1187 }
1188 value.fmt_pretty(fmt, true, ident + 2, visited)?;
1189 writeln!(fmt, ",")?;
1190 }
1191 }
1192 write!(fmt, "{}}}", " ".repeat(ident))
1193 }
1194}
1195
1196impl fmt::Debug for Table {
1197 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1198 if fmt.alternate() {
1199 return self.fmt_pretty(fmt, 0, &mut HashSet::new());
1200 }
1201 fmt.debug_tuple("Table").field(&self.0).finish()
1202 }
1203}
1204
1205impl<T> PartialEq<[T]> for Table
1206where
1207 T: IntoLua + Clone,
1208{
1209 fn eq(&self, other: &[T]) -> bool {
1210 let lua = self.0.lua.lock();
1211 let state = lua.state();
1212 unsafe {
1213 let _sg = StackGuard::new(state);
1214 assert_stack(state, 4);
1215
1216 lua.push_ref(&self.0);
1217
1218 let len = ffi::lua_rawlen(state, -1);
1219 for i in 0..len {
1220 ffi::lua_rawgeti(state, -1, (i + 1) as _);
1221 let val = lua.pop_value();
1222 if val == Nil {
1223 return i == other.len();
1224 }
1225 match other.get(i).map(|v| v.clone().into_lua(lua.lua())) {
1226 Some(Ok(other_val)) if val == other_val => continue,
1227 _ => return false,
1228 }
1229 }
1230 len == other.len()
1231 }
1232 }
1233}
1234
1235impl<T> PartialEq<&[T]> for Table
1236where
1237 T: IntoLua + Clone,
1238{
1239 #[inline]
1240 fn eq(&self, other: &&[T]) -> bool {
1241 self == *other
1242 }
1243}
1244
1245impl<T, const N: usize> PartialEq<[T; N]> for Table
1246where
1247 T: IntoLua + Clone,
1248{
1249 #[inline]
1250 fn eq(&self, other: &[T; N]) -> bool {
1251 self == &other[..]
1252 }
1253}
1254
1255impl ObjectLike for Table {
1256 #[inline]
1257 fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
1258 self.get(key)
1259 }
1260
1261 #[inline]
1262 fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
1263 self.set(key, value)
1264 }
1265
1266 #[inline]
1267 fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
1268 where
1269 R: FromLuaMulti,
1270 {
1271 Function(self.0.clone()).call(args)
1273 }
1274
1275 #[cfg(feature = "async")]
1276 #[inline]
1277 fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
1278 where
1279 R: FromLuaMulti,
1280 {
1281 Function(self.0.clone()).call_async(args)
1282 }
1283
1284 #[inline]
1285 fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
1286 where
1287 R: FromLuaMulti,
1288 {
1289 self.call_function(name, (self, args))
1290 }
1291
1292 #[cfg(feature = "async")]
1293 fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
1294 where
1295 R: FromLuaMulti,
1296 {
1297 self.call_async_function(name, (self, args))
1298 }
1299
1300 #[inline]
1301 fn call_function<R: FromLuaMulti>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R> {
1302 match self.get(name)? {
1303 Value::Function(func) => func.call(args),
1304 val => {
1305 let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
1306 Err(Error::runtime(msg))
1307 }
1308 }
1309 }
1310
1311 #[cfg(feature = "async")]
1312 #[inline]
1313 fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
1314 where
1315 R: FromLuaMulti,
1316 {
1317 match self.get(name) {
1318 Ok(Value::Function(func)) => func.call_async(args),
1319 Ok(val) => {
1320 let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
1321 AsyncCallFuture::error(Error::RuntimeError(msg))
1322 }
1323 Err(err) => AsyncCallFuture::error(err),
1324 }
1325 }
1326
1327 #[inline]
1328 fn to_string(&self) -> Result<String> {
1329 Value::Table(Table(self.0.clone())).to_string()
1330 }
1331
1332 #[inline]
1333 fn to_value(&self) -> Value {
1334 Value::Table(self.clone())
1335 }
1336
1337 #[inline]
1338 fn weak_lua(&self) -> &WeakLua {
1339 &self.0.lua
1340 }
1341}
1342
1343#[cfg(feature = "serde")]
1345pub(crate) struct SerializableTable<'a> {
1346 table: &'a Table,
1347 options: crate::serde::de::Options,
1348 visited: Rc<RefCell<FxHashSet<*const c_void>>>,
1349}
1350
1351#[cfg(feature = "serde")]
1352impl Serialize for Table {
1353 #[inline]
1354 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
1355 SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
1356 }
1357}
1358
1359#[cfg(feature = "serde")]
1360impl<'a> SerializableTable<'a> {
1361 #[inline]
1362 pub(crate) fn new(
1363 table: &'a Table,
1364 options: crate::serde::de::Options,
1365 visited: Rc<RefCell<FxHashSet<*const c_void>>>,
1366 ) -> Self {
1367 Self {
1368 table,
1369 options,
1370 visited,
1371 }
1372 }
1373}
1374
1375impl<V> TableSequence<'_, V> {
1376 #[cfg(feature = "serde")]
1378 pub(crate) fn with_len(mut self, len: usize) -> Self {
1379 self.len = Some(len);
1380 self
1381 }
1382}
1383
1384#[cfg(feature = "serde")]
1385impl Serialize for SerializableTable<'_> {
1386 fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
1387 where
1388 S: Serializer,
1389 {
1390 use crate::serde::de::{MapPairs, RecursionGuard, check_key_for_skip, check_value_for_skip};
1391 use crate::value::SerializableValue;
1392
1393 let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
1394 Ok(v) => Ok(v),
1395 Err(Error::SerializeError(_)) if serialize_err.is_some() => Err(serialize_err.unwrap()),
1396 Err(Error::SerializeError(msg)) => Err(serde::ser::Error::custom(msg)),
1397 Err(err) => Err(serde::ser::Error::custom(err.to_string())),
1398 };
1399
1400 let options = self.options;
1401 let visited = &self.visited;
1402 let _guard = RecursionGuard::new(self.table, visited, options.recursion_limit)
1403 .map_err(serde::ser::Error::custom)?;
1404
1405 if let Some(len) = self.table.encode_as_array(self.options) {
1407 let mut seq = serializer.serialize_seq(Some(len))?;
1408 let mut serialize_err = None;
1409 let res = self.table.for_each_value_by_len::<Value>(len, |value| {
1410 let skip = check_value_for_skip(&value, self.options, visited)
1411 .map_err(|err| Error::SerializeError(err.to_string()))?;
1412 if skip {
1413 return Ok(());
1415 }
1416 seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
1417 .map_err(|err| {
1418 serialize_err = Some(err);
1419 Error::SerializeError(String::new())
1420 })
1421 });
1422 convert_result(res, serialize_err)?;
1423 return seq.end();
1424 }
1425
1426 let mut map = serializer.serialize_map(None)?;
1428 let mut serialize_err = None;
1429 let mut process_pair = |key, value| {
1430 let skip_key = check_key_for_skip(&key, self.options, visited)
1431 .map_err(|err| Error::SerializeError(err.to_string()))?;
1432 let skip_value = check_value_for_skip(&value, self.options, visited)
1433 .map_err(|err| Error::SerializeError(err.to_string()))?;
1434 if skip_key || skip_value {
1435 return Ok(());
1437 }
1438 map.serialize_entry(
1439 &SerializableValue::new(&key, options, Some(visited)),
1440 &SerializableValue::new(&value, options, Some(visited)),
1441 )
1442 .map_err(|err| {
1443 serialize_err = Some(err);
1444 Error::SerializeError(String::new())
1445 })
1446 };
1447
1448 let res = if !self.options.sort_keys {
1449 self.table.for_each(process_pair)
1451 } else {
1452 MapPairs::new(self.table, self.options.sort_keys)
1453 .map_err(serde::ser::Error::custom)?
1454 .try_for_each(|kv| {
1455 let (key, value) = kv?;
1456 process_pair(key, value)
1457 })
1458 };
1459 convert_result(res, serialize_err)?;
1460 map.end()
1461 }
1462}
1463
1464pub struct TablePairs<'a, K, V> {
1470 guard: LuaGuard,
1471 table: &'a Table,
1472 key: Option<Value>,
1473 #[cfg(feature = "luau")]
1474 index: c_int,
1475 _phantom: PhantomData<(K, V)>,
1476}
1477
1478impl<K, V> Iterator for TablePairs<'_, K, V>
1479where
1480 K: FromLua,
1481 V: FromLua,
1482{
1483 type Item = Result<(K, V)>;
1484
1485 fn next(&mut self) -> Option<Self::Item> {
1486 if let Some(_prev_key) = self.key.take() {
1487 let lua: &RawLua = &self.guard;
1488 let state = lua.state();
1489
1490 let res = (|| unsafe {
1491 let _sg = StackGuard::new(state);
1492 check_stack(state, 5)?;
1493
1494 lua.push_ref(&self.table.0);
1495 #[cfg(feature = "luau")]
1496 let more = {
1497 self.index = ffi::lua_rawiter(state, -1, self.index);
1498 self.index >= 0
1499 };
1500 #[cfg(not(feature = "luau"))]
1501 let more = {
1502 lua.push_value(&_prev_key)?;
1503 Table::next(state)?
1504 };
1505
1506 if more {
1507 let key = lua.try_stack_value(-2, None)?;
1508 Ok(Some((
1509 key.clone(),
1510 K::from_lua(key, lua.lua())?,
1511 V::from_stack(-1, lua)?,
1512 )))
1513 } else {
1514 Ok(None)
1515 }
1516 })();
1517
1518 match res {
1519 Ok(Some((key, ret_key, value))) => {
1520 self.key = Some(key);
1521 Some(Ok((ret_key, value)))
1522 }
1523 Ok(None) => None,
1524 Err(e) => Some(Err(e)),
1525 }
1526 } else {
1527 None
1528 }
1529 }
1530}
1531
1532pub struct TableSequence<'a, V> {
1538 guard: LuaGuard,
1539 table: &'a Table,
1540 index: Integer,
1541 len: Option<usize>,
1542 _phantom: PhantomData<V>,
1543}
1544
1545impl<V: FromLua> Iterator for TableSequence<'_, V> {
1546 type Item = Result<V>;
1547
1548 fn next(&mut self) -> Option<Self::Item> {
1549 let lua: &RawLua = &self.guard;
1550 let state = lua.state();
1551 unsafe {
1552 let _sg = StackGuard::new(state);
1553 if let Err(err) = check_stack(state, 1) {
1554 return Some(Err(err));
1555 }
1556
1557 lua.push_ref(&self.table.0);
1558 match ffi::lua_rawgeti(state, -1, self.index) {
1559 ffi::LUA_TNIL if self.index as usize > self.len.unwrap_or(0) => None,
1560 _ => {
1561 self.index += 1;
1562 Some(V::from_stack(-1, lua))
1563 }
1564 }
1565 }
1566 }
1567}
1568
1569#[cfg(test)]
1570mod assertions {
1571 use super::*;
1572
1573 #[cfg(not(feature = "send"))]
1574 static_assertions::assert_not_impl_any!(Table: Send);
1575 #[cfg(feature = "send")]
1576 static_assertions::assert_impl_all!(Table: Send, Sync);
1577}