Skip to main content

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);