diff options
Diffstat (limited to 'engine-ecs/src/system.rs')
| -rw-r--r-- | engine-ecs/src/system.rs | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/engine-ecs/src/system.rs b/engine-ecs/src/system.rs new file mode 100644 index 0000000..38e480d --- /dev/null +++ b/engine-ecs/src/system.rs @@ -0,0 +1,157 @@ +use std::fmt::Debug; + +use seq_macro::seq; + +use crate::error::Error; +use crate::uid::Uid; +use crate::{Component, World}; + +pub mod initializable; +pub mod observer; +pub mod stateful; + +/// Metadata of a system. +#[derive(Debug)] +#[non_exhaustive] +pub struct Metadata +{ + pub ent_id: Uid, +} + +pub trait System<'world, Impl>: 'static +{ + type Callbacks: Callbacks; + + fn finish(self) -> (TypeErased, Self::Callbacks); +} + +macro_rules! impl_system { + ($c: tt) => { + seq!(I in 0..$c { + impl<'world, Func, Ret, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*) -> Ret> + for Func + where + Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, + #(TParam~I: Param<'world, Input = ()>,)* + { + type Callbacks = NoCallbacks; + + fn finish(self) -> (TypeErased, Self::Callbacks) + { + #![allow(unused)] + + let type_erased = TypeErased { + run: Box::new(move |world, metadata| { + // SAFETY: The caller of TypeErased::run ensures the lifetime + // is correct + let world = unsafe { &*std::ptr::from_ref(world) }; + + self(#({ + TParam~I::new(world, &metadata) + },)*).into_result() + }), + name: std::any::type_name::<Self>() + }; + + (type_erased, NoCallbacks) + } + } + }); + }; +} + +seq!(C in 0..16 { + impl_system!(C); +}); + +pub trait Into<'world, Impl> +{ + type System; + + fn into_system(self) -> Self::System; +} + +pub struct TypeErased +{ + run: Box<TypeErasedRunFn>, + name: &'static str, +} + +impl TypeErased +{ + /// Runs the system. + /// + /// # Safety + /// `world_data` must live at least as long as the [`World`] the system belongs to. + pub unsafe fn run(&self, world: &World, metadata: Metadata) -> Result<(), Error> + { + (self.run)(world, metadata) + } + + pub fn name(&self) -> &'static str + { + self.name + } +} + +impl Debug for TypeErased +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + formatter.debug_struct("TypeErased").finish_non_exhaustive() + } +} + +pub trait ReturnValue +{ + fn into_result(self) -> Result<(), Error>; +} + +impl ReturnValue for () +{ + fn into_result(self) -> Result<(), Error> + { + Ok(()) + } +} + +impl ReturnValue for Result<(), Error> +{ + fn into_result(self) -> Result<(), Error> + { + self + } +} + +/// A parameter to a [`System`]. +pub trait Param<'world> +{ + type Input; + + fn new(world: &'world World, system_metadata: &Metadata) -> Self; +} + +/// A type which can be used as input to a [`System`]. +pub trait Input: 'static {} + +pub trait Callbacks +{ + fn on_created(&mut self, world: &mut World, metadata: Metadata); +} + +pub struct NoCallbacks; + +impl Callbacks for NoCallbacks +{ + fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {} +} + +#[derive(Debug, Component)] +pub(crate) struct SystemComponent +{ + pub(crate) system: TypeErased, +} + +/// Function in [`TypeErased`] used to run the system. +type TypeErasedRunFn = dyn Fn(&World, Metadata) -> Result<(), Error>; |
