summaryrefslogtreecommitdiff
path: root/ecs/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-05-19 00:12:11 +0200
committerHampusM <hampus@hampusmat.com>2026-05-19 00:12:11 +0200
commitd5a744b0909c4b2bec397ae4dcd43b56aba355c6 (patch)
tree1dcf8b769775ab7a0f4ff87e280aa98a0d280d21 /ecs/src
parentb13b3f6e13f9ac9fe7fee0b5a81b026f411f0301 (diff)
feat(ecs): add error handlingHEADmaster
Diffstat (limited to 'ecs/src')
-rw-r--r--ecs/src/error.rs227
-rw-r--r--ecs/src/lib.rs42
-rw-r--r--ecs/src/system.rs44
-rw-r--r--ecs/src/system/observer.rs106
-rw-r--r--ecs/src/system/stateful.rs75
5 files changed, 387 insertions, 107 deletions
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);
});