tlua/
tuples.rs

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    ////////////////////////////////////////////////////////////////////////////
14    // single element
15    ////////////////////////////////////////////////////////////////////////////
16    ($ty:ident) => {
17
18        ////////////////////////////////////////////////////////////////////////
19        // Push
20        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        ////////////////////////////////////////////////////////////////////////
45        // PushInto
46        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        ////////////////////////////////////////////////////////////////////////
71        // LuaRead
72        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        ////////////////////////////////////////////////////////////////////////
93        // AsTable
94        ////////////////////////////////////////////////////////////////////////
95
96        ////////////////////////////////////////////////////////////////////////
97        // Push
98        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        ////////////////////////////////////////////////////////////////////////
121        // PushInto
122        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        ////////////////////////////////////////////////////////////////////////
145        // LuaRead
146        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    ////////////////////////////////////////////////////////////////////////////
165    // multiple elements
166    ////////////////////////////////////////////////////////////////////////////
167    ($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                                // TODO(gmoshkin): return an error capturing the
197                                // previously created pushguards so that the
198                                // caller can handle partially pushed tuples
199                                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                            // TODO(gmoshkin): return an error capturing the
237                            // first_pushed pushguard so that the caller can
238                            // handle partially pushed tuples
239                            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        // TODO: what if T or U are also tuples? indices won't match
254        #[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                // TODO(gmoshkin): + n_values_expected
284                // see comment below
285                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                    // The 0 index is the special case that should not be walked
297                    // over. Either we return Err on it or we handle the
298                    // situation correctly (e.g. Option<T>, (), ...)
299                    // TODO(gmoshkin): + n_values_expected but make sure not to
300                    // ignore going over 0
301                    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        ////////////////////////////////////////////////////////////////////////
322        // AsTable
323        ////////////////////////////////////////////////////////////////////////
324
325        ////////////////////////////////////////////////////////////////////////
326        // Push
327        #[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        ////////////////////////////////////////////////////////////////////////
380        // PushInto
381        #[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        ////////////////////////////////////////////////////////////////////////
434        // LuaRead
435        #[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/// Error that can happen when pushing multiple values at once.
488// TODO: implement Error on that thing
489#[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)]
568/// A wrapper type for pushing and reading rust tuples as lua tables.
569///
570/// Can also be constructed more comfortably using the [`as_table!`] macro.
571///
572/// Useful when working heterogeneous lua tables.
573/// ```no_run
574/// use tlua::{Lua, AsTable, AnyLuaValue::{LuaNumber, LuaString, LuaBoolean}};
575///
576/// let lua = Lua::new();
577/// lua.checked_set("x", AsTable((true, "two", 3))).unwrap();
578///
579/// assert_eq!(
580///     lua.get("x"),
581///     Some([LuaBoolean(true), LuaString("two".into()), LuaNumber(3.0)]),
582/// );
583/// assert_eq!(lua.get("x"), Some(AsTable((true, "two".to_string(), 3))));
584/// ```
585/// [`as_table!`]: crate::as_table
586pub struct AsTable<T>(pub T);
587
588////////////////////////////////////////////////////////////////////////////////
589// AsTablePushError
590////////////////////////////////////////////////////////////////////////////////
591
592/// An error that can happen during an attempt to push a rust tuple as a lua
593/// table (see [`AsTable`]).
594#[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
650////////////////////////////////////////////////////////////////////////////////
651// push_table_entry
652////////////////////////////////////////////////////////////////////////////////
653
654/// Push a single `table` entry. This function assumes a table is located at the
655/// top of the stack (index `-1`).
656///
657/// Depending on how many values are pushed onto the stack when
658/// `T::push_into_lua` is called the following can happen:
659/// - `0` values => nothing happens
660/// - `1` value: `v` => `table[i] = v`
661/// - `2` values: `k` & `v` => `table[k] = v`
662/// - any other number => nothing is inserted into table, `AsTablePushError::TooManyValues(n)` is returned
663///
664/// If an error happens during attempt to push `T`,
665/// `AsTablePushError::ValuePushError(e)` is returned
666fn 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            // swap index and value
679            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/// A helper macro for creating [`AsTable`] wrappers.
694/// Use this to pass ad-hoc lua tables into lua.
695///
696/// # Example
697/// ```no_run
698/// # fn extra_key() {}
699/// # fn extra_value() {}
700/// use tlua::as_table;
701/// let lua = tlua::Lua::new();
702///
703/// lua.exec_with(
704///     "set_configuration(...)",
705///     as_table! {
706///         "log" => "my-log-file.txt",
707///         "fruit" => as_table! { "apple", "banana", "orange" },
708///         extra_key() => extra_value(),
709///         420 => "secret-number-field",
710///     }
711/// )
712/// .unwrap();
713///
714/// lua.exec_with(
715///     "set_http_request_handler",
716///     tlua::Function::new(|| -> _ {
717///         as_table! {
718///             "status" => 200,
719///             "headers" => as_table! {
720///                 "content-type" => "application/json",
721///             },
722///             "body" => r#"{"error":"request-failed"}"#
723///         }
724///     })
725/// )
726/// .unwrap();
727/// ```
728#[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}