bevy_ecs/system/function_system.rs
1use crate::{
2 archetype::{ArchetypeComponentId, ArchetypeGeneration},
3 component::{ComponentId, Tick},
4 prelude::FromWorld,
5 query::{Access, FilteredAccessSet},
6 schedule::{InternedSystemSet, SystemSet},
7 system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem},
8 world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId},
9};
10
11use bevy_utils::all_tuples;
12use std::{borrow::Cow, marker::PhantomData};
13
14#[cfg(feature = "trace")]
15use bevy_utils::tracing::{info_span, Span};
16
17use super::{In, IntoSystem, ReadOnlySystem, SystemBuilder};
18
19/// The metadata of a [`System`].
20#[derive(Clone)]
21pub struct SystemMeta {
22 pub(crate) name: Cow<'static, str>,
23 pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
24 pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
25 // NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
26 // SystemParams from overriding each other
27 is_send: bool,
28 has_deferred: bool,
29 pub(crate) last_run: Tick,
30 #[cfg(feature = "trace")]
31 pub(crate) system_span: Span,
32 #[cfg(feature = "trace")]
33 pub(crate) commands_span: Span,
34}
35
36impl SystemMeta {
37 pub(crate) fn new<T>() -> Self {
38 let name = std::any::type_name::<T>();
39 Self {
40 name: name.into(),
41 archetype_component_access: Access::default(),
42 component_access_set: FilteredAccessSet::default(),
43 is_send: true,
44 has_deferred: false,
45 last_run: Tick::new(0),
46 #[cfg(feature = "trace")]
47 system_span: info_span!("system", name = name),
48 #[cfg(feature = "trace")]
49 commands_span: info_span!("system_commands", name = name),
50 }
51 }
52
53 /// Returns the system's name
54 #[inline]
55 pub fn name(&self) -> &str {
56 &self.name
57 }
58
59 /// Returns true if the system is [`Send`].
60 #[inline]
61 pub fn is_send(&self) -> bool {
62 self.is_send
63 }
64
65 /// Sets the system to be not [`Send`].
66 ///
67 /// This is irreversible.
68 #[inline]
69 pub fn set_non_send(&mut self) {
70 self.is_send = false;
71 }
72
73 /// Returns true if the system has deferred [`SystemParam`]'s
74 #[inline]
75 pub fn has_deferred(&self) -> bool {
76 self.has_deferred
77 }
78
79 /// Marks the system as having deferred buffers like [`Commands`](`super::Commands`)
80 /// This lets the scheduler insert [`apply_deferred`](`crate::prelude::apply_deferred`) systems automatically.
81 pub fn set_has_deferred(&mut self) {
82 self.has_deferred = true;
83 }
84}
85
86// TODO: Actually use this in FunctionSystem. We should probably only do this once Systems are constructed using a World reference
87// (to avoid the need for unwrapping to retrieve SystemMeta)
88/// Holds on to persistent state required to drive [`SystemParam`] for a [`System`].
89///
90/// This is a powerful and convenient tool for working with exclusive world access,
91/// allowing you to fetch data from the [`World`] as if you were running a [`System`].
92/// However, simply calling `world::run_system(my_system)` using a [`World::run_system`](World::run_system)
93/// can be significantly simpler and ensures that change detection and command flushing work as expected.
94///
95/// Borrow-checking is handled for you, allowing you to mutably access multiple compatible system parameters at once,
96/// and arbitrary system parameters (like [`EventWriter`](crate::event::EventWriter)) can be conveniently fetched.
97///
98/// For an alternative approach to split mutable access to the world, see [`World::resource_scope`].
99///
100/// # Warning
101///
102/// [`SystemState`] values created can be cached to improve performance,
103/// and *must* be cached and reused in order for system parameters that rely on local state to work correctly.
104/// These include:
105/// - [`Added`](crate::query::Added) and [`Changed`](crate::query::Changed) query filters
106/// - [`Local`](crate::system::Local) variables that hold state
107/// - [`EventReader`](crate::event::EventReader) system parameters, which rely on a [`Local`](crate::system::Local) to track which events have been seen
108///
109/// Note that this is automatically handled for you when using a [`World::run_system`](World::run_system).
110///
111/// # Example
112///
113/// Basic usage:
114/// ```
115/// # use bevy_ecs::prelude::*;
116/// # use bevy_ecs::system::SystemState;
117/// # use bevy_ecs::event::Events;
118/// #
119/// # #[derive(Event)]
120/// # struct MyEvent;
121/// # #[derive(Resource)]
122/// # struct MyResource(u32);
123/// #
124/// # #[derive(Component)]
125/// # struct MyComponent;
126/// #
127/// // Work directly on the `World`
128/// let mut world = World::new();
129/// world.init_resource::<Events<MyEvent>>();
130///
131/// // Construct a `SystemState` struct, passing in a tuple of `SystemParam`
132/// // as if you were writing an ordinary system.
133/// let mut system_state: SystemState<(
134/// EventWriter<MyEvent>,
135/// Option<ResMut<MyResource>>,
136/// Query<&MyComponent>,
137/// )> = SystemState::new(&mut world);
138///
139/// // Use system_state.get_mut(&mut world) and unpack your system parameters into variables!
140/// // system_state.get(&world) provides read-only versions of your system parameters instead.
141/// let (event_writer, maybe_resource, query) = system_state.get_mut(&mut world);
142///
143/// // If you are using `Commands`, you can choose when you want to apply them to the world.
144/// // You need to manually call `.apply(world)` on the `SystemState` to apply them.
145/// ```
146/// Caching:
147/// ```
148/// # use bevy_ecs::prelude::*;
149/// # use bevy_ecs::system::SystemState;
150/// # use bevy_ecs::event::Events;
151/// #
152/// # #[derive(Event)]
153/// # struct MyEvent;
154/// #[derive(Resource)]
155/// struct CachedSystemState {
156/// event_state: SystemState<EventReader<'static, 'static, MyEvent>>,
157/// }
158///
159/// // Create and store a system state once
160/// let mut world = World::new();
161/// world.init_resource::<Events<MyEvent>>();
162/// let initial_state: SystemState<EventReader<MyEvent>> = SystemState::new(&mut world);
163///
164/// // The system state is cached in a resource
165/// world.insert_resource(CachedSystemState {
166/// event_state: initial_state,
167/// });
168///
169/// // Later, fetch the cached system state, saving on overhead
170/// world.resource_scope(|world, mut cached_state: Mut<CachedSystemState>| {
171/// let mut event_reader = cached_state.event_state.get_mut(world);
172///
173/// for events in event_reader.read() {
174/// println!("Hello World!");
175/// }
176/// });
177/// ```
178pub struct SystemState<Param: SystemParam + 'static> {
179 meta: SystemMeta,
180 param_state: Param::State,
181 world_id: WorldId,
182 archetype_generation: ArchetypeGeneration,
183}
184
185impl<Param: SystemParam> SystemState<Param> {
186 /// Creates a new [`SystemState`] with default state.
187 ///
188 /// ## Note
189 /// For users of [`SystemState::get_manual`] or [`get_manual_mut`](SystemState::get_manual_mut):
190 ///
191 /// `new` does not cache any of the world's archetypes, so you must call [`SystemState::update_archetypes`]
192 /// manually before calling `get_manual{_mut}`.
193 pub fn new(world: &mut World) -> Self {
194 let mut meta = SystemMeta::new::<Param>();
195 meta.last_run = world.change_tick().relative_to(Tick::MAX);
196 let param_state = Param::init_state(world, &mut meta);
197 Self {
198 meta,
199 param_state,
200 world_id: world.id(),
201 archetype_generation: ArchetypeGeneration::initial(),
202 }
203 }
204
205 // Create a [`SystemState`] from a [`SystemBuilder`]
206 pub(crate) fn from_builder(builder: SystemBuilder<Param>) -> Self {
207 Self {
208 meta: builder.meta,
209 param_state: builder.state,
210 world_id: builder.world.id(),
211 archetype_generation: ArchetypeGeneration::initial(),
212 }
213 }
214
215 /// Gets the metadata for this instance.
216 #[inline]
217 pub fn meta(&self) -> &SystemMeta {
218 &self.meta
219 }
220
221 /// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
222 #[inline]
223 pub fn get<'w, 's>(&'s mut self, world: &'w World) -> SystemParamItem<'w, 's, Param>
224 where
225 Param: ReadOnlySystemParam,
226 {
227 self.validate_world(world.id());
228 self.update_archetypes(world);
229 // SAFETY: Param is read-only and doesn't allow mutable access to World.
230 // It also matches the World this SystemState was created with.
231 unsafe { self.get_unchecked_manual(world.as_unsafe_world_cell_readonly()) }
232 }
233
234 /// Retrieve the mutable [`SystemParam`] values.
235 #[inline]
236 pub fn get_mut<'w, 's>(&'s mut self, world: &'w mut World) -> SystemParamItem<'w, 's, Param> {
237 self.validate_world(world.id());
238 self.update_archetypes(world);
239 // SAFETY: World is uniquely borrowed and matches the World this SystemState was created with.
240 unsafe { self.get_unchecked_manual(world.as_unsafe_world_cell()) }
241 }
242
243 /// Applies all state queued up for [`SystemParam`] values. For example, this will apply commands queued up
244 /// by a [`Commands`](`super::Commands`) parameter to the given [`World`].
245 /// This function should be called manually after the values returned by [`SystemState::get`] and [`SystemState::get_mut`]
246 /// are finished being used.
247 pub fn apply(&mut self, world: &mut World) {
248 Param::apply(&mut self.param_state, &self.meta, world);
249 }
250
251 /// Returns `true` if `world_id` matches the [`World`] that was used to call [`SystemState::new`].
252 /// Otherwise, this returns false.
253 #[inline]
254 pub fn matches_world(&self, world_id: WorldId) -> bool {
255 self.world_id == world_id
256 }
257
258 /// Asserts that the [`SystemState`] matches the provided world.
259 #[inline]
260 #[track_caller]
261 fn validate_world(&self, world_id: WorldId) {
262 #[inline(never)]
263 #[track_caller]
264 #[cold]
265 fn panic_mismatched(this: WorldId, other: WorldId) -> ! {
266 panic!("Encountered a mismatched World. This SystemState was created from {this:?}, but a method was called using {other:?}.");
267 }
268
269 if !self.matches_world(world_id) {
270 panic_mismatched(self.world_id, world_id);
271 }
272 }
273
274 /// Updates the state's internal view of the [`World`]'s archetypes. If this is not called before fetching the parameters,
275 /// the results may not accurately reflect what is in the `world`.
276 ///
277 /// This is only required if [`SystemState::get_manual`] or [`SystemState::get_manual_mut`] is being called, and it only needs to
278 /// be called if the `world` has been structurally mutated (i.e. added/removed a component or resource). Users using
279 /// [`SystemState::get`] or [`SystemState::get_mut`] do not need to call this as it will be automatically called for them.
280 #[inline]
281 pub fn update_archetypes(&mut self, world: &World) {
282 self.update_archetypes_unsafe_world_cell(world.as_unsafe_world_cell_readonly());
283 }
284
285 /// Updates the state's internal view of the `world`'s archetypes. If this is not called before fetching the parameters,
286 /// the results may not accurately reflect what is in the `world`.
287 ///
288 /// This is only required if [`SystemState::get_manual`] or [`SystemState::get_manual_mut`] is being called, and it only needs to
289 /// be called if the `world` has been structurally mutated (i.e. added/removed a component or resource). Users using
290 /// [`SystemState::get`] or [`SystemState::get_mut`] do not need to call this as it will be automatically called for them.
291 ///
292 /// # Note
293 ///
294 /// This method only accesses world metadata.
295 #[inline]
296 pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
297 assert_eq!(self.world_id, world.id(), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
298
299 let archetypes = world.archetypes();
300 let old_generation =
301 std::mem::replace(&mut self.archetype_generation, archetypes.generation());
302
303 for archetype in &archetypes[old_generation..] {
304 // SAFETY: The assertion above ensures that the param_state was initialized from `world`.
305 unsafe { Param::new_archetype(&mut self.param_state, archetype, &mut self.meta) };
306 }
307 }
308
309 /// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
310 /// This will not update the state's view of the world's archetypes automatically nor increment the
311 /// world's change tick.
312 ///
313 /// For this to return accurate results, ensure [`SystemState::update_archetypes`] is called before this
314 /// function.
315 ///
316 /// Users should strongly prefer to use [`SystemState::get`] over this function.
317 #[inline]
318 pub fn get_manual<'w, 's>(&'s mut self, world: &'w World) -> SystemParamItem<'w, 's, Param>
319 where
320 Param: ReadOnlySystemParam,
321 {
322 self.validate_world(world.id());
323 let change_tick = world.read_change_tick();
324 // SAFETY: Param is read-only and doesn't allow mutable access to World.
325 // It also matches the World this SystemState was created with.
326 unsafe { self.fetch(world.as_unsafe_world_cell_readonly(), change_tick) }
327 }
328
329 /// Retrieve the mutable [`SystemParam`] values. This will not update the state's view of the world's archetypes
330 /// automatically nor increment the world's change tick.
331 ///
332 /// For this to return accurate results, ensure [`SystemState::update_archetypes`] is called before this
333 /// function.
334 ///
335 /// Users should strongly prefer to use [`SystemState::get_mut`] over this function.
336 #[inline]
337 pub fn get_manual_mut<'w, 's>(
338 &'s mut self,
339 world: &'w mut World,
340 ) -> SystemParamItem<'w, 's, Param> {
341 self.validate_world(world.id());
342 let change_tick = world.change_tick();
343 // SAFETY: World is uniquely borrowed and matches the World this SystemState was created with.
344 unsafe { self.fetch(world.as_unsafe_world_cell(), change_tick) }
345 }
346
347 /// Retrieve the [`SystemParam`] values. This will not update archetypes automatically.
348 ///
349 /// # Safety
350 /// This call might access any of the input parameters in a way that violates Rust's mutability rules. Make sure the data
351 /// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
352 /// created with.
353 #[inline]
354 pub unsafe fn get_unchecked_manual<'w, 's>(
355 &'s mut self,
356 world: UnsafeWorldCell<'w>,
357 ) -> SystemParamItem<'w, 's, Param> {
358 let change_tick = world.increment_change_tick();
359 // SAFETY: The invariants are uphold by the caller.
360 unsafe { self.fetch(world, change_tick) }
361 }
362
363 /// # Safety
364 /// This call might access any of the input parameters in a way that violates Rust's mutability rules. Make sure the data
365 /// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
366 /// created with.
367 #[inline]
368 unsafe fn fetch<'w, 's>(
369 &'s mut self,
370 world: UnsafeWorldCell<'w>,
371 change_tick: Tick,
372 ) -> SystemParamItem<'w, 's, Param> {
373 // SAFETY: The invariants are uphold by the caller.
374 let param =
375 unsafe { Param::get_param(&mut self.param_state, &self.meta, world, change_tick) };
376 self.meta.last_run = change_tick;
377 param
378 }
379}
380
381impl<Param: SystemParam> FromWorld for SystemState<Param> {
382 fn from_world(world: &mut World) -> Self {
383 Self::new(world)
384 }
385}
386
387/// The [`System`] counter part of an ordinary function.
388///
389/// You get this by calling [`IntoSystem::into_system`] on a function that only accepts
390/// [`SystemParam`]s. The output of the system becomes the functions return type, while the input
391/// becomes the functions [`In`] tagged parameter or `()` if no such parameter exists.
392///
393/// [`FunctionSystem`] must be `.initialized` before they can be run.
394///
395/// The [`Clone`] implementation for [`FunctionSystem`] returns a new instance which
396/// is NOT initialized. The cloned system must also be `.initialized` before it can be run.
397pub struct FunctionSystem<Marker, F>
398where
399 F: SystemParamFunction<Marker>,
400{
401 func: F,
402 pub(crate) param_state: Option<<F::Param as SystemParam>::State>,
403 pub(crate) system_meta: SystemMeta,
404 world_id: Option<WorldId>,
405 archetype_generation: ArchetypeGeneration,
406 // NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
407 marker: PhantomData<fn() -> Marker>,
408}
409
410impl<Marker, F> FunctionSystem<Marker, F>
411where
412 F: SystemParamFunction<Marker>,
413{
414 // Create a [`FunctionSystem`] from a [`SystemBuilder`]
415 pub(crate) fn from_builder(builder: SystemBuilder<F::Param>, func: F) -> Self {
416 Self {
417 func,
418 param_state: Some(builder.state),
419 system_meta: builder.meta,
420 world_id: Some(builder.world.id()),
421 archetype_generation: ArchetypeGeneration::initial(),
422 marker: PhantomData,
423 }
424 }
425}
426
427// De-initializes the cloned system.
428impl<Marker, F> Clone for FunctionSystem<Marker, F>
429where
430 F: SystemParamFunction<Marker> + Clone,
431{
432 fn clone(&self) -> Self {
433 Self {
434 func: self.func.clone(),
435 param_state: None,
436 system_meta: SystemMeta::new::<F>(),
437 world_id: None,
438 archetype_generation: ArchetypeGeneration::initial(),
439 marker: PhantomData,
440 }
441 }
442}
443
444/// A marker type used to distinguish regular function systems from exclusive function systems.
445#[doc(hidden)]
446pub struct IsFunctionSystem;
447
448impl<Marker, F> IntoSystem<F::In, F::Out, (IsFunctionSystem, Marker)> for F
449where
450 Marker: 'static,
451 F: SystemParamFunction<Marker>,
452{
453 type System = FunctionSystem<Marker, F>;
454 fn into_system(func: Self) -> Self::System {
455 FunctionSystem {
456 func,
457 param_state: None,
458 system_meta: SystemMeta::new::<F>(),
459 world_id: None,
460 archetype_generation: ArchetypeGeneration::initial(),
461 marker: PhantomData,
462 }
463 }
464}
465
466impl<Marker, F> FunctionSystem<Marker, F>
467where
468 F: SystemParamFunction<Marker>,
469{
470 /// Message shown when a system isn't initialised
471 // When lines get too long, rustfmt can sometimes refuse to format them.
472 // Work around this by storing the message separately.
473 const PARAM_MESSAGE: &'static str = "System's param_state was not found. Did you forget to initialize this system before running it?";
474}
475
476impl<Marker, F> System for FunctionSystem<Marker, F>
477where
478 Marker: 'static,
479 F: SystemParamFunction<Marker>,
480{
481 type In = F::In;
482 type Out = F::Out;
483
484 #[inline]
485 fn name(&self) -> Cow<'static, str> {
486 self.system_meta.name.clone()
487 }
488
489 #[inline]
490 fn component_access(&self) -> &Access<ComponentId> {
491 self.system_meta.component_access_set.combined_access()
492 }
493
494 #[inline]
495 fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
496 &self.system_meta.archetype_component_access
497 }
498
499 #[inline]
500 fn is_send(&self) -> bool {
501 self.system_meta.is_send
502 }
503
504 #[inline]
505 fn is_exclusive(&self) -> bool {
506 false
507 }
508
509 #[inline]
510 fn has_deferred(&self) -> bool {
511 self.system_meta.has_deferred
512 }
513
514 #[inline]
515 unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out {
516 #[cfg(feature = "trace")]
517 let _span_guard = self.system_meta.system_span.enter();
518
519 let change_tick = world.increment_change_tick();
520
521 // SAFETY:
522 // - The caller has invoked `update_archetype_component_access`, which will panic
523 // if the world does not match.
524 // - All world accesses used by `F::Param` have been registered, so the caller
525 // will ensure that there are no data access conflicts.
526 let params = unsafe {
527 F::Param::get_param(
528 self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
529 &self.system_meta,
530 world,
531 change_tick,
532 )
533 };
534 let out = self.func.run(input, params);
535 self.system_meta.last_run = change_tick;
536 out
537 }
538
539 #[inline]
540 fn apply_deferred(&mut self, world: &mut World) {
541 let param_state = self.param_state.as_mut().expect(Self::PARAM_MESSAGE);
542 F::Param::apply(param_state, &self.system_meta, world);
543 }
544
545 #[inline]
546 fn queue_deferred(&mut self, world: DeferredWorld) {
547 let param_state = self.param_state.as_mut().expect(Self::PARAM_MESSAGE);
548 F::Param::queue(param_state, &self.system_meta, world);
549 }
550
551 #[inline]
552 fn initialize(&mut self, world: &mut World) {
553 if let Some(id) = self.world_id {
554 assert_eq!(
555 id,
556 world.id(),
557 "System built with a different world than the one it was added to.",
558 );
559 } else {
560 self.world_id = Some(world.id());
561 self.param_state = Some(F::Param::init_state(world, &mut self.system_meta));
562 }
563 self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
564 }
565
566 fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
567 assert_eq!(self.world_id, Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
568 let archetypes = world.archetypes();
569 let old_generation =
570 std::mem::replace(&mut self.archetype_generation, archetypes.generation());
571
572 for archetype in &archetypes[old_generation..] {
573 let param_state = self.param_state.as_mut().unwrap();
574 // SAFETY: The assertion above ensures that the param_state was initialized from `world`.
575 unsafe { F::Param::new_archetype(param_state, archetype, &mut self.system_meta) };
576 }
577 }
578
579 #[inline]
580 fn check_change_tick(&mut self, change_tick: Tick) {
581 check_system_change_tick(
582 &mut self.system_meta.last_run,
583 change_tick,
584 self.system_meta.name.as_ref(),
585 );
586 }
587
588 fn default_system_sets(&self) -> Vec<InternedSystemSet> {
589 let set = crate::schedule::SystemTypeSet::<Self>::new();
590 vec![set.intern()]
591 }
592
593 fn get_last_run(&self) -> Tick {
594 self.system_meta.last_run
595 }
596
597 fn set_last_run(&mut self, last_run: Tick) {
598 self.system_meta.last_run = last_run;
599 }
600}
601
602/// SAFETY: `F`'s param is [`ReadOnlySystemParam`], so this system will only read from the world.
603unsafe impl<Marker, F> ReadOnlySystem for FunctionSystem<Marker, F>
604where
605 Marker: 'static,
606 F: SystemParamFunction<Marker>,
607 F::Param: ReadOnlySystemParam,
608{
609}
610
611/// A trait implemented for all functions that can be used as [`System`]s.
612///
613/// This trait can be useful for making your own systems which accept other systems,
614/// sometimes called higher order systems.
615///
616/// This should be used in combination with [`ParamSet`] when calling other systems
617/// within your system.
618/// Using [`ParamSet`] in this case avoids [`SystemParam`] collisions.
619///
620/// # Example
621///
622/// To create something like [`PipeSystem`], but in entirely safe code.
623///
624/// ```
625/// use std::num::ParseIntError;
626///
627/// use bevy_ecs::prelude::*;
628///
629/// /// Pipe creates a new system which calls `a`, then calls `b` with the output of `a`
630/// pub fn pipe<A, B, AMarker, BMarker>(
631/// mut a: A,
632/// mut b: B,
633/// ) -> impl FnMut(In<A::In>, ParamSet<(A::Param, B::Param)>) -> B::Out
634/// where
635/// // We need A and B to be systems, add those bounds
636/// A: SystemParamFunction<AMarker>,
637/// B: SystemParamFunction<BMarker, In = A::Out>,
638/// {
639/// // The type of `params` is inferred based on the return of this function above
640/// move |In(a_in), mut params| {
641/// let shared = a.run(a_in, params.p0());
642/// b.run(shared, params.p1())
643/// }
644/// }
645///
646/// // Usage example for `pipe`:
647/// fn main() {
648/// let mut world = World::default();
649/// world.insert_resource(Message("42".to_string()));
650///
651/// // pipe the `parse_message_system`'s output into the `filter_system`s input
652/// let mut piped_system = IntoSystem::into_system(pipe(parse_message, filter));
653/// piped_system.initialize(&mut world);
654/// assert_eq!(piped_system.run((), &mut world), Some(42));
655/// }
656///
657/// #[derive(Resource)]
658/// struct Message(String);
659///
660/// fn parse_message(message: Res<Message>) -> Result<usize, ParseIntError> {
661/// message.0.parse::<usize>()
662/// }
663///
664/// fn filter(In(result): In<Result<usize, ParseIntError>>) -> Option<usize> {
665/// result.ok().filter(|&n| n < 100)
666/// }
667/// ```
668/// [`PipeSystem`]: crate::system::PipeSystem
669/// [`ParamSet`]: crate::system::ParamSet
670#[diagnostic::on_unimplemented(
671 message = "`{Self}` is not a valid system",
672 label = "invalid system"
673)]
674pub trait SystemParamFunction<Marker>: Send + Sync + 'static {
675 /// The input type to this system. See [`System::In`].
676 type In;
677
678 /// The return type of this system. See [`System::Out`].
679 type Out;
680
681 /// The [`SystemParam`]/s used by this system to access the [`World`].
682 type Param: SystemParam;
683
684 /// Executes this system once. See [`System::run`] or [`System::run_unsafe`].
685 fn run(&mut self, input: Self::In, param_value: SystemParamItem<Self::Param>) -> Self::Out;
686}
687
688macro_rules! impl_system_function {
689 ($($param: ident),*) => {
690 #[allow(non_snake_case)]
691 impl<Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn($($param,)*) -> Out> for Func
692 where
693 for <'a> &'a mut Func:
694 FnMut($($param),*) -> Out +
695 FnMut($(SystemParamItem<$param>),*) -> Out, Out: 'static
696 {
697 type In = ();
698 type Out = Out;
699 type Param = ($($param,)*);
700 #[inline]
701 fn run(&mut self, _input: (), param_value: SystemParamItem< ($($param,)*)>) -> Out {
702 // Yes, this is strange, but `rustc` fails to compile this impl
703 // without using this function. It fails to recognize that `func`
704 // is a function, potentially because of the multiple impls of `FnMut`
705 #[allow(clippy::too_many_arguments)]
706 fn call_inner<Out, $($param,)*>(
707 mut f: impl FnMut($($param,)*)->Out,
708 $($param: $param,)*
709 )->Out{
710 f($($param,)*)
711 }
712 let ($($param,)*) = param_value;
713 call_inner(self, $($param),*)
714 }
715 }
716
717 #[allow(non_snake_case)]
718 impl<Input, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(In<Input>, $($param,)*) -> Out> for Func
719 where
720 for <'a> &'a mut Func:
721 FnMut(In<Input>, $($param),*) -> Out +
722 FnMut(In<Input>, $(SystemParamItem<$param>),*) -> Out, Out: 'static
723 {
724 type In = Input;
725 type Out = Out;
726 type Param = ($($param,)*);
727 #[inline]
728 fn run(&mut self, input: Input, param_value: SystemParamItem< ($($param,)*)>) -> Out {
729 #[allow(clippy::too_many_arguments)]
730 fn call_inner<Input, Out, $($param,)*>(
731 mut f: impl FnMut(In<Input>, $($param,)*)->Out,
732 input: In<Input>,
733 $($param: $param,)*
734 )->Out{
735 f(input, $($param,)*)
736 }
737 let ($($param,)*) = param_value;
738 call_inner(self, In(input), $($param),*)
739 }
740 }
741 };
742}
743
744// Note that we rely on the highest impl to be <= the highest order of the tuple impls
745// of `SystemParam` created.
746all_tuples!(impl_system_function, 0, 16, F);
747
748#[cfg(test)]
749mod tests {
750 use super::*;
751
752 #[test]
753 fn into_system_type_id_consistency() {
754 fn test<T, In, Out, Marker>(function: T)
755 where
756 T: IntoSystem<In, Out, Marker> + Copy,
757 {
758 fn reference_system() {}
759
760 use std::any::TypeId;
761
762 let system = IntoSystem::into_system(function);
763
764 assert_eq!(
765 system.type_id(),
766 function.system_type_id(),
767 "System::type_id should be consistent with IntoSystem::system_type_id"
768 );
769
770 assert_eq!(
771 system.type_id(),
772 TypeId::of::<T::System>(),
773 "System::type_id should be consistent with TypeId::of::<T::System>()"
774 );
775
776 assert_ne!(
777 system.type_id(),
778 IntoSystem::into_system(reference_system).type_id(),
779 "Different systems should have different TypeIds"
780 );
781 }
782
783 fn function_system() {}
784
785 test(function_system);
786 }
787}