use std::any::{type_name, Any, TypeId}; use std::collections::HashMap; use std::panic::{RefUnwindSafe, UnwindSafe}; use seq_macro::seq; use crate::component::Component; use crate::lock::Lock; use crate::system::util::check_params_are_compatible; use crate::system::{ComponentRefMut, Into as IntoSystem, Param, System, TypeErased}; use crate::tuple::{ IntoInOptions as TupleIntoInOptions, Reduce as TupleReduce, ReduceNoOp as TupleReduceNoOp, TakeOptionElementResult as TupleTakeOptionElementResult, WithOptionElements as TupleWithOptionElements, }; use crate::WorldData; /// A stateful system. pub struct Stateful { func: Func, local_components: HashMap>>, } macro_rules! impl_system { ($c: tt) => { seq!(I in 0..$c { impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)> for Stateful where Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, #(TParam~I: Param<'world>,)* #(TParam~I::Input: 'static,)* (#(TParam~I::Input,)*): TupleReduce, <(#(TParam~I::Input,)*) as TupleReduce>::Out: TupleIntoInOptions { type Input = <(#(TParam~I::Input,)*) as TupleReduce>::Out; fn initialize(mut self, input: Self::Input) -> Self { let mut option_input = input.into_in_options(); #( if TypeId::of::() != TypeId::of::() { let input = match option_input.take::() { TupleTakeOptionElementResult::Found(input) => input, TupleTakeOptionElementResult::NotFound => { panic!( "Parameter input {} not found", type_name::() ); } TupleTakeOptionElementResult::AlreadyTaken => { panic!( concat!( "Parameter {} is already initialized. ", "System cannot contain multiple inputs with ", "the same type", ), type_name::() ); } }; TParam~I::initialize( &mut self, input ); } )* self } fn run<'this>(&'this self, world_data: &'world WorldData) where 'this: 'world { #( check_params_are_compatible!(I, TParam~I, $c); )* let func = self.func; func(#({ TParam~I::new(self, world_data) },)*); } fn into_type_erased(self) -> TypeErased { TypeErased { data: Box::new(self), func: Box::new(|data, world_data| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let data = unsafe { &*(data as *const dyn Any) }; let me = data.downcast_ref::().unwrap(); // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world_data = unsafe { &*(world_data as *const WorldData) }; me.run(world_data); }), } } fn get_local_component_mut( &self, ) -> Option> { let local_component = self.local_components .get(&TypeId::of::())? .write_nonblock() .expect("Failed to aquire read-write local component lock"); Some(ComponentRefMut::new(local_component)) } fn set_local_component( &mut self, local_component: LocalComponent, ) { self.local_components .insert( TypeId::of::(), Lock::new(Box::new(local_component)) ); } } impl IntoSystem for Func where Func: Fn(#(TParam~I,)*) + Copy + 'static, { type System = Stateful; fn into_system(self) -> Self::System { Self::System { func: self, local_components: HashMap::new(), } } } }); }; } seq!(C in 1..16 { impl_system!(C); });