use std::ffi::CStr; use crate::{LuaRef, FromLuaStack, State, PushToLuaStack}; use mlua_sys as lua; pub struct Function<'a> { state: &'a State, lref: LuaRef } impl<'a> FromLuaStack<'a> for Function<'a> { unsafe fn from_lua_stack(state: &'a crate::State) -> crate::Result { Ok(Self { state, lref: LuaRef::from_stack(state)?, }) } } impl<'a> PushToLuaStack for Function<'a> { unsafe fn push_to_lua_stack(&self, state: &State) -> crate::Result<()> { self.lref.push_to_lua_stack(state) } } impl<'a> Function<'a> { pub fn exec(&self, args: A) -> crate::Result where A: FunctionArgs, R: FunctionResults<'a>, { unsafe { self.push_to_lua_stack(self.state)?; args.push_args_to_lua_stack(self.state)?; let arg_len = args.len() as i32; let res_len = R::len() as i32; let s = self.state.state_ptr(); if lua::lua_pcall(s, arg_len, res_len, 0) != 0 { let s = lua::lua_tostring(s, -1); let s = CStr::from_ptr(s); let s = s.to_str().unwrap(); return Err(crate::Error::runtime(s)); } R::results_from_lua_stack(self.state) } } } pub trait FunctionArgs { fn len(&self) -> usize; fn push_args_to_lua_stack(&self, state: &State) -> crate::Result<()>; } impl<'a, T> FunctionArgs for T where T: PushToLuaStack, { fn len(&self) -> usize { 1 } fn push_args_to_lua_stack(&self, state: &State) -> crate::Result<()> { unsafe { self.push_to_lua_stack(state)?; } Ok(()) } } pub trait FunctionResults<'a>: Sized { fn len() -> usize; fn results_from_lua_stack(state: &'a State) -> crate::Result; } impl<'a> FunctionResults<'a> for () { fn len() -> usize { 0 } fn results_from_lua_stack(_state: &'a State) -> crate::Result { Ok(()) } } impl<'a, T: FromLuaStack<'a>> FunctionResults<'a> for T { fn len() -> usize { 1 } fn results_from_lua_stack(state: &'a State) -> crate::Result { unsafe { T::from_lua_stack(state) } } } macro_rules! impl_function_arg_tuple { ( $count: expr, $first: tt, $( $name: tt ),+ ) => ( #[allow(non_snake_case)] impl<$first: PushToLuaStack, $($name: PushToLuaStack,)+> FunctionArgs for ($first, $($name,)+) { fn len(&self) -> usize { // this will end up generating $count - 1 - 1 - 1... hopefully the compiler will // optimize that out $count } fn push_args_to_lua_stack(&self, state: &State) -> crate::Result<()> { let ($first, $($name,)+) = self; unsafe { $first.push_to_lua_stack(state)?; $( $name.push_to_lua_stack(state)?; )+ } Ok(()) } } impl<'a, $first: FromLuaStack<'a>, $($name: FromLuaStack<'a>,)+> FunctionResults<'a> for ($first, $($name,)+) { fn len() -> usize { $count } fn results_from_lua_stack(state: &'a State) -> crate::Result { Ok(unsafe { ( $first::from_lua_stack(state)?, $( $name::from_lua_stack(state)?, )+ ) }) } } impl_function_arg_tuple!( $count - 1, $( $name ),+ ); ); // implements FunctionArgs and FunctionResults for a tuple with a single element ( $count: expr, $only: tt ) => { #[allow(non_snake_case)] impl<$only: PushToLuaStack> FunctionArgs for ($only,) { fn len(&self) -> usize { 1 } fn push_args_to_lua_stack(&self, state: &State) -> crate::Result<()> { let (a,) = self; unsafe { a.push_to_lua_stack(state)?; } Ok(()) } } impl<'a, $only: FromLuaStack<'a>> FunctionResults<'a> for ($only,) { fn len() -> usize { 1 } fn results_from_lua_stack(state: &'a State) -> crate::Result { Ok(unsafe { ( $only::from_lua_stack(state)?, ) }) } } }; } // hopefully 16 is more than enough // if you have 16 function results, and need more than 16, you NEED help impl_function_arg_tuple! { 16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16 }