diff options
| author | HampusM <hampus@hampusmat.com> | 2026-05-19 00:12:11 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-05-19 00:12:11 +0200 |
| commit | d5a744b0909c4b2bec397ae4dcd43b56aba355c6 (patch) | |
| tree | 1dcf8b769775ab7a0f4ff87e280aa98a0d280d21 | |
| parent | b13b3f6e13f9ac9fe7fee0b5a81b026f411f0301 (diff) | |
| -rw-r--r-- | Cargo.lock | 52 | ||||
| -rw-r--r-- | ecs/Cargo.toml | 7 | ||||
| -rw-r--r-- | ecs/examples/error_handling.rs | 79 | ||||
| -rw-r--r-- | ecs/src/error.rs | 227 | ||||
| -rw-r--r-- | ecs/src/lib.rs | 42 | ||||
| -rw-r--r-- | ecs/src/system.rs | 44 | ||||
| -rw-r--r-- | ecs/src/system/observer.rs | 106 | ||||
| -rw-r--r-- | ecs/src/system/stateful.rs | 75 |
8 files changed, 523 insertions, 109 deletions
@@ -3,6 +3,15 @@ version = 4 [[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -86,9 +95,9 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "as-raw-xcb-connection" @@ -109,6 +118,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -522,6 +546,8 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" name = "ecs" version = "0.1.0" dependencies = [ + "anyhow", + "backtrace", "criterion", "ecs-macros", "hashbrown", @@ -530,6 +556,7 @@ dependencies = [ "seq-macro", "thiserror", "tracing", + "tracing-subscriber", "util-macros", "vizoxide", ] @@ -731,6 +758,12 @@ dependencies = [ ] [[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] name = "gl_generator" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1414,6 +1447,15 @@ dependencies = [ ] [[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] name = "once_cell" version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1725,6 +1767,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index 31fa169..0a94ff0 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -13,6 +13,8 @@ thiserror = "1.0.49" tracing = "0.1.39" hashbrown = "0.15.2" parking_lot = "0.12.3" +anyhow = "1.0.102" +backtrace = "0.3.76" ecs-macros = { workspace = true } util-macros = { workspace = true } vizoxide = { version = "1.0.5", optional = true } @@ -22,6 +24,11 @@ version = "0.5.1" default-features = false features = ["cargo_bench_support"] +[dev-dependencies.tracing-subscriber] +version = "0.3.17" +default-features = false +features = ["std", "ansi", "fmt", "smallvec", "env-filter", "chrono"] + [[bench]] name = "query" harness = false diff --git a/ecs/examples/error_handling.rs b/ecs/examples/error_handling.rs new file mode 100644 index 0000000..dc34c5f --- /dev/null +++ b/ecs/examples/error_handling.rs @@ -0,0 +1,79 @@ +use ecs::error::Error; +use ecs::event::component::{Changed, EventMatchExt}; +use ecs::pair::Pair; +use ecs::phase::UPDATE; +use ecs::query::Query; +use ecs::system::observer::Observe; +use ecs::{Component, World, error}; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::EnvFilter; +use tracing_subscriber::fmt::time::ChronoLocal; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; + +#[derive(Component)] +struct State +{ + value: u32, +} + +fn do_something_fallible(query: Query<(&mut State,)>) -> Result<(), Error> +{ + for (mut state,) in &query { + state.value += 1; + + state.set_changed(); + + if state.value > 3 { + return Err(error!("Invalid state value {}", state.value)); + } + } + + Ok(()) +} + +fn handle_state_changed(observe: Observe<Pair<Changed, State>>) -> Result<(), Error> +{ + for evt_match in &observe { + let state = evt_match.get_ent_target_comp(); + + if state.value > 3 { + return Err(error!("Invalid state value {}", state.value)); + } + + tracing::info!("State has valid value {}", state.value); + } + + Ok(()) +} + +fn main() +{ + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_timer(ChronoLocal::new("%T%.6f".to_string())), + ) + .with( + EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env() + .unwrap(), + ) + .init(); + + let mut world = World::new(); + + world.set_err_handler(ecs::error::err_handler_log_error); + + world.create_entity((State { value: 0 },)); + + world.register_system(*UPDATE, do_something_fallible); + + world.register_observer(handle_state_changed); + + world.step(); + world.step(); + world.step(); + world.step(); +} diff --git a/ecs/src/error.rs b/ecs/src/error.rs new file mode 100644 index 0000000..f70524d --- /dev/null +++ b/ecs/src/error.rs @@ -0,0 +1,227 @@ +use std::fmt::{Debug, Display, Write as _}; + +use backtrace::Backtrace; + +#[macro_export] +macro_rules! error { + ($lit: literal) => { + $crate::error::Error::from($lit) + }; + + ($lit: literal, $($tt: tt)*) => { + $crate::error::Error::from(std::format!($lit, $($tt)*)) + }; + + ($err: expr) => { + $crate::error::Error::from($err) + }; +} + +pub struct Error +{ + inner: Box<dyn std::error::Error + Send + Sync>, + backtrace: Backtrace, +} + +impl Debug for Error +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + let error = &*self.inner; + + write!(formatter, "{error}")?; + + if let Some(cause) = error.source() { + write!(formatter, "\n\nCaused by:")?; + let multiple = cause.source().is_some(); + for (n, error) in anyhow::Chain::new(cause).enumerate() { + writeln!(formatter)?; + + let mut indented = Indented { + inner: formatter, + number: if multiple { Some(n) } else { None }, + started: false, + }; + write!(indented, "{error}")?; + } + } + + write!( + formatter, + "\n\nStack backtrace:\n{:?}", + std::fmt::from_fn(|backtrace_formatter| fmt_backtrace( + &self.backtrace, + backtrace_formatter + )) + )?; + + Ok(()) + } +} + +impl Display for Error +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + let error = &*self.inner; + + write!(formatter, "{error}")?; + + if formatter.alternate() { + let chain = anyhow::Chain::new(error); + for cause in chain.skip(1) { + write!(formatter, ": {}", cause)?; + } + } + + Ok(()) + } +} + +impl<Err: Send + Sync + 'static> From<Err> for Error +where + Box<dyn std::error::Error + Send + Sync>: From<Err>, +{ + fn from(err: Err) -> Self + { + Self { + inner: err.into(), + backtrace: Backtrace::new(), + } + } +} + +pub type ErrorHandler = fn(Error, Metadata); + +/// Error metadata. +#[derive(Debug)] +pub struct Metadata +{ + pub source_name: &'static str, + pub source_kind: SourceKind, +} + +/// Error source kind. +#[derive(Debug)] +#[non_exhaustive] +pub enum SourceKind +{ + System, + Observer, +} + +impl Display for SourceKind +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self { + SourceKind::System => formatter.write_str("system"), + SourceKind::Observer => formatter.write_str("observer"), + } + } +} + +pub fn err_handler_panic(err: Error, err_metadata: Metadata) +{ + panic!( + "Error occurred in {} '{}': {err:?}", + err_metadata.source_kind, err_metadata.source_name + ); +} + +pub fn err_handler_log_error(err: Error, err_metadata: Metadata) +{ + tracing::error!( + "Error occurred in {} '{}': {err:#}", + err_metadata.source_kind, + err_metadata.source_name + ); +} + +fn fmt_backtrace( + backtrace: &Backtrace, + fmt: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result +{ + let style = if fmt.alternate() { + backtrace::PrintFmt::Full + } else { + backtrace::PrintFmt::Short + }; + + // When printing paths we try to strip the cwd if it exists, otherwise + // we just print the path as-is. Note that we also only do this for the + // short format, because if it's full we presumably want to print + // everything. + let cwd = std::env::current_dir(); + let mut print_path = + move |fmt: &mut std::fmt::Formatter<'_>, + path: backtrace::BytesOrWideString<'_>| { + let path = path.into_path_buf(); + if style != backtrace::PrintFmt::Full { + if let Ok(cwd) = &cwd { + if let Ok(suffix) = path.strip_prefix(cwd) { + return std::fmt::Display::fmt(&suffix.display(), fmt); + } + } + } + std::fmt::Display::fmt(&path.display(), fmt) + }; + + let mut f = backtrace::BacktraceFmt::new(fmt, style, &mut print_path); + + f.add_context()?; + + for frame in backtrace.frames() { + if frame.symbols().iter().all(|symbol| { + symbol.name().is_some_and(|symbol_name| { + let symbol_name = symbol_name.to_string(); + + symbol_name + .contains("<ecs::error::Error as core::convert::From<Err>>::from") + }) + }) { + continue; + } + + f.frame().backtrace_frame(frame)?; + } + f.finish()?; + Ok(()) +} + +struct Indented<'a, D> +{ + inner: &'a mut D, + number: Option<usize>, + started: bool, +} + +impl<T> std::fmt::Write for Indented<'_, T> +where + T: std::fmt::Write, +{ + fn write_str(&mut self, s: &str) -> std::fmt::Result + { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => write!(self.inner, "{: >5}: ", number)?, + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 32ade13..667aa0e 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -20,6 +20,12 @@ use crate::component::{ Sequence as ComponentSequence, }; use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle}; +use crate::error::{ + ErrorHandler, + Metadata as ErrorMetadata, + SourceKind as ErrorSourceKind, + err_handler_panic, +}; use crate::event::component::Added; use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter}; use crate::extension::{Collector as ExtensionCollector, Extension}; @@ -50,6 +56,7 @@ use crate::uid::{Kind as UidKind, Uid}; pub mod actions; pub mod component; pub mod entity; +pub mod error; pub mod event; pub mod extension; pub mod pair; @@ -74,6 +81,7 @@ pub struct World data: WorldData, stop: AtomicBool, is_first_tick: AtomicBool, + error_handler: ErrorHandler, } impl World @@ -85,6 +93,7 @@ impl World data: WorldData::default(), stop: AtomicBool::new(false), is_first_tick: AtomicBool::new(false), + error_handler: err_handler_panic, }; crate::phase::spawn_entities(&mut world); @@ -94,6 +103,11 @@ impl World world } + pub fn set_err_handler(&mut self, err_handler: ErrorHandler) + { + self.error_handler = err_handler; + } + /// Creates a entity with the given components. A new unique [`Uid`] will be generated /// for this entity. pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid @@ -371,10 +385,20 @@ impl World }; // SAFETY: The world lives long enough - unsafe { + if let Err(err) = unsafe { system .system - .run(self, SystemMetadata { ent_id: system_entity.uid() }); + .run(self, SystemMetadata { ent_id: system_entity.uid() }) + } { + cold_path(); + + (self.error_handler)( + err, + ErrorMetadata { + source_name: system.system.name(), + source_kind: ErrorSourceKind::System, + }, + ) } } } @@ -570,12 +594,22 @@ impl World ); for (observer_ent_id, (observer,)) in query.iter_with_euids() { - unsafe { + if let Err(err) = unsafe { observer.run( self, SystemMetadata { ent_id: observer_ent_id }, emitted_event.clone(), - ); + ) + } { + cold_path(); + + (self.error_handler)( + err, + ErrorMetadata { + source_name: observer.name(), + source_kind: ErrorSourceKind::Observer, + }, + ) } } } diff --git a/ecs/src/system.rs b/ecs/src/system.rs index ff3437a..5d3e0bf 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -4,6 +4,7 @@ use ecs_macros::Component; use seq_macro::seq; use crate::World; +use crate::error::Error; use crate::uid::Uid; pub mod initializable; @@ -28,10 +29,11 @@ pub trait System<'world, Impl>: 'static macro_rules! impl_system { ($c: tt) => { seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*)> + impl<'world, Func, Ret, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*) -> Ret> for Func where - Func: Fn(#(TParam~I,)*) + Copy + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world, Input = ()>,)* { type Callbacks = NoCallbacks; @@ -48,8 +50,9 @@ macro_rules! impl_system { self(#({ TParam~I::new(world, &metadata) - },)*); + },)*).into_result() }), + name: std::any::type_name::<Self>() }; (type_erased, NoCallbacks) @@ -73,6 +76,7 @@ pub trait Into<'world, Impl> pub struct TypeErased { run: Box<TypeErasedRunFn>, + name: &'static str, } impl TypeErased @@ -81,9 +85,14 @@ impl TypeErased /// /// # 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) + pub unsafe fn run(&self, world: &World, metadata: Metadata) -> Result<(), Error> { - (self.run)(world, metadata); + (self.run)(world, metadata) + } + + pub fn name(&self) -> &'static str + { + self.name } } @@ -95,8 +104,26 @@ impl Debug for TypeErased } } -/// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&World, Metadata); +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> @@ -126,3 +153,6 @@ pub(crate) struct SystemComponent { pub(crate) system: TypeErased, } + +/// Function in [`TypeErased`] used to run the system. +type TypeErasedRunFn = dyn Fn(&World, Metadata) -> Result<(), Error>; diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs index 0472614..6893b0f 100644 --- a/ecs/src/system/observer.rs +++ b/ecs/src/system/observer.rs @@ -1,3 +1,4 @@ +use std::any::type_name; use std::fmt::Debug; use std::marker::PhantomData; use std::mem::transmute; @@ -6,20 +7,22 @@ use std::slice::Iter as SliceIter; use ecs_macros::Component; use seq_macro::seq; +use crate::World; use crate::component::Component; use crate::entity::Handle as EntityHandle; +use crate::error::Error; use crate::event::Emitted as EmittedEvent; use crate::pair::Pair; use crate::system::{ Metadata, NoCallbacks, Param, + ReturnValue as SystemReturnValue, System, TypeErased as TypeErasedSystem, }; use crate::uid::Uid; use crate::util::Array; -use crate::World; pub trait Observed { @@ -154,30 +157,34 @@ impl<'world, ObservedT: Observed> EventMatch<'world, ObservedT> macro_rules! impl_observer { ($c: tt) => { seq!(I in 0..$c { - impl<'world, ObservedT, Func, #(TParam~I,)*> System< + impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> System< 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) + fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Func where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Ret: SystemReturnValue, #(TParam~I: Param<'world, Input = ()>,)* { type Callbacks = NoCallbacks; fn finish(self) -> (TypeErasedSystem, NoCallbacks) { - unimplemented!(); + const { + panic!("Observers cannot be used as regular systems"); + } } } - impl<'world, ObservedT, Func, #(TParam~I,)*> Observer< + impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer< 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) + fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Func where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Ret: SystemReturnValue, #(TParam~I: Param<'world, Input = ()>,)* { type ObservedEvents = ObservedT::Events; @@ -189,8 +196,10 @@ macro_rules! impl_observer { fn finish_observer(self) -> (WrapperComponent, NoCallbacks) { - let wrapper_comp = WrapperComponent { - run: Box::new(move |world, metadata, emitted_event| { + #[allow(unused)] + + let wrapper_comp = WrapperComponent::new( + move |world, metadata, emitted_event| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world = unsafe { &*std::ptr::from_ref(world) }; @@ -205,9 +214,10 @@ macro_rules! impl_observer { self(Observe::new(world, emitted_event), #({ TParam~I::new(world, &metadata) - },)*); - }), - }; + },)*).into_result() + }, + type_name::<Func>() + ); (wrapper_comp, NoCallbacks) } @@ -216,70 +226,25 @@ macro_rules! impl_observer { }; } -seq!(C in 1..16 { +seq!(C in 0..16 { impl_observer!(C); }); -impl<'world, ObservedT, Func> System<'world, fn(Observe<'world, ObservedT>)> for Func -where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>) + Copy + 'static, -{ - type Callbacks = NoCallbacks; - - fn finish(self) -> (TypeErasedSystem, NoCallbacks) - { - const { - panic!("Observers cannot be used as regular systems"); - } - } -} - -impl<'world, ObservedT, Func> Observer<'world, fn(Observe<'world, ObservedT>)> for Func -where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>) + Copy + 'static, -{ - type ObservedEvents = ObservedT::Events; - - fn observed_events() -> Self::ObservedEvents - { - ObservedT::events() - } - - fn finish_observer(self) -> (WrapperComponent, NoCallbacks) - { - let wrapper_comp = WrapperComponent { - run: Box::new(move |world, _metadata, emitted_event| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; - - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let emitted_event = unsafe { - transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(emitted_event) - }; - - self(Observe::new(world, emitted_event)); - }), - }; - - (wrapper_comp, NoCallbacks) - } -} - #[derive(Component)] pub struct WrapperComponent { run: Box<RunFn>, + name: &'static str, } impl WrapperComponent { - pub fn new(run: impl Fn(&World, Metadata, EmittedEvent<'_>) + 'static) -> Self + pub fn new( + run: impl Fn(&World, Metadata, EmittedEvent<'_>) -> Result<(), Error> + 'static, + name: &'static str, + ) -> Self { - Self { run: Box::new(run) } + Self { run: Box::new(run), name } } /// Runs the observer system. @@ -291,9 +256,14 @@ impl WrapperComponent world: &World, metadata: Metadata, emitted_event: EmittedEvent<'_>, - ) + ) -> Result<(), Error> + { + (self.run)(world, metadata, emitted_event) + } + + pub fn name(&self) -> &'static str { - (self.run)(world, metadata, emitted_event); + self.name } } @@ -307,4 +277,4 @@ impl Debug for WrapperComponent } } -type RunFn = dyn Fn(&World, Metadata, EmittedEvent<'_>); +type RunFn = dyn Fn(&World, Metadata, EmittedEvent<'_>) -> Result<(), Error>; diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index e74ef31..3e0076a 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,10 +1,11 @@ +use std::any::type_name; use std::mem::transmute; -use std::panic::{RefUnwindSafe, UnwindSafe}; use seq_macro::seq; -use crate::component::local::SystemWithLocalComponents; +use crate::World; use crate::component::Parts as ComponentParts; +use crate::component::local::SystemWithLocalComponents; use crate::event::Emitted as EmittedEvent; use crate::system::initializable::{Initializable, MaybeInitializableParamTuple}; use crate::system::observer::{ @@ -13,8 +14,14 @@ use crate::system::observer::{ Observer, WrapperComponent as ObserverWrapperComponent, }; -use crate::system::{Into as IntoSystem, Metadata, Param, System, TypeErased}; -use crate::World; +use crate::system::{ + Into as IntoSystem, + Metadata, + Param, + ReturnValue, + System, + TypeErased, +}; /// A stateful system. pub struct Stateful<Func> @@ -26,10 +33,11 @@ pub struct Stateful<Func> macro_rules! impl_system { ($c: tt) => { seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> - System<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func> + impl<'world, Func, Ret, #(TParam~I,)*> + System<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func> where - Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world, Input: 'static>,)* { type Callbacks = Callbacks; @@ -49,7 +57,10 @@ macro_rules! impl_system { func(#({ TParam~I::new(&world, &metadata) },)*); + + Ok(()) }), + name: type_name::<Func>() }; @@ -57,10 +68,11 @@ macro_rules! impl_system { } } - impl<'world, Func, #(TParam~I,)*> - Initializable<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func> + impl<'world, Func, Ret, #(TParam~I,)*> + Initializable<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func> where - Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world, Input: 'static>,)* (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> { @@ -76,11 +88,12 @@ macro_rules! impl_system { } } - impl<'world, Func, #(TParam~I,)*> IntoSystem<'world, fn(#(TParam~I,)*)> - for Func + impl<'world, Func, Ret, #(TParam~I,)*> + IntoSystem<'world, fn(#(TParam~I,)*) -> Ret> for Func where + Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world>,)* - Func: Fn(#(TParam~I,)*) + Copy + 'static, { type System = Stateful<Func>; @@ -136,13 +149,14 @@ fn init_initializable_params<'world, SystemT, Params>( macro_rules! impl_observer { ($c: tt) => { seq!(I in 0..$c { - impl<'world, ObservedT, Func, #(TParam~I,)*> System< + impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> System< 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) + fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Stateful<Func> where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world>,)* { type Callbacks = Callbacks; @@ -153,13 +167,14 @@ macro_rules! impl_observer { } } - impl<'world, ObservedT, Func, #(TParam~I,)*> Initializable< + impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Initializable< 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) + fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Stateful<Func> where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world>,)* (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> { @@ -175,13 +190,14 @@ macro_rules! impl_observer { } } - impl<'world, ObservedT, Func, #(TParam~I,)*> Observer< + impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer< 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) + fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Stateful<Func> where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world>,)* { type ObservedEvents = ObservedT::Events; @@ -193,6 +209,8 @@ macro_rules! impl_observer { fn finish_observer(self) -> (ObserverWrapperComponent, Callbacks) { + #![allow(unused)] + let Self { func, local_components } = self; let callbacks = Callbacks { local_components }; @@ -213,23 +231,24 @@ macro_rules! impl_observer { func(Observe::new(world, emitted_event), #({ TParam~I::new(world, &metadata) - },)*); + },)*).into_result() }, + type_name::<Func>() ); (wrapper_comp, callbacks) } } - impl<'world, Func, ObservedT, #(TParam~I,)*> IntoSystem< + impl<'world, Func, Ret, ObservedT, #(TParam~I,)*> IntoSystem< 'world, - fn(Observe<'world, ObservedT>, - #(TParam~I,)*) + fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Func where ObservedT: Observed, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Ret: ReturnValue, #(TParam~I: Param<'world>,)* - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, { type System = Stateful<Func>; @@ -245,6 +264,6 @@ macro_rules! impl_observer { }; } -seq!(C in 1..16 { +seq!(C in 0..16 { impl_observer!(C); }); |
