use super::Thread;
use crate::context::Arg;
use crate::value::UnsafeValue;
use crate::{Fp, Nil, Object, Str, Table, Value};
use alloc::vec::Vec;
use core::marker::PhantomData;
pub struct DynamicInputs<'a, A> {
list: Vec<UnsafeValue<A>>,
phantom: PhantomData<&'a Object<A>>,
}
impl<'a, D> DynamicInputs<'a, D> {
#[inline]
pub fn with_capacity(cap: usize) -> Self {
Self {
list: Vec::with_capacity(cap),
phantom: PhantomData,
}
}
#[inline]
pub fn push_nil(&mut self) {
self.list.push(UnsafeValue::from(Nil));
}
#[inline]
pub fn push_bool(&mut self, v: bool) {
self.list.push(UnsafeValue::from(v));
}
#[inline]
pub fn push_int(&mut self, v: i64) {
self.list.push(UnsafeValue::from(v));
}
#[inline]
pub fn push_float(&mut self, v: f64) {
self.list.push(UnsafeValue::from(v));
}
#[inline]
pub fn push_str(&mut self, v: &'a Str<D>) {
self.list.push(UnsafeValue::from(v));
}
#[inline]
pub fn push_table(&mut self, v: &'a Table<D>) {
self.list.push(UnsafeValue::from(v));
}
#[inline]
pub fn push_fp(&mut self, v: Fp<D>) {
self.list.push(UnsafeValue::from(v));
}
#[inline]
pub fn push_arg<'b>(&mut self, v: Arg<'a, 'b, D>)
where
'b: 'a,
{
self.list.push(v.into());
}
}
impl<'a, D> Default for DynamicInputs<'a, D> {
fn default() -> Self {
Self {
list: Vec::new(),
phantom: PhantomData,
}
}
}
unsafe impl<'a, D> Inputs<D> for DynamicInputs<'a, D> {
#[inline(always)]
fn len(&self) -> usize {
self.list.len()
}
unsafe fn push_to(self, th: &Thread<D>) {
for (i, v) in self.list.into_iter().enumerate() {
if unsafe { (v.tt_ & 1 << 6 != 0) && (*v.value_.gc).global != th.hdr.global } {
panic!("argument #{i} come from a different Lua");
}
unsafe { th.top.write(v) };
unsafe { th.top.add(1) };
}
}
}
pub unsafe trait Inputs<D> {
fn len(&self) -> usize;
unsafe fn push_to(self, th: &Thread<D>);
}
unsafe impl<D> Inputs<D> for () {
#[inline(always)]
fn len(&self) -> usize {
0
}
#[inline(always)]
unsafe fn push_to(self, _: &Thread<D>) {}
}
unsafe impl<T: Into<UnsafeValue<D>>, D> Inputs<D> for T {
#[inline(always)]
fn len(&self) -> usize {
1
}
unsafe fn push_to(self, th: &Thread<D>) {
let v = self.into();
if unsafe { (v.tt_ & 1 << 6 != 0) && (*v.value_.gc).global != th.hdr.global } {
panic!("argument #0 come from a different Lua");
}
unsafe { th.top.write(v) };
unsafe { th.top.add(1) };
}
}
unsafe impl<'a, A> Inputs<A> for Vec<Value<'a, A>> {
#[inline(always)]
fn len(&self) -> usize {
self.len()
}
unsafe fn push_to(self, th: &Thread<A>) {
for (i, v) in self.into_iter().enumerate() {
let v = UnsafeValue::from(v);
if unsafe { (v.tt_ & 1 << 6 != 0) && (*v.value_.gc).global != th.hdr.global } {
panic!("argument #{i} was created from different Lua");
}
unsafe { th.top.write(v) };
unsafe { th.top.add(1) };
}
}
}
macro_rules! count {
() => (0usize);
($x:tt,$($xs:tt)*) => (1usize + count!($($xs)*));
}
macro_rules! impl_tuple {
($($idx:tt $name:tt),+) => {
unsafe impl<Z, $($name,)+> Inputs<Z> for ($($name,)+)
where
$($name: Into<UnsafeValue<Z>>,)+
{
#[inline(always)]
fn len(&self) -> usize {
count!($($name,)+)
}
unsafe fn push_to(self, th: &Thread<Z>) {
$({
let v = self.$idx.into();
if unsafe { (v.tt_ & 1 << 6 != 0) && (*v.value_.gc).global != th.hdr.global } {
panic!("argument #{} come from a different Lua", $idx);
}
unsafe { th.top.write(v) };
unsafe { th.top.add(1) };
})+
}
}
};
}
impl_tuple!(0 A, 1 B);
impl_tuple!(0 A, 1 B, 2 C);
impl_tuple!(0 A, 1 B, 2 C, 3 D);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J);