use std::any::{type_name, TypeId}; use std::collections::HashMap; use std::ptr::addr_of_mut; use seq_macro::seq; use crate::component::Component; use crate::system::util::check_params_are_compatible; use crate::system::{ FilteredInputs, InputFilter, Into as IntoSystem, OptionInputs, Param, System, TakeOptionInputResult, TypeErased, }; use crate::ComponentStorage; /// 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 for Stateful where Func: Fn(#(TParam~I,)*) + Copy + 'static, #(TParam~I: Param<'world>,)* #(TParam~I::Input: 'static,)* (#(TParam~I::Input,)*): InputFilter { type Input = <(#(TParam~I::Input,)*) as InputFilter>::Filtered; 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::() { TakeOptionInputResult::Found(input) => input, TakeOptionInputResult::NotFound => { panic!( "Parameter input {} not found", type_name::() ); } TakeOptionInputResult::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(&mut self, component_storage: &mut ComponentStorage) { #( check_params_are_compatible!(I, TParam~I, $c); )* let func = self.func; func(#({ // SAFETY: All parameters are compatible so this is fine let this = unsafe { &mut *addr_of_mut!(*self) }; // SAFETY: All parameters are compatible so this is fine let component_storage = unsafe { &mut *addr_of_mut!(*component_storage) }; TParam~I::new(this, component_storage) },)*); } fn into_type_erased(self) -> TypeErased { TypeErased { data: Box::new(self), func: Box::new(|data, component_storage| { let me = data.downcast_mut::().unwrap(); me.run(component_storage); }), } } fn get_local_component_mut( &mut self, ) -> Option<&mut LocalComponent> { self.local_components .get_mut(&TypeId::of::())? .downcast_mut() } fn set_local_component( &mut self, local_component: LocalComponent, ) { self.local_components .insert(TypeId::of::(), 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 0..4 { impl_system!(C); });