1use crate::error::Result;
4use crate::state::{Lua, LuaRef};
5use crate::sync::{NotSync, XRc, NOT_SYNC};
6use crate::sys::*;
7use crate::traits::{FromLua, IntoLua};
8use crate::value::Value;
9
10#[derive(Clone)]
17pub struct Table {
18 pub(crate) reference: XRc<LuaRef>,
19 pub(crate) _not_sync: NotSync,
20}
21
22impl Table {
23 pub(crate) fn from_ref(reference: LuaRef) -> Table {
24 Table {
25 reference: XRc::new(reference),
26 _not_sync: NOT_SYNC,
27 }
28 }
29
30 pub(crate) unsafe fn push_to_stack(&self) {
31 self.reference.push();
32 }
33
34 pub fn lua(&self) -> Lua {
36 self.reference.lua()
37 }
38
39 pub fn set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
44 let lua = self.lua();
45 let state = lua.state();
46 let k = key.into_lua(&lua)?;
47 let v = value.into_lua(&lua)?;
48 unsafe {
51 self.reference.push(); lua.push_value(&k)?; lua.push_value(&v)?; let status = protected_settable(state);
55 if status != 0 {
56 return Err(lua.pop_error(status));
57 }
58 }
59 Ok(())
60 }
61
62 pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
68 let lua = self.lua();
69 let state = lua.state();
70 let k = key.into_lua(&lua)?;
71 let value = unsafe {
72 self.reference.push(); lua.push_value(&k)?; let status = protected_gettable(state);
75 if status != 0 {
76 return Err(lua.pop_error(status));
77 }
78 let v = lua.value_from_stack(-1)?;
79 lua_pop(state, 1); v
81 };
82 V::from_lua(value, &lua)
83 }
84
85 pub fn contains_key<K: IntoLua>(&self, key: K) -> Result<bool> {
89 let v: Value = self.get(key)?;
90 Ok(!v.is_nil())
91 }
92
93 pub fn raw_len(&self) -> usize {
98 let state = self.reference.state();
99 unsafe {
100 self.reference.push();
101 let n = lua_objlen(state, -1);
102 lua_pop(state, 1);
103 n.max(0) as usize
104 }
105 }
106
107 pub fn len(&self) -> Result<usize> {
112 let lua = self.lua();
113 if self.metatable().is_none() {
115 return Ok(self.raw_len());
116 }
117 let f = lua.load("local t = ...; return #t").into_function()?;
119 let n: i64 = f.call(self.clone())?;
120 Ok(n.max(0) as usize)
121 }
122
123 pub fn is_empty(&self) -> bool {
129 let state = self.reference.state();
130 unsafe {
131 self.reference.push(); lua_pushnil(state); if lua_next(state, -2) == 0 {
134 lua_pop(state, 1); return true;
137 }
138 lua_pop(state, 3);
140 false
141 }
142 }
143
144 pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
149 TablePairs {
150 table: self.clone(),
151 next_key: Some(Value::Nil),
152 _phantom: std::marker::PhantomData,
153 }
154 }
155
156 pub fn pairs_vec<K: FromLua, V: FromLua>(&self) -> Result<Vec<(K, V)>> {
159 self.pairs().collect()
160 }
161
162 pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
165 TableSequence {
166 table: self.clone(),
167 index: 1,
168 _phantom: std::marker::PhantomData,
169 }
170 }
171
172 pub fn for_each<K: FromLua, V: FromLua>(
178 &self,
179 mut f: impl FnMut(K, V) -> Result<()>,
180 ) -> Result<()> {
181 for pair in self.pairs::<K, V>() {
182 let (k, v) = pair?;
183 f(k, v)?;
184 }
185 Ok(())
186 }
187
188 pub fn for_each_value<V: FromLua>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()> {
191 for v in self.sequence_values::<V>() {
192 f(v?)?;
193 }
194 Ok(())
195 }
196
197 pub fn raw_set<K: IntoLua, V: IntoLua>(&self, key: K, value: V) -> Result<()> {
203 let lua = self.lua();
204 let state = lua.state();
205 let k = key.into_lua(&lua)?;
206 let v = value.into_lua(&lua)?;
207 if self.is_readonly() {
208 return Err(crate::error::Error::RuntimeError(
209 "attempt to modify a readonly table".to_string(),
210 ));
211 }
212 unsafe {
213 self.reference.push(); lua.push_value(&k)?; lua.push_value(&v)?; lua_rawset(state, -3);
217 lua_pop(state, 1); }
219 Ok(())
220 }
221
222 pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
226 let lua = self.lua();
227 let state = lua.state();
228 let k = key.into_lua(&lua)?;
229 let value = unsafe {
230 self.reference.push(); lua.push_value(&k)?; lua_rawget(state, -2); let v = lua.value_from_stack(-1)?;
234 lua_pop(state, 2); v
236 };
237 V::from_lua(value, &lua)
238 }
239
240 pub fn raw_push<V: IntoLua>(&self, value: V) -> Result<()> {
244 let n = self.raw_len();
245 self.raw_set((n + 1) as i64, value)
246 }
247
248 pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
252 let lua = self.lua();
253 let n = self.raw_len();
254 if n == 0 {
255 return V::from_lua(Value::Nil, &lua);
256 }
257 if self.is_readonly() {
258 return Err(crate::error::Error::RuntimeError(
259 "attempt to modify a readonly table".to_string(),
260 ));
261 }
262 let v: V = self.raw_get(n as i64)?;
263 self.raw_set(n as i64, Value::Nil)?;
264 Ok(v)
265 }
266
267 pub fn raw_insert<V: IntoLua>(&self, idx: i64, value: V) -> Result<()> {
271 let n = self.raw_len() as i64;
272 if idx < 1 || idx > n + 1 {
273 return Err(crate::error::Error::RuntimeError(format!(
274 "bad argument #2 to 'insert' (position out of bounds): {idx}"
275 )));
276 }
277 if self.is_readonly() {
278 return Err(crate::error::Error::RuntimeError(
279 "attempt to modify a readonly table".to_string(),
280 ));
281 }
282 let mut i = n;
284 while i >= idx {
285 let moved: Value = self.raw_get(i)?;
286 self.raw_set(i + 1, moved)?;
287 i -= 1;
288 }
289 self.raw_set(idx, value.into_lua(&self.lua())?)
290 }
291
292 pub fn raw_remove(&self, idx: i64) -> Result<Value> {
295 let n = self.raw_len() as i64;
296 if n == 0 {
297 return Ok(Value::Nil);
298 }
299 if idx < 1 || idx > n {
300 return Err(crate::error::Error::RuntimeError(format!(
301 "bad argument #1 to 'remove' (position out of bounds): {idx}"
302 )));
303 }
304 if self.is_readonly() {
305 return Err(crate::error::Error::RuntimeError(
306 "attempt to modify a readonly table".to_string(),
307 ));
308 }
309 let removed: Value = self.raw_get(idx)?;
310 let mut i = idx;
311 while i < n {
312 let moved: Value = self.raw_get(i + 1)?;
313 self.raw_set(i, moved)?;
314 i += 1;
315 }
316 self.raw_set(n, Value::Nil)?;
317 Ok(removed)
318 }
319
320 pub fn push<V: IntoLua>(&self, value: V) -> Result<()> {
323 let n = self.len()?;
324 self.set((n + 1) as i64, value)
325 }
326
327 pub fn pop<V: FromLua>(&self) -> Result<V> {
330 let lua = self.lua();
331 let n = self.len()?;
332 if n == 0 {
333 return V::from_lua(Value::Nil, &lua);
334 }
335 let v: V = self.get(n as i64)?;
336 self.set(n as i64, Value::Nil)?;
337 Ok(v)
338 }
339
340 pub fn clear(&self) -> Result<()> {
343 if self.is_readonly() {
344 return Err(crate::error::Error::RuntimeError(
345 "attempt to modify a readonly table".to_string(),
346 ));
347 }
348 let lua = self.lua();
350 let state = lua.state();
351 let mut keys: Vec<Value> = Vec::new();
352 unsafe {
353 self.reference.push(); lua_pushnil(state); while lua_next(state, -2) != 0 {
356 let k = lua.value_from_stack(-2)?;
358 keys.push(k);
359 lua_pop(state, 1); }
361 lua_pop(state, 1); }
363 for k in keys {
364 self.raw_set(k, Value::Nil)?;
365 }
366 Ok(())
367 }
368
369 pub fn to_pointer(&self) -> *const std::ffi::c_void {
374 let state = self.reference.state();
375 unsafe {
376 self.reference.push();
377 let p = lua_topointer(state, -1);
378 lua_pop(state, 1);
379 p
380 }
381 }
382
383 pub fn equals(&self, other: &Table) -> Result<bool> {
386 let lua = self.lua();
387 let state = lua.state();
388 unsafe {
389 self.reference.push();
390 other.reference.push();
391 let eq = lua_equal(state, -2, -1);
392 lua_pop(state, 2);
393 Ok(eq != 0)
394 }
395 }
396
397 pub fn metatable(&self) -> Option<Table> {
399 let lua = self.lua();
400 let state = lua.state();
401 unsafe {
402 self.reference.push();
403 let has = lua_getmetatable(state, -1);
404 if has == 0 {
405 lua_pop(state, 1); return None;
407 }
408 let mt = Table::from_ref(lua.pop_ref());
410 lua_pop(state, 1); Some(mt)
412 }
413 }
414
415 pub fn set_metatable(&self, metatable: Option<Table>) -> Result<()> {
418 if self.is_readonly() {
419 return Err(crate::error::Error::RuntimeError(
420 "attempt to modify a readonly table".to_string(),
421 ));
422 }
423 let lua = self.lua();
424 let state = lua.state();
425 unsafe {
426 self.reference.push(); match metatable {
428 Some(mt) => mt.push_to_stack(),
429 None => lua_pushnil(state),
430 }
431 lua_setmetatable(state, -2);
432 lua_pop(state, 1); }
434 Ok(())
435 }
436
437 pub fn is_readonly(&self) -> bool {
442 let state = self.reference.state();
443 unsafe {
444 self.reference.push();
445 let ro = lua_getreadonly(state, -1);
446 lua_pop(state, 1);
447 ro != 0
448 }
449 }
450
451 pub fn set_readonly(&self, enabled: bool) {
454 let state = self.reference.state();
455 unsafe {
456 self.reference.push();
457 lua_setreadonly(state, -1, enabled as c_int);
458 lua_pop(state, 1);
459 }
460 }
461}
462
463pub struct TableSequence<V> {
465 table: Table,
466 index: i64,
467 _phantom: std::marker::PhantomData<V>,
468}
469
470impl<V: FromLua> Iterator for TableSequence<V> {
471 type Item = Result<V>;
472
473 fn next(&mut self) -> Option<Self::Item> {
474 let lua = self.table.lua();
475 let value: Value = match self.table.raw_get(self.index) {
477 Ok(v) => v,
478 Err(e) => return Some(Err(e)),
479 };
480 if value.is_nil() {
481 return None;
482 }
483 self.index += 1;
484 Some(V::from_lua(value, &lua))
485 }
486}
487
488impl std::fmt::Debug for Table {
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490 write!(f, "Table(len={})", self.raw_len())
491 }
492}
493
494impl PartialEq for Table {
495 fn eq(&self, other: &Self) -> bool {
496 self.to_pointer() == other.to_pointer()
499 }
500}
501
502impl<T> PartialEq<[T]> for Table
508where
509 T: FromLua + PartialEq + Clone,
510{
511 fn eq(&self, other: &[T]) -> bool {
512 let mut iter = self.sequence_values::<T>();
517 for expected in other.iter() {
518 match iter.next() {
519 Some(Ok(got)) if &got == expected => {}
520 _ => return false,
521 }
522 }
523 iter.next().is_none()
525 }
526}
527
528impl<T, const N: usize> PartialEq<[T; N]> for Table
529where
530 T: FromLua + PartialEq + Clone,
531{
532 fn eq(&self, other: &[T; N]) -> bool {
533 self == other.as_slice()
534 }
535}
536
537impl<T> PartialEq<&[T]> for Table
538where
539 T: FromLua + PartialEq + Clone,
540{
541 fn eq(&self, other: &&[T]) -> bool {
542 self == *other
543 }
544}
545
546pub struct TablePairs<K, V> {
548 table: Table,
549 next_key: Option<Value>,
550 _phantom: std::marker::PhantomData<(K, V)>,
551}
552
553impl<K: FromLua, V: FromLua> Iterator for TablePairs<K, V> {
554 type Item = Result<(K, V)>;
555
556 fn next(&mut self) -> Option<Self::Item> {
557 let key = self.next_key.take()?;
558 let lua = self.table.lua();
559 let state = lua.state();
560 unsafe {
561 self.table.reference.push(); if lua.push_value(&key).is_err() {
563 lua_pop(state, 1);
564 return None;
565 }
566 let has = lua_next(state, -2);
568 if has == 0 {
569 lua_pop(state, 1);
571 self.next_key = None;
572 return None;
573 }
574 let k_val = match lua.value_from_stack(-2) {
576 Ok(v) => v,
577 Err(e) => {
578 lua_pop(state, 3);
579 return Some(Err(e));
580 }
581 };
582 let v_val = match lua.value_from_stack(-1) {
583 Ok(v) => v,
584 Err(e) => {
585 lua_pop(state, 3);
586 return Some(Err(e));
587 }
588 };
589 self.next_key = Some(k_val.clone());
591 lua_pop(state, 3); let k = match K::from_lua(k_val, &lua) {
594 Ok(k) => k,
595 Err(e) => return Some(Err(e)),
596 };
597 let v = match V::from_lua(v_val, &lua) {
598 Ok(v) => v,
599 Err(e) => return Some(Err(e)),
600 };
601 Some(Ok((k, v)))
602 }
603 }
604}
605
606pub(crate) fn create_table(lua: &Lua) -> Table {
608 let state = lua.state();
609 unsafe {
610 lua_createtable(state, 0, 0);
611 Table::from_ref(lua.pop_ref())
612 }
613}
614
615unsafe fn c_gettable(state: *mut lua_State) -> c_int {
628 unsafe {
629 lua_gettable(state, 1);
630 1
631 }
632}
633
634unsafe fn c_settable(state: *mut lua_State) -> c_int {
636 unsafe {
637 lua_settable(state, 1);
638 0
639 }
640}
641
642unsafe fn protected_gettable(state: *mut lua_State) -> c_int {
646 unsafe {
647 lua_pushcclosurek(
649 state,
650 Some(c_gettable),
651 c"luaur-rt-gettable".as_ptr(),
652 0,
653 None,
654 );
655 lua_insert(state, -3);
656 lua_pcall(state, 2, 1, 0)
657 }
658}
659
660unsafe fn protected_settable(state: *mut lua_State) -> c_int {
663 unsafe {
664 lua_pushcclosurek(
666 state,
667 Some(c_settable),
668 c"luaur-rt-settable".as_ptr(),
669 0,
670 None,
671 );
672 lua_insert(state, -4);
673 lua_pcall(state, 3, 0, 0)
674 }
675}