use crate::{
error, ffi, values::ToString, AsLua, LuaError, LuaRead, LuaState, Nil, Push, PushGuard,
PushInto, PushOne, PushOneInto, StaticLua, Void,
};
use std::fmt::Display;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
#[macro_export]
macro_rules! function {
(@ret) => { () };
(@ret $t:ty) => { $t };
(($($p:ty),*) $(-> $r:ty)?) => {
$crate::Function<
fn($($p),*) $(-> $r)?,
($($p,)*),
function!(@ret $($r)?)
>
}
}
macro_rules! impl_function {
($name:ident, $($p:ident),*) => (
#[inline]
pub fn $name<Z, R $(, $p)*>(f: Z) -> Function<Z, ($($p,)*), R>
where
Z: FnMut($($p),*) -> R,
{
Function {
function: f,
marker: PhantomData,
}
}
)
}
impl_function!(function0,);
impl_function!(function1, A);
impl_function!(function2, A, B);
impl_function!(function3, A, B, C);
impl_function!(function4, A, B, C, D);
impl_function!(function5, A, B, C, D, E);
impl_function!(function6, A, B, C, D, E, F);
impl_function!(function7, A, B, C, D, E, F, G);
impl_function!(function8, A, B, C, D, E, F, G, H);
impl_function!(function9, A, B, C, D, E, F, G, H, I);
impl_function!(function10, A, B, C, D, E, F, G, H, I, J);
#[derive(Clone, Copy)]
pub struct Function<F, P, R> {
function: F,
marker: PhantomData<(P, R)>,
}
impl<F, P, R> std::fmt::Debug for Function<F, P, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Function({})", std::any::type_name::<F>())
}
}
impl<F, P, R> Function<F, P, R> {
pub fn new(function: F) -> Self {
Self {
function,
marker: PhantomData,
}
}
}
pub trait FnMutExt<P> {
type Output;
fn call_mut(&mut self, params: P) -> Self::Output;
}
macro_rules! impl_function_ext {
(@recur) => {};
(@recur $_head:ident $($tail:ident)*) => {
impl_function_ext!{ $($tail)* }
};
($($p:ident)*) => {
impl<Z, R $(,$p)*> FnMutExt<($($p,)*)> for Function<Z, ($($p,)*), R>
where
Z: FnMut($($p),*) -> R,
{
type Output = R;
#[allow(non_snake_case)]
#[inline]
fn call_mut(&mut self, params: ($($p,)*)) -> Self::Output {
let ($($p,)*) = params;
(self.function)($($p),*)
}
}
impl<L, Z, R $(,$p: 'static)*> PushInto<L> for Function<Z, ($($p,)*), R>
where
L: AsLua,
Z: FnMut($($p),*) -> R,
Z: 'static,
($($p,)*): for<'p> LuaRead<&'p InsideCallback>,
R: PushInto<InsideCallback> + 'static,
{
type Err = Void;
#[inline]
fn push_into_lua(self, lua: L) -> Result<PushGuard<L>, (Void, L)> {
unsafe {
let ud = ffi::lua_newuserdata(lua.as_lua(), mem::size_of::<Self>() as _);
ptr::write(ud.cast(), self);
if std::mem::needs_drop::<Self>() {
ffi::lua_newtable(lua.as_lua());
lua.as_lua().push("__gc").forget_internal();
ffi::lua_pushcfunction(lua.as_lua(), wrap_gc::<Self>);
ffi::lua_settable(lua.as_lua(), -3);
ffi::lua_setmetatable(lua.as_lua(), -2);
}
ffi::lua_pushcclosure(lua.as_lua(), wrapper::<Self, _, R>, 1);
return Ok(PushGuard::new(lua, 1));
extern "C" fn wrap_gc<T>(lua: LuaState) -> i32 {
unsafe {
let obj = ffi::lua_touserdata(lua, -1);
ptr::drop_in_place(obj.cast::<T>());
0
}
}
}
}
}
impl<L, Z, R $(,$p: 'static)*> PushOneInto<L> for Function<Z, ($($p,)*), R>
where
L: AsLua,
Z: FnMut($($p),*) -> R,
Z: 'static,
($($p,)*): for<'p> LuaRead<&'p InsideCallback>,
R: PushInto<InsideCallback> + 'static,
{
}
impl<L, Z, R $(,$p: 'static)*> Push<L> for Function<Z, ($($p,)*), R>
where
L: AsLua,
Z: FnMut($($p),*) -> R,
Self: Copy + 'static,
($($p,)*): for<'p> LuaRead<&'p InsideCallback>,
R: PushInto<InsideCallback> + 'static,
{
type Err = Void;
fn push_to_lua(&self, lua: L) -> Result<PushGuard<L>, (Void, L)> {
unsafe {
let ud = ffi::lua_newuserdata(lua.as_lua(), mem::size_of::<Self>() as _);
ptr::write(ud.cast(), *self);
ffi::lua_pushcclosure(lua.as_lua(), wrapper::<Self, _, R>, 1);
Ok(PushGuard::new(lua, 1))
}
}
}
impl<L, Z, R $(,$p: 'static)*> PushOne<L> for Function<Z, ($($p,)*), R>
where
L: AsLua,
Z: FnMut($($p),*) -> R,
Self: Copy + 'static,
($($p,)*): for<'p> LuaRead<&'p InsideCallback>,
R: PushInto<InsideCallback> + 'static,
{
}
impl_function_ext!{ @recur $($p)* }
}
}
impl_function_ext! {A B C D E F G H I J K M N}
#[derive(Debug)]
pub struct InsideCallback(LuaState);
impl AsLua for InsideCallback {
#[inline]
fn as_lua(&self) -> LuaState {
self.0
}
}
impl<T, E> PushInto<InsideCallback> for Result<T, E>
where
T: PushInto<InsideCallback>,
E: Display,
{
type Err = T::Err;
#[inline]
fn push_into_lua(
self,
lua: InsideCallback,
) -> Result<PushGuard<InsideCallback>, (T::Err, InsideCallback)> {
match self {
Ok(val) => val.push_into_lua(lua),
Err(val) => Ok(lua.push(&(Nil, val.to_string()))),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Throw<E>(pub E);
impl<E> From<E> for Throw<E> {
fn from(err: E) -> Self {
Self(err)
}
}
impl<T, E> PushInto<InsideCallback> for Result<T, Throw<E>>
where
T: PushInto<InsideCallback>,
E: Display,
{
type Err = T::Err;
#[inline]
fn push_into_lua(
self,
lua: InsideCallback,
) -> Result<PushGuard<InsideCallback>, (T::Err, InsideCallback)> {
match self {
Ok(ok) => ok.push_into_lua(lua),
Err(Throw(err)) => crate::error!(lua, "{}", err),
}
}
}
impl<T, E> PushOneInto<InsideCallback> for Result<T, E>
where
T: PushOneInto<InsideCallback>,
E: Display,
{
}
extern "C" fn wrapper<T, A, R>(lua: LuaState) -> libc::c_int
where
T: FnMutExt<A, Output = R>,
A: for<'p> LuaRead<&'p InsideCallback> + 'static,
R: PushInto<InsideCallback>,
{
let data_raw = unsafe { ffi::lua_touserdata(lua, ffi::lua_upvalueindex(1)) };
let data = unsafe { data_raw.cast::<T>().as_mut() }.expect("lua_touserdata returned NULL");
let tmp_lua = InsideCallback(lua.as_lua());
let arguments_count = unsafe { ffi::lua_gettop(lua) } as i32;
let args = A::lua_read_at_maybe_zero_position(&tmp_lua, -arguments_count);
let args = match args {
Err(lua) => {
error!(
lua,
"{}",
LuaError::wrong_type_passed::<A, _>(lua, arguments_count),
)
}
Ok(a) => a,
};
let ret_value = data.call_mut(args);
let nb = match ret_value.push_into_lua(tmp_lua) {
Ok(p) => p.forget_internal(),
Err(_) => panic!(), };
nb as _
}
#[track_caller]
pub fn protected_call<L, F, R>(lua: L, f: F) -> Result<R, LuaError>
where
L: AsLua,
F: FnOnce(StaticLua) -> R,
{
let mut ud = PCallCtx {
r#in: Some(f),
out: None,
};
let ud_ptr = &mut ud as *mut PCallCtx<_, _>;
let rc = unsafe { ffi::lua_cpcall(lua.as_lua(), trampoline::<F, R>, ud_ptr.cast()) };
match rc {
0 => {}
ffi::LUA_ERRMEM => panic!("lua_cpcall returned LUA_ERRMEM"),
ffi::LUA_ERRRUN => unsafe {
let error_msg = ToString::lua_read(PushGuard::new(lua, 1))
.ok()
.expect("can't find error message at the top of the Lua stack");
return Err(LuaError::ExecutionError(error_msg.into()));
},
rc => panic!("Unknown error code returned by lua_cpcall: {}", rc),
}
return Ok(ud.out.expect("if trampoline succeeded the value is set"));
struct PCallCtx<F, R> {
r#in: Option<F>,
out: Option<R>,
}
unsafe extern "C" fn trampoline<F, R>(l: LuaState) -> i32
where
F: FnOnce(StaticLua) -> R,
{
let ud_ptr = ffi::lua_touserdata(l, 1);
let PCallCtx { r#in, out } = ud_ptr
.cast::<PCallCtx<F, R>>()
.as_mut()
.unwrap_or_else(|| error!(l, "userdata is null"));
let f = r#in.take().expect("callback must be set by caller");
out.replace(f(StaticLua::from_static(l)));
0
}
}