bevy_ecs/schedule/config.rs
1use alloc::{boxed::Box, vec, vec::Vec};
2use variadics_please::all_tuples;
3
4use crate::{
5 schedule::{
6 auto_insert_apply_deferred::IgnoreDeferred,
7 condition::{BoxedCondition, SystemCondition},
8 graph::{Ambiguity, Dependency, DependencyKind, GraphInfo},
9 set::{InternedSystemSet, IntoSystemSet, SystemSet},
10 Chain, Weak,
11 },
12 system::{BoxedSystem, IntoSystem, ScheduleSystem, System},
13};
14
15fn new_condition<M>(condition: impl SystemCondition<M>) -> BoxedCondition {
16 let condition_system = IntoSystem::into_system(condition);
17 assert!(
18 condition_system.is_send(),
19 "SystemCondition `{}` accesses `NonSend` resources. This is not currently supported.",
20 condition_system.name()
21 );
22
23 Box::new(condition_system)
24}
25
26fn ambiguous_with(graph_info: &mut GraphInfo, set: InternedSystemSet) {
27 match &mut graph_info.ambiguous_with {
28 detection @ Ambiguity::Check => {
29 *detection = Ambiguity::IgnoreWithSet(vec![set]);
30 }
31 Ambiguity::IgnoreWithSet(ambiguous_with) => {
32 ambiguous_with.push(set);
33 }
34 Ambiguity::IgnoreAll => (),
35 }
36}
37
38/// Stores data to differentiate different schedulable structs.
39pub trait Schedulable {
40 /// Additional data used to configure independent scheduling. Stored in [`ScheduleConfig`].
41 type Metadata;
42 /// Additional data used to configure a schedulable group. Stored in [`ScheduleConfigs`].
43 type GroupMetadata;
44
45 /// Initializes a configuration from this node.
46 fn into_config(self) -> ScheduleConfig<Self>
47 where
48 Self: Sized;
49}
50
51impl Schedulable for ScheduleSystem {
52 type Metadata = GraphInfo;
53 type GroupMetadata = Chain;
54
55 fn into_config(self) -> ScheduleConfig<Self> {
56 let sets = self.default_system_sets().clone();
57 ScheduleConfig {
58 node: self,
59 metadata: GraphInfo {
60 hierarchy: sets,
61 ..Default::default()
62 },
63 conditions: Vec::new(),
64 }
65 }
66}
67
68impl Schedulable for InternedSystemSet {
69 type Metadata = GraphInfo;
70 type GroupMetadata = Chain;
71
72 fn into_config(self) -> ScheduleConfig<Self> {
73 assert!(
74 self.system_type().is_none(),
75 "configuring system type sets is not allowed"
76 );
77
78 ScheduleConfig {
79 node: self,
80 metadata: GraphInfo::default(),
81 conditions: Vec::new(),
82 }
83 }
84}
85
86/// Stores configuration for a single generic node (a system or a system set)
87///
88/// The configuration includes the node itself, scheduling metadata
89/// (hierarchy: in which sets is the node contained,
90/// dependencies: before/after which other nodes should this node run)
91/// and the run conditions associated with this node.
92pub struct ScheduleConfig<T: Schedulable> {
93 pub(crate) node: T,
94 pub(crate) metadata: T::Metadata,
95 pub(crate) conditions: Vec<BoxedCondition>,
96}
97
98/// Single or nested configurations for [`Schedulable`]s.
99#[must_use]
100pub enum ScheduleConfigs<T: Schedulable> {
101 /// Configuration for a single [`Schedulable`].
102 ScheduleConfig(ScheduleConfig<T>),
103 /// Configuration for a tuple of nested `Configs` instances.
104 Configs {
105 /// Configuration for each element of the tuple.
106 configs: Vec<ScheduleConfigs<T>>,
107 /// Run conditions applied to everything in the tuple.
108 collective_conditions: Vec<BoxedCondition>,
109 /// Metadata to be applied to all elements in the tuple.
110 metadata: T::GroupMetadata,
111 },
112}
113
114impl<T: Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>> ScheduleConfigs<T> {
115 /// Adds a new boxed system set to the systems.
116 pub fn in_set_inner(&mut self, set: InternedSystemSet) {
117 match self {
118 Self::ScheduleConfig(config) => {
119 config.metadata.hierarchy.push(set);
120 }
121 Self::Configs { configs, .. } => {
122 for config in configs {
123 config.in_set_inner(set);
124 }
125 }
126 }
127 }
128
129 fn before_inner(&mut self, set: InternedSystemSet) {
130 match self {
131 Self::ScheduleConfig(config) => {
132 config
133 .metadata
134 .dependencies
135 .push(Dependency::new(DependencyKind::Before, set));
136 }
137 Self::Configs { configs, .. } => {
138 for config in configs {
139 config.before_inner(set);
140 }
141 }
142 }
143 }
144
145 fn after_inner(&mut self, set: InternedSystemSet) {
146 match self {
147 Self::ScheduleConfig(config) => {
148 config
149 .metadata
150 .dependencies
151 .push(Dependency::new(DependencyKind::After, set));
152 }
153 Self::Configs { configs, .. } => {
154 for config in configs {
155 config.after_inner(set);
156 }
157 }
158 }
159 }
160
161 fn before_weak_inner(&mut self, set: InternedSystemSet) {
162 match self {
163 Self::ScheduleConfig(config) => {
164 config
165 .metadata
166 .dependencies
167 .push(Dependency::new(DependencyKind::Before, set).add_config(Weak));
168 }
169 Self::Configs { configs, .. } => {
170 for config in configs {
171 config.before_weak_inner(set);
172 }
173 }
174 }
175 }
176
177 fn after_weak_inner(&mut self, set: InternedSystemSet) {
178 match self {
179 Self::ScheduleConfig(config) => {
180 config
181 .metadata
182 .dependencies
183 .push(Dependency::new(DependencyKind::After, set).add_config(Weak));
184 }
185 Self::Configs { configs, .. } => {
186 for config in configs {
187 config.after_weak_inner(set);
188 }
189 }
190 }
191 }
192
193 fn before_ignore_deferred_inner(&mut self, set: InternedSystemSet) {
194 match self {
195 Self::ScheduleConfig(config) => {
196 config
197 .metadata
198 .dependencies
199 .push(Dependency::new(DependencyKind::Before, set).add_config(IgnoreDeferred));
200 }
201 Self::Configs { configs, .. } => {
202 for config in configs {
203 config.before_ignore_deferred_inner(set.intern());
204 }
205 }
206 }
207 }
208
209 fn after_ignore_deferred_inner(&mut self, set: InternedSystemSet) {
210 match self {
211 Self::ScheduleConfig(config) => {
212 config
213 .metadata
214 .dependencies
215 .push(Dependency::new(DependencyKind::After, set).add_config(IgnoreDeferred));
216 }
217 Self::Configs { configs, .. } => {
218 for config in configs {
219 config.after_ignore_deferred_inner(set.intern());
220 }
221 }
222 }
223 }
224
225 fn distributive_run_if_inner<M>(&mut self, condition: impl SystemCondition<M> + Clone) {
226 match self {
227 Self::ScheduleConfig(config) => {
228 config.conditions.push(new_condition(condition));
229 }
230 Self::Configs { configs, .. } => {
231 for config in configs {
232 config.distributive_run_if_inner(condition.clone());
233 }
234 }
235 }
236 }
237
238 fn ambiguous_with_inner(&mut self, set: InternedSystemSet) {
239 match self {
240 Self::ScheduleConfig(config) => {
241 ambiguous_with(&mut config.metadata, set);
242 }
243 Self::Configs { configs, .. } => {
244 for config in configs {
245 config.ambiguous_with_inner(set);
246 }
247 }
248 }
249 }
250
251 fn ambiguous_with_all_inner(&mut self) {
252 match self {
253 Self::ScheduleConfig(config) => {
254 config.metadata.ambiguous_with = Ambiguity::IgnoreAll;
255 }
256 Self::Configs { configs, .. } => {
257 for config in configs {
258 config.ambiguous_with_all_inner();
259 }
260 }
261 }
262 }
263
264 /// Adds a new boxed run condition to the systems.
265 ///
266 /// This is useful if you have a run condition whose concrete type is unknown.
267 /// Prefer `run_if` for run conditions whose type is known at compile time.
268 pub fn run_if_dyn(&mut self, condition: BoxedCondition) {
269 match self {
270 Self::ScheduleConfig(config) => {
271 config.conditions.push(condition);
272 }
273 Self::Configs {
274 collective_conditions,
275 ..
276 } => {
277 collective_conditions.push(condition);
278 }
279 }
280 }
281
282 fn chain_inner(mut self) -> Self {
283 match &mut self {
284 Self::ScheduleConfig(_) => { /* no op */ }
285 Self::Configs { metadata, .. } => {
286 metadata.set_chained();
287 }
288 };
289 self
290 }
291
292 fn chain_ignore_deferred_inner(mut self) -> Self {
293 match &mut self {
294 Self::ScheduleConfig(_) => { /* no op */ }
295 Self::Configs { metadata, .. } => {
296 metadata.set_chained_with_config(IgnoreDeferred);
297 }
298 }
299 self
300 }
301
302 fn chain_weak_inner(mut self) -> Self {
303 match &mut self {
304 Self::ScheduleConfig(_) => { /* no op */ }
305 Self::Configs { metadata, .. } => {
306 metadata.set_chained_with_config(Weak);
307 }
308 }
309 self
310 }
311}
312
313/// Types that can convert into a [`ScheduleConfigs`].
314///
315/// This trait is implemented for "systems" (functions whose arguments all implement
316/// [`SystemParam`](crate::system::SystemParam)), or tuples thereof.
317/// It is a common entry point for system configurations.
318///
319/// # Usage notes
320///
321/// This trait should only be used as a bound for trait implementations or as an
322/// argument to a function. If system configs need to be returned from a
323/// function or stored somewhere, use [`ScheduleConfigs`] instead of this trait.
324///
325/// # Examples
326///
327/// ```
328/// # use bevy_ecs::{schedule::IntoScheduleConfigs, system::ScheduleSystem};
329/// # struct AppMock;
330/// # struct Update;
331/// # impl AppMock {
332/// # pub fn add_systems<M>(
333/// # &mut self,
334/// # schedule: Update,
335/// # systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
336/// # ) -> &mut Self { self }
337/// # }
338/// # let mut app = AppMock;
339///
340/// fn handle_input() {}
341///
342/// fn update_camera() {}
343/// fn update_character() {}
344///
345/// app.add_systems(
346/// Update,
347/// (
348/// handle_input,
349/// (update_camera, update_character).after(handle_input)
350/// )
351/// );
352/// ```
353#[diagnostic::on_unimplemented(
354 message = "`{Self}` does not describe a valid system configuration",
355 label = "invalid system configuration"
356)]
357pub trait IntoScheduleConfigs<T: Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>, Marker>:
358 Sized
359{
360 /// Convert into a [`ScheduleConfigs`].
361 fn into_configs(self) -> ScheduleConfigs<T>;
362
363 /// Add these systems to the provided `set`.
364 #[track_caller]
365 fn in_set(self, set: impl SystemSet) -> ScheduleConfigs<T> {
366 self.into_configs().in_set(set)
367 }
368
369 /// Runs before all systems in `set`. If `self` has any systems that produce [`Commands`](crate::system::Commands)
370 /// or other [`Deferred`](crate::system::Deferred) operations, all systems in `set` will see their effect.
371 ///
372 /// If automatically inserting [`ApplyDeferred`](crate::schedule::ApplyDeferred) like
373 /// this isn't desired, use [`before_ignore_deferred`](Self::before_ignore_deferred) instead.
374 ///
375 /// Calling [`.chain`](Self::chain) is often more convenient and ensures that all systems are added to the schedule.
376 /// Please check the [caveats section of `.after`](Self::after) for details.
377 fn before<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
378 self.into_configs().before(set)
379 }
380
381 /// Run after all systems in `set`. If `set` has any systems that produce [`Commands`](crate::system::Commands)
382 /// or other [`Deferred`](crate::system::Deferred) operations, all systems in `self` will see their effect.
383 ///
384 /// If automatically inserting [`ApplyDeferred`](crate::schedule::ApplyDeferred) like
385 /// this isn't desired, use [`after_ignore_deferred`](Self::after_ignore_deferred) instead.
386 ///
387 /// Calling [`.chain`](Self::chain) is often more convenient and ensures that all systems are added to the schedule.
388 ///
389 /// # Caveats
390 ///
391 /// If you configure two [`System`]s like `(GameSystem::A).after(GameSystem::B)` or `(GameSystem::A).before(GameSystem::B)`, the `GameSystem::B` will not be automatically scheduled.
392 ///
393 /// This means that the system `GameSystem::A` and the system or systems in `GameSystem::B` will run independently of each other if `GameSystem::B` was never explicitly scheduled with [`configure_sets`]
394 /// If that is the case, `.after`/`.before` will not provide the desired behavior
395 /// and the systems can run in parallel or in any order determined by the scheduler.
396 /// Only use `after(GameSystem::B)` and `before(GameSystem::B)` when you know that `B` has already been scheduled for you,
397 /// e.g. when it was provided by Bevy or a third-party dependency,
398 /// or you manually scheduled it somewhere else in your app.
399 ///
400 /// Another caveat is that if `GameSystem::B` is placed in a different schedule than `GameSystem::A`,
401 /// any ordering calls between them—whether using `.before`, `.after`, or `.chain`—will be silently ignored.
402 ///
403 /// [`configure_sets`]: https://docs.rs/bevy/latest/bevy/app/struct.App.html#method.configure_sets
404 fn after<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
405 self.into_configs().after(set)
406 }
407
408 /// Run before all systems in `set`.
409 ///
410 /// Unlike [`before`](Self::before), this will not cause the systems in
411 /// `set` to wait for the deferred effects of `self` to be applied.
412 fn before_ignore_deferred<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
413 self.into_configs().before_ignore_deferred(set)
414 }
415
416 /// Run after all systems in `set`.
417 ///
418 /// Unlike [`after`](Self::after), this will not wait for the deferred
419 /// effects of systems in `set` to be applied.
420 fn after_ignore_deferred<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
421 self.into_configs().after_ignore_deferred(set)
422 }
423
424 /// Run before the systems in `set` that `self` actually conflicts with, leaving the rest
425 /// unordered.
426 ///
427 /// Like [`before`](Self::before), this requests that `self` run before `set`. Unlike
428 /// [`before`](Self::before), the ordering is kept only between systems whose data accesses
429 /// conflict, where a system's run conditions (and those of its sets) count toward its access.
430 /// Systems that don't conflict are left unordered and may run in any order,
431 /// including in parallel. This is useful for ordering against large groups of systems (such
432 /// as system sets) without serializing systems that don't actually depend on each other.
433 ///
434 /// A `self` that produces deferred effects such as [`Commands`](crate::system::Commands)
435 /// (with an [`ApplyDeferred`](crate::schedule::ApplyDeferred) inserted as usual) and
436 /// exclusive systems are treated as always conflicting, so their ordering is always kept.
437 ///
438 /// Dependencies the scheduler can't see (interior mutability on read-only accesses, global state, etc.)
439 /// are **not** respected, so only use this when the systems don't rely on such hidden data dependencies.
440 ///
441 /// A weak ordering cannot be combined with an
442 /// [`ignore_deferred`](Self::before_ignore_deferred) one on a single edge. Configuring both
443 /// for the same pair adds a strict ordering alongside the weak one, and a strict ordering
444 /// always wins: the ordering is kept even between systems that don't conflict, just without
445 /// the sync point.
446 fn before_weak<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
447 self.into_configs().before_weak(set)
448 }
449
450 /// Run after the systems in `set` that `self` actually conflicts with, leaving the rest
451 /// unordered.
452 ///
453 /// Like [`after`](Self::after), this requests that `self` run after `set`. Unlike
454 /// [`after`](Self::after), the ordering is kept only between systems whose data accesses
455 /// conflict, where a system's run conditions (and those of its sets) count toward its access.
456 /// Systems that don't conflict are left unordered and may run in any order,
457 /// including in parallel. This is useful for ordering against large groups of systems (such
458 /// as system sets) without serializing systems that don't actually depend on each other.
459 ///
460 /// A system in `set` that produces deferred effects such as [`Commands`](crate::system::Commands)
461 /// (with an [`ApplyDeferred`](crate::schedule::ApplyDeferred) inserted as usual) and exclusive
462 /// systems are treated as always conflicting, so their ordering is always kept.
463 ///
464 /// Dependencies the scheduler can't see (interior mutability on read-only accesses, global state, etc.)
465 /// are **not** respected, so only use this when the systems don't rely on such hidden data dependencies.
466 ///
467 /// A weak ordering cannot be combined with an
468 /// [`ignore_deferred`](Self::after_ignore_deferred) one on a single edge. Configuring both
469 /// for the same pair adds a strict ordering alongside the weak one, and a strict ordering
470 /// always wins: the ordering is kept even between systems that don't conflict, just without
471 /// the sync point.
472 fn after_weak<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
473 self.into_configs().after_weak(set)
474 }
475
476 /// Add a run condition to each contained system.
477 ///
478 /// Each system will receive its own clone of the [`SystemCondition`] and will only run
479 /// if the `SystemCondition` is true.
480 ///
481 /// Each individual condition will be evaluated at most once (per schedule run),
482 /// right before the corresponding system prepares to run.
483 ///
484 /// This is equivalent to calling [`run_if`](IntoScheduleConfigs::run_if) on each individual
485 /// system, as shown below:
486 ///
487 /// ```
488 /// # use bevy_ecs::prelude::*;
489 /// # let mut schedule = Schedule::default();
490 /// # fn a() {}
491 /// # fn b() {}
492 /// # fn condition() -> bool { true }
493 /// schedule.add_systems((a, b).distributive_run_if(condition));
494 /// schedule.add_systems((a.run_if(condition), b.run_if(condition)));
495 /// ```
496 ///
497 /// # Note
498 ///
499 /// Because the conditions are evaluated separately for each system, there is no guarantee
500 /// that all evaluations in a single schedule run will yield the same result. If another
501 /// system is run inbetween two evaluations it could cause the result of the condition to change.
502 ///
503 /// Use [`run_if`](ScheduleConfigs::run_if) on a [`SystemSet`] if you want to make sure
504 /// that either all or none of the systems are run, or you don't want to evaluate the run
505 /// condition for each contained system separately.
506 fn distributive_run_if<M>(
507 self,
508 condition: impl SystemCondition<M> + Clone,
509 ) -> ScheduleConfigs<T> {
510 self.into_configs().distributive_run_if(condition)
511 }
512
513 /// Run the systems only if the [`SystemCondition`] is `true`.
514 ///
515 /// The `SystemCondition` will be evaluated at most once (per schedule run),
516 /// the first time a system in this set prepares to run.
517 ///
518 /// If this set contains more than one system, calling `run_if` is equivalent to adding each
519 /// system to a common set and configuring the run condition on that set, as shown below:
520 ///
521 /// # Examples
522 ///
523 /// ```
524 /// # use bevy_ecs::prelude::*;
525 /// # let mut schedule = Schedule::default();
526 /// # fn a() {}
527 /// # fn b() {}
528 /// # fn condition() -> bool { true }
529 /// # #[derive(SystemSet, Debug, Eq, PartialEq, Hash, Clone, Copy)]
530 /// # struct C;
531 /// schedule.add_systems((a, b).run_if(condition));
532 /// schedule.add_systems((a, b).in_set(C)).configure_sets(C.run_if(condition));
533 /// ```
534 ///
535 /// # Note
536 ///
537 /// Because the condition will only be evaluated once, there is no guarantee that the condition
538 /// is upheld after the first system has run. You need to make sure that no other systems that
539 /// could invalidate the condition are scheduled inbetween the first and last run system.
540 ///
541 /// Use [`distributive_run_if`](IntoScheduleConfigs::distributive_run_if) if you want the
542 /// condition to be evaluated for each individual system, right before one is run.
543 fn run_if<M>(self, condition: impl SystemCondition<M>) -> ScheduleConfigs<T> {
544 self.into_configs().run_if(condition)
545 }
546
547 /// Suppress warnings and errors that would result from these systems having ambiguities
548 /// (conflicting access but indeterminate order) with systems in `set`.
549 fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> ScheduleConfigs<T> {
550 self.into_configs().ambiguous_with(set)
551 }
552
553 /// Suppress warnings and errors that would result from these systems having ambiguities
554 /// (conflicting access but indeterminate order) with any other system.
555 fn ambiguous_with_all(self) -> ScheduleConfigs<T> {
556 self.into_configs().ambiguous_with_all()
557 }
558
559 /// Treat this collection as a sequence of systems.
560 ///
561 /// Ordering constraints will be applied between the successive elements.
562 ///
563 /// If the preceding node on an edge has deferred parameters, an [`ApplyDeferred`](crate::schedule::ApplyDeferred)
564 /// will be inserted on the edge. If this behavior is not desired consider using
565 /// [`chain_ignore_deferred`](Self::chain_ignore_deferred) instead.
566 fn chain(self) -> ScheduleConfigs<T> {
567 self.into_configs().chain()
568 }
569
570 /// Treat this collection as a sequence of systems.
571 ///
572 /// Ordering constraints will be applied between the successive elements.
573 ///
574 /// Unlike [`chain`](Self::chain) this will **not** add [`ApplyDeferred`](crate::schedule::ApplyDeferred) on the edges.
575 fn chain_ignore_deferred(self) -> ScheduleConfigs<T> {
576 self.into_configs().chain_ignore_deferred()
577 }
578
579 /// Treat this collection as a sequence, but only order successive systems that actually
580 /// conflict and leave the rest unordered.
581 ///
582 /// Like [`chain`](Self::chain), this requests an ordering between the successive elements.
583 /// Unlike [`chain`](Self::chain), the ordering is kept only between systems whose data
584 /// accesses conflict, where a system's run conditions (and those of its sets) count toward
585 /// its access. Systems that don't conflict are left unordered and may run in any
586 /// order, including in parallel. Two systems that conflict only through a non-conflicting
587 /// system between them in the chain are still ordered. This is useful for ordering large
588 /// groups of systems (such as system sets) without serializing systems that don't actually
589 /// depend on each other.
590 ///
591 /// An earlier system that produces deferred effects such as [`Commands`](crate::system::Commands)
592 /// (with an [`ApplyDeferred`](crate::schedule::ApplyDeferred) inserted as usual) and exclusive
593 /// systems are treated as always conflicting, so their ordering is always kept.
594 ///
595 /// Dependencies the scheduler can't see (interior mutability on read-only accesses, global state, etc.)
596 /// are **not** respected, so only use this when the systems don't rely on such hidden data dependencies.
597 ///
598 /// A weak ordering cannot be combined with an
599 /// [`ignore_deferred`](Self::chain_ignore_deferred) one on a single edge. Configuring both
600 /// for the same pair adds a strict ordering alongside the weak one, and a strict ordering
601 /// always wins: the ordering is kept even between systems that don't conflict, just without
602 /// the sync point.
603 fn chain_weak(self) -> ScheduleConfigs<T> {
604 self.into_configs().chain_weak()
605 }
606}
607
608impl<T: Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>> IntoScheduleConfigs<T, ()>
609 for ScheduleConfigs<T>
610{
611 fn into_configs(self) -> Self {
612 self
613 }
614
615 #[track_caller]
616 fn in_set(mut self, set: impl SystemSet) -> Self {
617 assert!(
618 set.system_type().is_none(),
619 "adding arbitrary systems to a system type set is not allowed"
620 );
621
622 self.in_set_inner(set.intern());
623
624 self
625 }
626
627 fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
628 let set = set.into_system_set();
629 self.before_inner(set.intern());
630 self
631 }
632
633 fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
634 let set = set.into_system_set();
635 self.after_inner(set.intern());
636 self
637 }
638
639 fn before_ignore_deferred<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
640 let set = set.into_system_set();
641 self.before_ignore_deferred_inner(set.intern());
642 self
643 }
644
645 fn after_ignore_deferred<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
646 let set = set.into_system_set();
647 self.after_ignore_deferred_inner(set.intern());
648 self
649 }
650
651 fn before_weak<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
652 let set = set.into_system_set();
653 self.before_weak_inner(set.intern());
654 self
655 }
656
657 fn after_weak<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
658 let set = set.into_system_set();
659 self.after_weak_inner(set.intern());
660 self
661 }
662
663 fn distributive_run_if<M>(
664 mut self,
665 condition: impl SystemCondition<M> + Clone,
666 ) -> ScheduleConfigs<T> {
667 self.distributive_run_if_inner(condition);
668 self
669 }
670
671 fn run_if<M>(mut self, condition: impl SystemCondition<M>) -> ScheduleConfigs<T> {
672 self.run_if_dyn(new_condition(condition));
673 self
674 }
675
676 fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
677 let set = set.into_system_set();
678 self.ambiguous_with_inner(set.intern());
679 self
680 }
681
682 fn ambiguous_with_all(mut self) -> Self {
683 self.ambiguous_with_all_inner();
684 self
685 }
686
687 fn chain(self) -> Self {
688 self.chain_inner()
689 }
690
691 fn chain_ignore_deferred(self) -> Self {
692 self.chain_ignore_deferred_inner()
693 }
694
695 fn chain_weak(self) -> Self {
696 self.chain_weak_inner()
697 }
698}
699
700impl<F, Marker> IntoScheduleConfigs<ScheduleSystem, Marker> for F
701where
702 F: IntoSystem<(), (), Marker>,
703{
704 fn into_configs(self) -> ScheduleConfigs<ScheduleSystem> {
705 let boxed_system = Box::new(IntoSystem::into_system(self));
706 ScheduleConfigs::ScheduleConfig(ScheduleSystem::into_config(boxed_system))
707 }
708}
709
710impl IntoScheduleConfigs<ScheduleSystem, ()> for BoxedSystem<(), ()> {
711 fn into_configs(self) -> ScheduleConfigs<ScheduleSystem> {
712 ScheduleConfigs::ScheduleConfig(ScheduleSystem::into_config(self))
713 }
714}
715
716impl<S: SystemSet> IntoScheduleConfigs<InternedSystemSet, ()> for S {
717 fn into_configs(self) -> ScheduleConfigs<InternedSystemSet> {
718 ScheduleConfigs::ScheduleConfig(InternedSystemSet::into_config(self.intern()))
719 }
720}
721
722#[doc(hidden)]
723pub struct ScheduleConfigTupleMarker;
724
725macro_rules! impl_node_type_collection {
726 ($(#[$meta:meta])* $(($param: ident, $sys: ident)),*) => {
727 $(#[$meta])*
728 impl<$($param, $sys),*, T: Schedulable<Metadata = GraphInfo, GroupMetadata = Chain>> IntoScheduleConfigs<T, (ScheduleConfigTupleMarker, $($param,)*)> for ($($sys,)*)
729 where
730 $($sys: IntoScheduleConfigs<T, $param>),*
731 {
732 #[expect(
733 clippy::allow_attributes,
734 reason = "We are inside a macro, and as such, `non_snake_case` is not guaranteed to apply."
735 )]
736 #[allow(
737 non_snake_case,
738 reason = "Variable names are provided by the macro caller, not by us."
739 )]
740 fn into_configs(self) -> ScheduleConfigs<T> {
741 let ($($sys,)*) = self;
742 ScheduleConfigs::Configs {
743 metadata: Default::default(),
744 configs: vec![$($sys.into_configs(),)*],
745 collective_conditions: Vec::new(),
746 }
747 }
748 }
749 }
750}
751
752all_tuples!(
753 #[doc(fake_variadic)]
754 impl_node_type_collection,
755 1,
756 20,
757 P,
758 S
759);