summaryrefslogtreecommitdiff
path: root/engine-ecs/src/system.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/src/system.rs')
-rw-r--r--engine-ecs/src/system.rs157
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>;