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#[derive(Clone, Debug)]
22pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
23
24#[allow(clippy::len_without_is_empty)]
25impl<'lua> Table<'lua> {
26 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 ffi::lua_setsafeenv(refthr, self.0.index, 0);
370 }
371 });
372 }
373 }
374
375 #[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 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 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 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 #[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
554pub trait TableExt<'lua> {
556 fn call<A, R>(&self, args: A) -> Result<R>
560 where
561 A: ToLuaMulti<'lua>,
562 R: FromLuaMulti<'lua>;
563
564 #[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 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 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 #[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 #[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 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
754pub 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
813pub 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}