1use std::fmt::{self, Debug};
2use std::num::NonZeroI32;
3
4use crate::{
5 ffi,
6 object::{Index, Indexable, Object},
7 rust_tables::{push_iter, PushIterError},
8 AsLua, LuaError, LuaRead, LuaState, Push, PushGuard, PushInto, PushOne, PushOneInto,
9 ReadResult, Void, WrongType,
10};
11
12macro_rules! tuple_impl {
13 ($ty:ident) => {
17
18 impl<LU, $ty> Push<LU> for ($ty,)
21 where
22 LU: AsLua,
23 $ty: Push<LU>,
24 {
25 type Err = TuplePushError<
26 <$ty as Push<LU>>::Err,
27 Void,
28 >;
29
30 #[inline]
31 fn push_to_lua(&self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
32 self.0.push_to_lua(lua)
33 .map_err(|(e, l)| (TuplePushError::First(e), l))
34 }
35 }
36
37 impl<LU, $ty> PushOne<LU> for ($ty,)
38 where
39 LU: AsLua,
40 $ty: PushOne<LU>,
41 {
42 }
43
44 impl<LU, $ty> PushInto<LU> for ($ty,)
47 where
48 LU: AsLua,
49 $ty: PushInto<LU>,
50 {
51 type Err = TuplePushError<
52 <$ty as PushInto<LU>>::Err,
53 Void,
54 >;
55
56 #[inline]
57 fn push_into_lua(self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
58 self.0.push_into_lua(lua)
59 .map_err(|(e, l)| (TuplePushError::First(e), l))
60 }
61 }
62
63 impl<LU, $ty> PushOneInto<LU> for ($ty,)
64 where
65 LU: AsLua,
66 $ty: PushOneInto<LU>,
67 {
68 }
69
70 impl<LU, $ty> LuaRead<LU> for ($ty,)
73 where
74 LU: AsLua,
75 $ty: LuaRead<LU>,
76 {
77 fn n_values_expected() -> i32 {
78 $ty::n_values_expected()
79 }
80
81 #[inline]
82 fn lua_read_at_position(lua: LU, index: NonZeroI32) -> ReadResult<($ty,), LU> {
83 LuaRead::lua_read_at_position(lua, index).map(|v| (v,))
84 }
85
86 #[inline]
87 fn lua_read_at_maybe_zero_position(lua: LU, index: i32) -> ReadResult<($ty,), LU> {
88 LuaRead::lua_read_at_maybe_zero_position(lua, index).map(|v| (v,))
89 }
90 }
91
92 impl<LU, $ty> Push<LU> for AsTable<($ty,)>
99 where
100 LU: AsLua,
101 $ty: Push<LuaState>,
102 {
103 type Err = AsTablePushError<TuplePushError<$ty::Err, Void>>;
104
105 #[inline]
106 fn push_to_lua(&self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
107 push_iter(lua, std::iter::once(&self.0.0))
108 .map_err(|(e, l)| (e.map(TuplePushError::First), l))
109 .map_err(|(e, l)| (e.into(), l))
110 }
111 }
112
113 impl<LU, $ty> PushOne<LU> for AsTable<($ty,)>
114 where
115 LU: AsLua,
116 $ty: PushOne<LuaState>,
117 {
118 }
119
120 impl<LU, $ty> PushInto<LU> for AsTable<($ty,)>
123 where
124 LU: AsLua,
125 $ty: PushInto<LuaState>,
126 {
127 type Err = AsTablePushError<TuplePushError<$ty::Err, Void>>;
128
129 #[inline]
130 fn push_into_lua(self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
131 push_iter(lua, std::iter::once(self.0.0))
132 .map_err(|(e, l)| (e.map(TuplePushError::First), l))
133 .map_err(|(e, l)| (e.into(), l))
134 }
135 }
136
137 impl<LU, $ty> PushOneInto<LU> for AsTable<($ty,)>
138 where
139 LU: AsLua,
140 $ty: PushOneInto<LuaState>,
141 {
142 }
143
144 impl<LU, $ty> LuaRead<LU> for AsTable<($ty,)>
147 where
148 LU: AsLua,
149 $ty: for<'a> LuaRead<PushGuard<&'a LU>>,
150 {
151 #[inline]
152 fn lua_read_at_position(lua: LU, index: NonZeroI32) -> ReadResult<Self, LU> {
153 let table = Indexable::lua_read_at_position(lua, index)?;
154 match table.try_get(1) {
155 Ok(res) => Ok(AsTable(res)),
156 Err(err) => {
157 convert_as_table_read_error::<Self, _>(table, 1, err)
158 },
159 }
160 }
161 }
162 };
163
164 ($first:ident, $($other:ident),+) => {
168 #[allow(non_snake_case)]
169 impl<LU, $first, $($other),+> Push<LU> for ($first, $($other),+)
170 where
171 LU: AsLua,
172 $first: Push<LuaState>,
173 $( $other: Push<LuaState>, )+
174 {
175 type Err = TuplePushError<
176 <$first as Push<LuaState>>::Err,
177 <($($other,)+) as Push<LuaState>>::Err,
178 >;
179
180 #[inline]
181 fn push_to_lua(&self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
182 use TuplePushError::{First, Other};
183 match self {
184 ($first, $($other),+) => {
185 let error = |e| e;
186 let pushed = match lua.as_lua().try_push($first) {
187 Ok(pushed) => pushed,
188 Err((err, _)) => return Err((error(First(err)), lua)),
189 };
190 let total = move || pushed.forget_internal();
191
192 $(
193 let error = |e| error(Other(e));
194 let pushed = match lua.as_lua().try_push($other) {
195 Ok(pushed) => pushed,
196 Err((err, _)) => return Err((error(First(err)), lua)),
200 };
201 let total = move || pushed.forget_internal() + total();
202 )+
203
204 unsafe {
205 Ok(PushGuard::new(lua, total()))
206 }
207 }
208 }
209 }
210 }
211
212 #[allow(non_snake_case)]
213 impl<LU, $first, $($other),+> PushInto<LU> for ($first, $($other),+)
214 where
215 LU: AsLua,
216 $first: PushInto<LuaState>,
217 $( $other: PushInto<LuaState>, )+
218 {
219 type Err = TuplePushError<
220 <$first as PushInto<LuaState>>::Err,
221 <($($other,)+) as PushInto<LuaState>>::Err,
222 >;
223
224 #[inline]
225 fn push_into_lua(self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
226 use TuplePushError::{First, Other};
227 match self {
228 ($first, $($other),+) => {
229 let first_pushed = match lua.as_lua().try_push($first) {
230 Ok(pushed) => pushed,
231 Err((err, _)) => return Err((First(err), lua)),
232 };
233
234 let other_pushed = match lua.as_lua().try_push(($($other,)+)) {
235 Ok(pushed) => pushed,
236 Err((err, _)) => return Err((Other(err), lua)),
240 };
241
242 let total = first_pushed.forget_internal()
243 + other_pushed.forget_internal();
244
245 unsafe {
246 Ok(PushGuard::new(lua, total))
247 }
248 }
249 }
250 }
251 }
252
253 #[allow(unused_assignments)]
255 #[allow(non_snake_case)]
256 impl<LU, $first, $($other),+> LuaRead<LU> for ($first, $($other),+)
257 where
258 LU: AsLua,
259 $first: for<'a> LuaRead<&'a LU>,
260 $($other: for<'a> LuaRead<&'a LU>),+
261 {
262 #[inline(always)]
263 fn n_values_expected() -> i32 {
264 $first::n_values_expected() $( + $other::n_values_expected() )+
265 }
266
267 #[inline]
268 fn lua_read_at_position(lua: LU, index: NonZeroI32) -> ReadResult<($first, $($other),+), LU> {
269 LuaRead::lua_read_at_maybe_zero_position(lua, index.into())
270 }
271
272 #[inline]
273 fn lua_read_at_maybe_zero_position(lua: LU, index: i32) -> ReadResult<($first, $($other),+), LU> {
274 let mut tuple_i = 0;
275 let $first: $first = match LuaRead::lua_read_at_maybe_zero_position(&lua, index) {
276 Ok(v) => v,
277 Err((_, e)) => {
278 return Err(on_error::<$first, _>(lua, tuple_i, index, e));
279 }
280 };
281
282 let mut i = index;
283 i = if i == 0 { 0 } else { i + 1 };
286
287 $(
288 tuple_i += 1;
289
290 let $other: $other = match LuaRead::lua_read_at_maybe_zero_position(&lua, i) {
291 Ok(v) => v,
292 Err((_, e)) => {
293 return Err(on_error::<$other, _>(lua, tuple_i, i, e));
294 }
295 };
296 i = if i == 0 { 0 } else { i + 1 };
302 )+
303
304 return Ok(($first, $($other),+));
305
306 fn on_error<T, LU: AsLua>(lua: LU, tuple_i: i32, lua_i: i32, e: WrongType) -> (LU, WrongType) {
307 let mut e = WrongType::info("reading one of multiple values")
308 .expected(format!("{} at index {} (1-based)", std::any::type_name::<T>(), tuple_i + 1))
309 .subtype(e);
310
311 if lua_i != 0 {
312 e = e.actual("incorrect value")
313 } else {
314 e = e.actual("no value")
315 }
316 (lua, e)
317 }
318 }
319 }
320
321 #[allow(non_snake_case)]
328 impl<LU, $first, $($other),+> Push<LU> for AsTable<($first, $($other),+)>
329 where
330 LU: AsLua,
331 $first: Push<LuaState>,
332 $( $other: Push<LuaState>, )+
333 {
334 type Err = AsTablePushError<
335 TuplePushError<
336 $first::Err,
337 <($($other,)+) as Push<LuaState>>::Err,
338 >
339 >;
340
341 #[inline]
342 fn push_to_lua(&self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
343 use TuplePushError::{First, Other};
344
345 let raw_lua = lua.as_lua();
346 let table = unsafe {
347 ffi::lua_newtable(raw_lua);
348 PushGuard::new(lua, 1)
349 };
350
351 let Self(($first, $($other),+)) = self;
352 let i = 1;
353 let tuple_error = |e| e;
354 if let Err(e) = push_table_entry(raw_lua, i, $first) {
355 return Err((e.map(First).map(tuple_error), table.into_inner()))
356 }
357
358 $(
359 let i = i + 1;
360 let tuple_error = |e| tuple_error(Other(e));
361 if let Err(e) = push_table_entry(raw_lua, i, $other) {
362 return Err((e.map(First).map(tuple_error), table.into_inner()))
363 }
364 )+
365
366 Ok(table)
367 }
368 }
369
370 #[allow(non_snake_case)]
371 impl<LU, $first, $($other),+> PushOne<LU> for AsTable<($first, $($other),+)>
372 where
373 LU: AsLua,
374 $first: Push<LuaState>,
375 $( $other: Push<LuaState>, )+
376 {
377 }
378
379 #[allow(non_snake_case)]
382 impl<LU, $first, $($other),+> PushInto<LU> for AsTable<($first, $($other),+)>
383 where
384 LU: AsLua,
385 $first: PushInto<LuaState>,
386 $( $other: PushInto<LuaState>, )+
387 {
388 type Err = AsTablePushError<
389 TuplePushError<
390 $first::Err,
391 <($($other,)+) as PushInto<LuaState>>::Err,
392 >
393 >;
394
395 #[inline]
396 fn push_into_lua(self, lua: LU) -> Result<PushGuard<LU>, (Self::Err, LU)> {
397 use TuplePushError::{First, Other};
398
399 let raw_lua = lua.as_lua();
400 let table = unsafe {
401 ffi::lua_newtable(raw_lua);
402 PushGuard::new(lua, 1)
403 };
404
405 let Self(($first, $($other),+)) = self;
406 let i = 1;
407 let tuple_error = |e| e;
408 if let Err(e) = push_table_entry(raw_lua, i, $first) {
409 return Err((e.map(First).map(tuple_error), table.into_inner()))
410 }
411
412 $(
413 let i = i + 1;
414 let tuple_error = |e| tuple_error(Other(e));
415 if let Err(e) = push_table_entry(raw_lua, i, $other) {
416 return Err((e.map(First).map(tuple_error), table.into_inner()))
417 }
418 )+
419
420 Ok(table)
421 }
422 }
423
424 #[allow(non_snake_case)]
425 impl<LU, $first, $($other),+> PushOneInto<LU> for AsTable<($first, $($other),+)>
426 where
427 LU: AsLua,
428 $first: PushInto<LuaState>,
429 $( $other: PushInto<LuaState>, )+
430 {
431 }
432
433 #[allow(non_snake_case)]
436 impl<LU, $first, $($other),+> LuaRead<LU> for AsTable<($first, $($other),+)>
437 where
438 LU: AsLua,
439 $first: for<'a> LuaRead<PushGuard<&'a LU>>,
440 $($other: for<'a> LuaRead<PushGuard<&'a LU>>),+
441 {
442 #[inline]
443 fn lua_read_at_position(lua: LU, index: NonZeroI32) -> ReadResult<Self, LU> {
444 let table = Indexable::lua_read_at_position(lua, index)?;
445 let i = 1;
446 let $first = match table.try_get(i) {
447 Ok(res) => res,
448 Err(err) => return convert_as_table_read_error::<Self, _>(table, i, err),
449 };
450
451 $(
452 let i = i + 1;
453 let $other = match table.try_get(i) {
454 Ok(res) => res,
455 Err(err) => return convert_as_table_read_error::<Self, _>(table, i, err),
456 };
457 )+
458
459 return Ok(AsTable(($first, $($other),+)));
460 }
461 }
462
463 tuple_impl!{ $($other),+ }
464 };
465}
466
467tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
468
469fn convert_as_table_read_error<T, L>(table: Indexable<L>, i: i32, err: LuaError) -> ReadResult<T, L>
470where
471 L: AsLua,
472{
473 let g = Object::from(table).into_guard();
474 let e = WrongType::info("reading Lua table").expected_type::<T>();
475 let e = match err {
476 LuaError::WrongType(subtype) => e
477 .actual(format!("table with wrong value at index {}", i))
478 .subtype(subtype),
479 other_err => e.actual(format!(
480 "error in meta method for index {}: {}",
481 i, other_err,
482 )),
483 };
484 Err((g, e))
485}
486
487#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
490pub enum TuplePushError<C, O> {
491 First(C),
492 Other(O),
493}
494
495impl<F, O> TuplePushError<F, O> {
496 pub fn first(self) -> F
497 where
498 O: Into<Void>,
499 {
500 match self {
501 Self::First(f) => f,
502 Self::Other(_) => unreachable!("no way to construct an instance of Void"),
503 }
504 }
505
506 pub fn other(self) -> O
507 where
508 F: Into<Void>,
509 {
510 match self {
511 Self::First(_) => unreachable!("no way to construct an instance of Void"),
512 Self::Other(o) => o,
513 }
514 }
515}
516
517impl<H, T> fmt::Display for TuplePushError<H, T>
518where
519 H: fmt::Display,
520 T: fmt::Display,
521{
522 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
523 write!(
524 f,
525 "Error during attempt to push multiple values: ({}, ...)",
526 TuplePushErrorDisplayHelper(self),
527 )
528 }
529}
530
531struct TuplePushErrorDisplayHelper<'a, H, T>(&'a TuplePushError<H, T>);
532
533impl<H, T> fmt::Display for TuplePushErrorDisplayHelper<'_, H, T>
534where
535 H: fmt::Display,
536 T: fmt::Display,
537{
538 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
539 match self.0 {
540 TuplePushError::First(head) => write!(f, "{}", head),
541 TuplePushError::Other(tail) => write!(f, "ok, {}", tail),
542 }
543 }
544}
545
546macro_rules! impl_tuple_push_error {
547 [@t] => { Void };
548 [@t $h:tt $($t:tt)*] => { TuplePushError<$h, impl_tuple_push_error![@t $($t)*]> };
549 () => {};
550 ($h:tt $($t:tt)*) => {
551 impl<$h, $($t,)*> From<impl_tuple_push_error![@t $h $($t)*]> for Void
552 where
553 $h: Into<Void>,
554 $( $t: Into<Void>, )*
555 {
556 #[inline]
557 fn from(_: impl_tuple_push_error![@t $h $($t)*]) -> Void {
558 unreachable!("There's no way to create an instance of Void")
559 }
560 }
561 impl_tuple_push_error!{ $($t)* }
562 };
563}
564
565impl_tuple_push_error! {A B C D E F G H I J K L M}
566
567#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
568pub struct AsTable<T>(pub T);
587
588#[derive(Debug, PartialEq, Eq)]
595pub enum AsTablePushError<E> {
596 TooManyValues(i32),
597 ValuePushError(E),
598}
599
600impl<E> AsTablePushError<E> {
601 pub fn map<F, R>(self, f: F) -> AsTablePushError<R>
602 where
603 F: FnOnce(E) -> R,
604 {
605 match self {
606 Self::ValuePushError(e) => AsTablePushError::ValuePushError(f(e)),
607 Self::TooManyValues(n) => AsTablePushError::TooManyValues(n),
608 }
609 }
610}
611
612impl<E> fmt::Display for AsTablePushError<E>
613where
614 E: fmt::Display,
615{
616 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
617 match self {
618 Self::TooManyValues(n) => {
619 write!(
620 fmt,
621 "Can only push 1 or 2 values as lua table item, got {} instead",
622 n,
623 )
624 }
625 Self::ValuePushError(e) => {
626 write!(fmt, "Pushing iterable item failed: {}", e)
627 }
628 }
629 }
630}
631
632impl<V> From<AsTablePushError<V>> for Void
633where
634 Void: From<V>,
635{
636 fn from(_: AsTablePushError<V>) -> Void {
637 unreachable!("value of Void cannot be created")
638 }
639}
640
641impl<E> From<PushIterError<E>> for AsTablePushError<E> {
642 fn from(e: PushIterError<E>) -> Self {
643 match e {
644 PushIterError::TooManyValues(n) => Self::TooManyValues(n),
645 PushIterError::ValuePushError(e) => Self::ValuePushError(e),
646 }
647 }
648}
649
650fn push_table_entry<T>(raw_lua: LuaState, i: i32, v: T) -> Result<(), AsTablePushError<T::Err>>
667where
668 T: PushInto<LuaState>,
669{
670 let n_pushed = match raw_lua.try_push(v) {
671 Ok(pushed) => pushed.forget_internal(),
672 Err((e, _)) => return Err(AsTablePushError::ValuePushError(e)),
673 };
674 match n_pushed {
675 0 => {}
676 1 => unsafe {
677 raw_lua.push_one(i).forget();
678 ffi::lua_insert(raw_lua, -2);
680 ffi::lua_settable(raw_lua, -3);
681 },
682 2 => unsafe {
683 ffi::lua_settable(raw_lua, -3);
684 },
685 n => unsafe {
686 drop(PushGuard::new(raw_lua, n));
687 return Err(AsTablePushError::TooManyValues(n));
688 },
689 }
690 Ok(())
691}
692
693#[macro_export]
729macro_rules! as_table {
730 ($( $key:expr => $value:expr ),* $(,)?) => {
731 $crate::AsTable( ( $( ($key, $value), )* ) )
732 };
733 ($( $item:expr ),* $(,)?) => {
734 $crate::AsTable( ( $( $item, )* ) )
735 }
736}
737
738#[cfg(feature = "internal_test")]
739mod test {
740 #[crate::test]
741 fn as_table_macro() {
742 let key = "KEY";
743 let value = [1, 2, 3];
744 let t = as_table! {
745 "foo" => "bar",
746 key => value,
747 "sequence-table" => as_table! { 1, "two", 3.0 },
748 };
749
750 let lua = crate::Lua::new();
751 let table: crate::LuaTable<_> = lua.eval_with("return ...", t).unwrap();
752
753 let v: String = table.get("foo").unwrap();
754 assert_eq!(v, "bar");
755
756 let v: [u32; 3] = table.get("KEY").unwrap();
757 assert_eq!(v, [1, 2, 3]);
758
759 {
760 let t: crate::LuaTable<_> = table.get("sequence-table").unwrap();
761 let v: u32 = t.get(1).unwrap();
762 assert_eq!(v, 1);
763
764 let v: String = t.get(2).unwrap();
765 assert_eq!(v, "two");
766
767 let v: f32 = t.get(3).unwrap();
768 assert_eq!(v, 3.0);
769 }
770 }
771}