use std::marker::PhantomData; use futures::future::try_join_all; use lua_typed::Typed; use mlua::{FromLua, IntoLuaMulti}; #[derive(Debug, Clone)] pub struct ActionCallback

{ callbacks: Vec, _parameters: PhantomData

, } impl Typed for ActionCallback<()> { fn type_name() -> String { "fun() | fun()[]".into() } } impl Typed for ActionCallback { fn type_name() -> String { let type_name = A::type_name(); format!("fun(_: {type_name}) | fun(_: {type_name})[]") } } impl Typed for ActionCallback<(A, B)> { fn type_name() -> String { let type_name_a = A::type_name(); let type_name_b = B::type_name(); format!( "fun(_: {type_name_a}, _: {type_name_b}) | fun(_: {type_name_a}, _: {type_name_b})[]" ) } } // NOTE: For some reason the derive macro combined with PhantomData leads to issues where it // requires all types part of P to implement default, even if they never actually get constructed. // By manually implemented Default it works fine. impl

Default for ActionCallback

{ fn default() -> Self { Self { callbacks: Default::default(), _parameters: Default::default(), } } } impl

FromLua for ActionCallback

{ fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result { let callbacks = match value { mlua::Value::Function(f) => vec![f], mlua::Value::Table(table) => table .pairs::() .map(|pair| { let (_, f) = pair?; Ok::<_, mlua::Error>(f) }) .try_collect()?, _ => { return Err(mlua::Error::FromLuaConversionError { from: value.type_name(), to: "ActionCallback".into(), message: Some("expected function or table of functions".into()), }); } }; Ok(ActionCallback { callbacks, _parameters: PhantomData::

, }) } } // TODO: Return proper error here impl

ActionCallback

where P: IntoLuaMulti + Sync + Clone, { pub async fn call(&self, parameters: P) { try_join_all( self.callbacks .iter() .map(async |f| f.call_async::<()>(parameters.clone()).await), ) .await .unwrap(); } pub fn is_empty(&self) -> bool { self.callbacks.is_empty() } }