1mod context;
2mod input;
3mod traits;
4
5use crate::{
6 archetype::{ArchetypeId, ArchetypeInfo},
7 component::ComponentKey,
8 query::{QueryData, QueryStrategy},
9 util::TuplePush,
10 CommandBuffer, Fetch, FetchItem, Query, World,
11};
12use alloc::{
13 boxed::Box,
14 collections::BTreeMap,
15 format,
16 string::{String, ToString},
17 vec::Vec,
18};
19use core::{
20 any::{type_name, TypeId},
21 fmt::{self, Formatter},
22 marker::PhantomData,
23};
24
25pub use context::*;
26pub use input::IntoInput;
27pub use traits::{AsBorrowed, SystemAccess, SystemData, SystemFn};
28
29use self::traits::{WithCmd, WithCmdMut, WithInput, WithInputMut, WithWorld, WithWorldMut};
30
31#[cfg(feature = "rayon")]
32use rayon::prelude::{ParallelBridge, ParallelIterator};
33
34pub struct SystemBuilder<Args> {
37 args: Args,
38 name: Option<String>,
39}
40
41impl SystemBuilder<()> {
42 pub fn new() -> Self {
44 Self {
45 args: (),
46 name: None,
47 }
48 }
49}
50
51impl Default for SystemBuilder<()> {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57#[doc(hidden)]
58pub struct ForEach<Func> {
59 func: Func,
60}
61
62impl<'a, Func, Q, F> SystemFn<'a, (QueryData<'a, Q, F>,), ()> for ForEach<Func>
63where
64 for<'x> Q: Fetch<'x>,
65 for<'x> F: Fetch<'x>,
66 for<'x> Func: FnMut(<Q as FetchItem<'x>>::Item),
67{
68 fn execute(&mut self, mut data: (QueryData<Q, F>,)) {
69 for item in &mut data.0.borrow() {
70 (self.func)(item)
71 }
72 }
73}
74
75#[doc(hidden)]
76pub struct TryForEach<Func, E> {
77 func: Func,
78 _marker: PhantomData<E>,
79}
80
81impl<'a, Func, Q, F, E> SystemFn<'a, (QueryData<'a, Q, F>,), Result<(), E>> for TryForEach<Func, E>
82where
83 for<'x> Q: Fetch<'x>,
84 for<'x> F: Fetch<'x>,
85 for<'x> Func: FnMut(<Q as FetchItem<'x>>::Item) -> Result<(), E>,
86 E: Send + Sync,
87{
88 fn execute(&mut self, mut data: (QueryData<Q, F>,)) -> Result<(), E> {
89 for item in &mut data.0.borrow() {
90 (self.func)(item)?;
91 }
92
93 Ok(())
94 }
95}
96#[cfg(feature = "rayon")]
98pub struct ParForEach<F> {
99 func: F,
100}
101
102#[cfg(feature = "rayon")]
103impl<'a, Func, Q, F> SystemFn<'a, (QueryData<'a, Q, F>,), ()> for ParForEach<Func>
104where
105 for<'x> Q: Fetch<'x>,
106 for<'x> F: Fetch<'x>,
107 for<'x> <crate::filter::Filtered<Q, F> as Fetch<'x>>::Prepared: Send,
108 for<'x, 'y> <<Q as Fetch<'x>>::Prepared as crate::fetch::PreparedFetch<'y>>::Chunk: Send,
109 for<'x> Func: Fn(<Q as FetchItem<'x>>::Item) + Send + Sync,
110{
111 fn execute(&mut self, mut data: (QueryData<Q, F>,)) {
112 let mut borrow = data.0.borrow();
113 borrow
114 .iter_batched()
115 .par_bridge()
116 .for_each(|v| v.for_each(&self.func));
117 }
118}
119
120pub(crate) type TrySystem<F, Args, Err> = System<F, Args, Result<(), Err>>;
121
122impl<Q, F> SystemBuilder<(Query<Q, F>,)>
123where
124 for<'x> Q: Fetch<'x> + 'static,
125 for<'x> F: Fetch<'x> + 'static,
126{
127 pub fn for_each<Func>(self, func: Func) -> System<ForEach<Func>, (Query<Q, F>,), ()>
129 where
130 for<'x> Func: FnMut(<Q as FetchItem<'x>>::Item),
131 {
132 System::new(
133 self.name.unwrap_or_else(|| type_name::<Func>().to_string()),
134 ForEach { func },
135 self.args,
136 )
137 }
138
139 pub fn try_for_each<Func, E>(
141 self,
142 func: Func,
143 ) -> TrySystem<TryForEach<Func, E>, (Query<Q, F>,), E>
144 where
145 E: Into<anyhow::Error>,
146 for<'x> Func: FnMut(<Q as FetchItem<'x>>::Item) -> Result<(), E>,
147 {
148 System::new(
149 self.name.unwrap_or_else(|| type_name::<Func>().to_string()),
150 TryForEach {
151 func,
152 _marker: PhantomData,
153 },
154 self.args,
155 )
156 }
157}
158
159#[cfg(feature = "rayon")]
160impl<Q, F> SystemBuilder<(Query<Q, F>,)>
161where
162 for<'x> Q: 'static + Fetch<'x> + Send,
163 for<'x> F: 'static + Fetch<'x> + Send,
164 for<'x> <<Q as Fetch<'x>>::Prepared as crate::fetch::PreparedFetch<'x>>::Chunk: Send,
165 {
167 pub fn par_for_each<Func>(self, func: Func) -> System<ParForEach<Func>, (Query<Q, F>,), ()>
169 where
170 for<'x> Func: Fn(<Q as FetchItem<'x>>::Item) + Send + Sync,
171 {
172 System::new(
173 self.name.unwrap_or_else(|| type_name::<Func>().to_string()),
174 ParForEach { func },
175 self.args,
176 )
177 }
178}
179
180impl<Args> SystemBuilder<Args> {
181 pub fn with_query<Q, F, S>(self, query: Query<Q, F, S>) -> SystemBuilder<Args::PushRight>
189 where
190 Q: 'static + for<'x> Fetch<'x>,
191 F: 'static + for<'x> Fetch<'x>,
192 S: 'static + for<'x> QueryStrategy<'x, Q, F>,
193 Args: TuplePush<Query<Q, F, S>>,
194 {
195 self.with(query)
196 }
197 pub fn with_world(self) -> SystemBuilder<Args::PushRight>
202 where
203 Args: TuplePush<WithWorld>,
204 {
205 self.with(WithWorld)
206 }
207
208 pub fn with_world_mut(self) -> SystemBuilder<Args::PushRight>
210 where
211 Args: TuplePush<WithWorldMut>,
212 {
213 self.with(WithWorldMut)
214 }
215
216 pub fn with_cmd(self) -> SystemBuilder<Args::PushRight>
218 where
219 Args: TuplePush<WithCmd>,
220 {
221 self.with(WithCmd)
222 }
223
224 pub fn with_cmd_mut(self) -> SystemBuilder<Args::PushRight>
228 where
229 Args: TuplePush<WithCmdMut>,
230 {
231 self.with(WithCmdMut)
232 }
233
234 pub fn with_input<T>(self) -> SystemBuilder<Args::PushRight>
236 where
237 T: 'static,
238 Args: TuplePush<WithInput<T>>,
239 {
240 self.with(WithInput::<T>(PhantomData))
241 }
242
243 pub fn with_input_mut<T>(self) -> SystemBuilder<Args::PushRight>
245 where
246 T: 'static,
247 Args: TuplePush<WithInputMut<T>>,
248 {
249 self.with(WithInputMut::<T>(PhantomData))
250 }
251
252 pub fn with_name(mut self, name: impl Into<String>) -> Self {
254 self.name = Some(name.into());
255 self
256 }
257
258 pub fn with_resource<R>(self, resource: SharedResource<R>) -> SystemBuilder<Args::PushRight>
264 where
265 Args: TuplePush<SharedResource<R>>,
266 R: Send + 'static,
267 {
268 self.with(resource)
269 }
270
271 pub fn build<Func, Ret>(self, func: Func) -> System<Func, Args, Ret>
273 where
274 Args: for<'a> SystemData<'a> + 'static,
275 Func: for<'this, 'a> SystemFn<'this, <Args as SystemData<'a>>::Value, Ret>,
276 {
277 System::new(
278 self.name.unwrap_or_else(|| type_name::<Func>().to_string()),
279 func,
280 self.args,
281 )
282 }
283
284 fn with<S>(self, other: S) -> SystemBuilder<Args::PushRight>
286 where
287 S: for<'x> SystemData<'x>,
288 Args: TuplePush<S>,
289 {
290 SystemBuilder {
291 name: self.name,
292 args: self.args.push_right(other),
293 }
294 }
295}
296
297pub struct System<F, Args, Ret> {
299 name: String,
300 data: Args,
301 func: F,
302 _marker: PhantomData<Ret>,
303}
304
305struct FormatWith<F> {
306 func: F,
307}
308
309impl<F> fmt::Debug for FormatWith<F>
310where
311 F: Fn(&mut Formatter<'_>) -> fmt::Result,
312{
313 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
314 (self.func)(f)
315 }
316}
317
318#[doc(hidden)]
320pub trait DynSystem {
321 fn name(&self) -> &str;
322 fn describe(&self, f: &mut Formatter<'_>) -> fmt::Result;
323 fn execute(&mut self, ctx: &SystemContext<'_, '_, '_>) -> anyhow::Result<()>;
324 fn access(&self, world: &World, dst: &mut Vec<Access>);
325}
326
327impl<F, Args, Err> DynSystem for System<F, Args, Result<(), Err>>
328where
329 Args: for<'x> SystemData<'x>,
330 F: for<'x> SystemFn<'x, <Args as SystemData<'x>>::Value, Result<(), Err>>,
331 Err: Into<anyhow::Error>,
332{
333 fn execute(&mut self, ctx: &SystemContext<'_, '_, '_>) -> anyhow::Result<()> {
334 profile_function!(self.name());
335
336 #[cfg(feature = "tracing")]
337 let _span = tracing::info_span!("system", name = self.name).entered();
338
339 let data = self.data.acquire(ctx);
340
341 let res: anyhow::Result<()> = self.func.execute(data).map_err(Into::into);
342 if let Err(err) = res {
343 return Err(err.context(format!("Failed to execute system: {:?}", self)));
344 }
345
346 Ok(())
347 }
348
349 fn describe(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
350 f.write_str("fn ")?;
351 f.write_str(&self.name)?;
352 self.data.describe(f)?;
353 f.write_str(" -> ")?;
354 f.write_str(&tynm::type_name::<core::result::Result<(), Err>>())?;
355
356 Ok(())
357 }
358
359 fn access(&self, world: &World, dst: &mut Vec<Access>) {
360 self.data.access(world, dst)
361 }
362
363 fn name(&self) -> &str {
364 &self.name
365 }
366}
367
368impl<F, Args> DynSystem for System<F, Args, ()>
369where
370 Args: for<'x> SystemData<'x>,
371 F: for<'x> SystemFn<'x, <Args as SystemData<'x>>::Value, ()>,
372{
373 fn execute(&mut self, ctx: &SystemContext<'_, '_, '_>) -> anyhow::Result<()> {
374 profile_function!(self.name());
375
376 #[cfg(feature = "tracing")]
377 let _span = tracing::info_span!("system", name = self.name).entered();
378
379 let data = {
380 profile_scope!("acquire_data");
381 self.data.acquire(ctx)
382 };
383
384 {
385 profile_scope!("exec");
386 self.func.execute(data);
387 }
388
389 Ok(())
390 }
391
392 fn describe(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
393 f.write_str("fn ")?;
394 f.write_str(&self.name)?;
395 self.data.describe(f)?;
396
397 Ok(())
398 }
399
400 fn access(&self, world: &World, dst: &mut Vec<Access>) {
401 self.data.access(world, dst)
402 }
403
404 fn name(&self) -> &str {
405 &self.name
406 }
407}
408
409impl<F, Args, Ret> fmt::Debug for System<F, Args, Ret>
410where
411 Self: DynSystem,
412{
413 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
414 self.describe(f)
415 }
416}
417
418impl<F, Args, Ret> System<F, Args, Ret> {
419 pub(crate) fn new(name: String, func: F, data: Args) -> Self {
420 Self {
421 name,
422 data,
423 func,
424 _marker: PhantomData,
425 }
426 }
427
428 pub fn boxed(self) -> BoxedSystem
430 where
431 Ret: Send + Sync + 'static,
432 Args: Send + Sync + 'static,
433 F: Send + Sync + 'static,
434 Self: DynSystem,
435 {
436 BoxedSystem::new(self)
437 }
438}
439
440impl System<(), (), ()> {
441 pub fn builder() -> SystemBuilder<()> {
443 SystemBuilder::new()
444 }
445}
446
447impl<F, Args, Ret> System<F, Args, Ret> {
448 pub fn run<'a>(&'a mut self, world: &'a mut World) -> Ret
450 where
451 Ret: 'static,
452 for<'x> Args: SystemData<'x>,
453 for<'x> F: SystemFn<'x, <Args as SystemData<'x>>::Value, Ret>,
454 {
455 self.run_with(world, &mut ())
456 }
457}
458
459impl<F, Args, Ret> System<F, Args, Ret> {
460 pub fn run_with<'a>(&mut self, world: &mut World, input: impl IntoInput<'a>) -> Ret
462 where
463 Ret: 'static,
464 for<'x> Args: SystemData<'x>,
465 for<'x> F: SystemFn<'x, <Args as SystemData<'x>>::Value, Ret>,
466 {
467 #[cfg(feature = "tracing")]
468 let _span = tracing::info_span!("run_on", name = self.name).entered();
469
470 let mut cmd = CommandBuffer::new();
471 let input = input.into_input();
472 let ctx = SystemContext::new(world, &mut cmd, &input);
473
474 let data = self.data.acquire(&ctx);
475
476 let ret = self.func.execute(data);
477 ctx.cmd_mut()
478 .apply(&mut ctx.world.borrow_mut())
479 .expect("Failed to apply commandbuffer");
480 ret
481 }
482}
483
484#[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
485pub enum AccessKind {
487 Archetype {
489 id: ArchetypeId,
491 component: ComponentKey,
493 },
494 External(TypeId),
496 World,
498 CommandBuffer,
500 Input(TypeId),
502}
503
504impl AccessKind {
505 #[must_use]
509 pub fn is_archetype(&self) -> bool {
510 matches!(self, Self::Archetype { .. })
511 }
512
513 #[must_use]
517 pub fn is_world(&self) -> bool {
518 matches!(self, Self::World)
519 }
520
521 #[must_use]
525 pub fn is_command_buffer(&self) -> bool {
526 matches!(self, Self::CommandBuffer)
527 }
528}
529
530#[derive(Default, Debug, Clone)]
532#[allow(dead_code)]
533struct ArchetypeAccess {
534 arch: ArchetypeInfo,
535 components: Vec<ComponentAccessInfo>,
536 change_events: Vec<ComponentAccessInfo>,
537}
538
539#[derive(Debug, Clone)]
540#[allow(dead_code)]
541struct ComponentAccessInfo {
542 mutable: bool,
543 name: &'static str,
544 id: ComponentKey,
545}
546
547#[derive(Default, Debug, Clone)]
549pub struct AccessInfo {
550 archetypes: BTreeMap<ArchetypeId, ArchetypeAccess>,
551 world: Option<bool>,
552 cmd: Option<bool>,
553 external: Vec<TypeId>,
554 input: Vec<(TypeId, bool)>,
555}
556
557#[derive(Hash, Debug, Clone, PartialEq, Eq)]
558pub struct Access {
561 pub kind: AccessKind,
563 pub mutable: bool,
565}
566
567pub(crate) fn access_info(accesses: &[Access], world: &World) -> AccessInfo {
569 let mut result = AccessInfo::default();
570 for access in accesses {
571 match access.kind {
572 AccessKind::Archetype { id, component } => {
573 let arch = world.archetypes.get(id);
574 result
575 .archetypes
576 .entry(id)
577 .or_insert_with(|| ArchetypeAccess {
578 arch: arch.desc(),
579 ..Default::default()
580 })
581 .components
582 .push(ComponentAccessInfo {
583 mutable: access.mutable,
584 name: arch.component(component).unwrap().name(),
585 id: component,
586 })
587 }
588 AccessKind::External(ty) => result.external.push(ty),
589 AccessKind::Input(ty) => {
590 result.input.push((ty, access.mutable));
591 }
592 AccessKind::World => match result.world {
593 Some(true) => result.world = Some(true),
594 _ => result.world = Some(access.mutable),
595 },
596 AccessKind::CommandBuffer => match result.cmd {
597 Some(true) => result.cmd = Some(true),
598 _ => result.cmd = Some(access.mutable),
599 },
600 }
601 }
602
603 result
604}
605
606impl Access {
607 pub(crate) fn is_compatible_with(&self, other: &Self) -> bool {
609 !(self.kind == other.kind && (self.mutable || other.mutable))
610 }
611}
612
613pub struct BoxedSystem {
615 inner: Box<dyn DynSystem + Send + Sync>,
616}
617
618impl core::fmt::Debug for BoxedSystem {
619 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
620 self.inner.describe(f)
621 }
622}
623
624impl BoxedSystem {
625 pub fn run<'a>(&'a mut self, world: &'a mut World) -> anyhow::Result<()> {
627 self.run_with(world, &mut ())
628 }
629}
630
631impl BoxedSystem {
632 fn new<S>(system: S) -> Self
634 where
635 S: DynSystem + Send + Sync + 'static,
636 {
637 Self {
638 inner: Box::new(system),
639 }
640 }
641
642 pub fn execute<'a>(&'a mut self, ctx: &'a SystemContext<'_, '_, '_>) -> anyhow::Result<()> {
644 self.inner.execute(ctx)
645 }
646
647 pub fn run_with<'a>(
649 &'a mut self,
650 world: &'a mut World,
651 input: impl IntoInput<'a>,
652 ) -> anyhow::Result<()> {
653 let mut cmd = CommandBuffer::new();
654 let input = input.into_input();
655 let ctx = SystemContext::new(world, &mut cmd, &input);
656 self.inner.execute(&ctx)?;
657
658 ctx.cmd_mut()
659 .apply(&mut ctx.world.borrow_mut())
660 .expect("Failed to apply commandbuffer");
661
662 Ok(())
663 }
664
665 pub fn describe(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667 self.inner.describe(f)
668 }
669
670 pub fn access(&self, world: &World, dst: &mut Vec<Access>) {
672 self.inner.access(world, dst)
673 }
674
675 pub fn name(&self) -> &str {
677 self.inner.name()
678 }
679}
680
681impl<T> From<T> for BoxedSystem
682where
683 T: 'static + Send + Sync + DynSystem,
684{
685 fn from(system: T) -> Self {
686 Self::new(system)
687 }
688}
689
690#[cfg(test)]
691#[cfg(feature = "std")]
692mod test {
693
694 use crate::{component, CommandBuffer, Component, EntityBuilder, Query, QueryBorrow, World};
695
696 use super::*;
697
698 #[test]
699 fn system_builder() {
700 component! {
701 a: String,
702 b: i32,
703 };
704
705 let mut world = World::new();
706
707 let id = EntityBuilder::new()
708 .set(a(), "Foo".to_string())
709 .set(b(), 5)
710 .spawn(&mut world);
711
712 let mut system = System::builder()
713 .with(Query::new(a()))
714 .build(|mut a: QueryBorrow<Component<String>>| assert_eq!(a.iter().count(), 1));
716
717 let mut fallible = System::builder()
718 .with(Query::new(b()))
720 .build(move |mut query: QueryBorrow<_>| -> anyhow::Result<()> {
721 let item: &i32 = query.get(id)?;
723 eprintln!("Item: {item}");
724
725 Ok(())
726 });
727
728 let mut cmd = CommandBuffer::new();
729
730 #[allow(clippy::let_unit_value)]
731 let data = &mut ();
732 let ctx = SystemContext::new(&mut world, &mut cmd, data);
733
734 system.execute(&ctx).unwrap();
735
736 fallible.execute(&ctx).unwrap();
737
738 world.remove(id, b()).unwrap();
739
740 let mut boxed = fallible.boxed();
741
742 let ctx = SystemContext::new(&mut world, &mut cmd, data);
743 let res = boxed.execute(&ctx);
744 let _ = res.unwrap_err();
745 }
746
747 #[test]
748 fn system_builder_empty() {
749 let mut a = 5;
750 let mut system = System::builder().build(|| {
751 a += 1;
752 });
753
754 let mut world = World::new();
755 system.run(&mut world);
756
757 assert_eq!(a, 6);
758 }
759}