1use std::cell::RefMut;
2use std::ops::{Deref, DerefMut};
3
4use crate::ecs::resources::Resources;
5
6pub struct Res<'a, T: hecs::Component> {
10 pub(crate) data: hecs::Ref<'a, T>,
11}
12
13impl<'a, T: hecs::Component> Deref for Res<'a, T> {
14 type Target = T;
15 fn deref(&self) -> &Self::Target {
16 &self.data
17 }
18}
19
20pub struct ResMut<'a, T: hecs::Component> {
24 data: hecs::RefMut<'a, T>,
25}
26
27impl<'a, T: hecs::Component> Deref for ResMut<'a, T> {
28 type Target = T;
29 fn deref(&self) -> &Self::Target {
30 &self.data
31 }
32}
33
34impl<'a, T: hecs::Component> DerefMut for ResMut<'a, T> {
35 fn deref_mut(&mut self) -> &mut Self::Target {
36 &mut self.data
37 }
38}
39
40pub struct Query<'a, Q: hecs::Query> {
81 world: &'a hecs::World,
82 borrow: hecs::QueryBorrow<'a, Q>,
83 scratch: Option<hecs::QueryOne<'a, Q>>,
88}
89
90impl<'q, Q: hecs::Query> IntoIterator for &'q mut Query<'_, Q> {
91 type Item = Q::Item<'q>;
92 type IntoIter = hecs::QueryIter<'q, Q>;
93
94 fn into_iter(self) -> Self::IntoIter {
95 (&mut self.borrow).into_iter()
96 }
97}
98
99impl<'a, Q: hecs::Query> Query<'a, Q> {
100 pub fn iter(&mut self) -> impl Iterator<Item = Q::Item<'_>> {
104 self.borrow.iter()
105 }
106
107 pub fn get(&mut self, entity: hecs::Entity) -> Option<Q::Item<'_>> {
110 self.scratch = Some(self.world.query_one::<Q>(entity));
111 self.scratch.as_mut().unwrap().get().ok()
112 }
113
114 pub fn with<R: hecs::Query>(self) -> Query<'a, hecs::With<Q, R>> {
118 Query { world: self.world, borrow: self.borrow.with::<R>(), scratch: None }
119 }
120
121 pub fn without<R: hecs::Query>(self) -> Query<'a, hecs::Without<Q, R>> {
124 Query { world: self.world, borrow: self.borrow.without::<R>(), scratch: None }
125 }
126
127 pub fn single(&mut self) -> Q::Item<'_> {
135 self.get_single()
136 .expect("Query::single: expected exactly one matching entity")
137 }
138
139 pub fn get_single(&mut self) -> Option<Q::Item<'_>> {
142 let mut iter = self.borrow.iter();
143 let first = iter.next()?;
144 if iter.next().is_some() {
145 return None;
146 }
147 Some(first)
148 }
149}
150
151pub struct Commands<'a> {
156 buffer: RefMut<'a, hecs::CommandBuffer>,
157 resource_entity: hecs::Entity,
158}
159
160impl<'a> Commands<'a> {
161 pub fn insert_resource<T: hecs::Component>(&mut self, res: T) {
163 self.buffer.insert_one(self.resource_entity, res);
164 }
165
166 pub fn remove_resource<T: hecs::Component>(&mut self) {
168 self.buffer.remove_one::<T>(self.resource_entity);
169 }
170}
171
172impl<'a> Deref for Commands<'a> {
173 type Target = hecs::CommandBuffer;
174 fn deref(&self) -> &Self::Target {
175 &self.buffer
176 }
177}
178
179impl<'a> DerefMut for Commands<'a> {
180 fn deref_mut(&mut self) -> &mut Self::Target {
181 &mut self.buffer
182 }
183}
184
185pub struct Local<'a, T: Default + Send + Sync + 'static> {
195 data: &'a mut T,
196}
197
198impl<'a, T: Default + Send + Sync + 'static> Deref for Local<'a, T> {
199 type Target = T;
200 fn deref(&self) -> &Self::Target {
201 self.data
202 }
203}
204
205impl<'a, T: Default + Send + Sync + 'static> DerefMut for Local<'a, T> {
206 fn deref_mut(&mut self) -> &mut Self::Target {
207 self.data
208 }
209}
210
211pub trait SystemParam {
219 type Item<'a>;
220 type State: Default + 'static;
221 fn fetch<'a>(
222 state: &'a mut Self::State,
223 world: &'a hecs::World,
224 resources: &'a Resources,
225 ) -> Self::Item<'a>;
226}
227
228impl<T> SystemParam for Res<'static, T>
229where
230 T: 'static + Sync + Send,
231{
232 type Item<'a> = Res<'a, T>;
233 type State = ();
234
235 fn fetch<'a>(
236 _state: &'a mut Self::State,
237 world: &'a hecs::World,
238 resource: &'a Resources,
239 ) -> Self::Item<'a> {
240 Res {
241 data: resource.get_resource(world),
242 }
243 }
244}
245
246impl<T> SystemParam for Option<Res<'static, T>>
247where
248 T: 'static + Sync + Send,
249{
250 type Item<'a> = Option<Res<'a, T>>;
251 type State = ();
252
253 fn fetch<'a>(
254 _state: &'a mut Self::State,
255 world: &'a hecs::World,
256 resource: &'a Resources,
257 ) -> Self::Item<'a> {
258 if resource.has_resource::<T>(world) {
259 return Some(Res {
260 data: resource.get_resource(world),
261 });
262 }
263
264 None
265 }
266}
267
268impl<T> SystemParam for ResMut<'static, T>
269where
270 T: 'static + Sync + Send,
271{
272 type Item<'a> = ResMut<'a, T>;
273 type State = ();
274
275 fn fetch<'a>(
276 _state: &'a mut Self::State,
277 world: &'a hecs::World,
278 resource: &'a Resources,
279 ) -> Self::Item<'a> {
280 ResMut {
281 data: resource.get_resource_mut(world),
282 }
283 }
284}
285
286impl<T> SystemParam for Option<ResMut<'static, T>>
287where
288 T: 'static + Sync + Send,
289{
290 type Item<'a> = Option<ResMut<'a, T>>;
291 type State = ();
292
293 fn fetch<'a>(
294 _state: &'a mut Self::State,
295 world: &'a hecs::World,
296 resource: &'a Resources,
297 ) -> Self::Item<'a> {
298 if resource.has_resource::<T>(world) {
299 return Some(ResMut {
300 data: resource.get_resource_mut(world),
301 });
302 }
303
304 None
305 }
306}
307
308impl<Q> SystemParam for Query<'static, Q>
309where
310 Q: hecs::Query + 'static,
311{
312 type Item<'a> = Query<'a, Q>;
313 type State = ();
314
315 fn fetch<'a>(
316 _state: &'a mut Self::State,
317 world: &'a hecs::World,
318 _resources: &'a Resources,
319 ) -> Self::Item<'a> {
320 Query {
321 world,
322 borrow: world.query::<Q>(),
323 scratch: None,
324 }
325 }
326}
327
328impl SystemParam for Commands<'static> {
329 type Item<'a> = Commands<'a>;
330 type State = ();
331
332 fn fetch<'a>(
333 _state: &'a mut Self::State,
334 _world: &'a hecs::World,
335 resources: &'a Resources,
336 ) -> Self::Item<'a> {
337 Commands {
338 buffer: resources.get_command_buffer(),
339 resource_entity: resources.resource_entity,
340 }
341 }
342}
343
344impl SystemParam for &'static hecs::World {
345 type Item<'a> = &'a hecs::World;
346 type State = ();
347
348 fn fetch<'a>(
349 _state: &'a mut Self::State,
350 world: &'a hecs::World,
351 _resources: &'a Resources,
352 ) -> Self::Item<'a> {
353 world
354 }
355}
356
357impl SystemParam for &'static Resources {
358 type Item<'a> = &'a Resources;
359 type State = ();
360
361 fn fetch<'a>(
362 _state: &'a mut Self::State,
363 _world: &'a hecs::World,
364 resources: &'a Resources,
365 ) -> Self::Item<'a> {
366 resources
367 }
368}
369
370impl<T> SystemParam for Local<'static, T>
371where
372 T: Default + Send + Sync + 'static,
373{
374 type Item<'a> = Local<'a, T>;
375 type State = T;
376
377 fn fetch<'a>(
378 state: &'a mut Self::State,
379 _world: &'a hecs::World,
380 _resources: &'a Resources,
381 ) -> Self::Item<'a> {
382 Local { data: state }
383 }
384}
385
386pub trait System: 'static {
388 fn run(&mut self, world: &hecs::World, resources: &Resources);
389
390 fn name(&self) -> &'static str {
394 std::any::type_name::<Self>()
395 }
396
397 fn ordering_id(&self) -> std::any::TypeId {
406 std::any::TypeId::of::<Self>()
407 }
408
409 fn after_ids(&self) -> &[std::any::TypeId] {
413 &[]
414 }
415
416 fn before_ids(&self) -> &[std::any::TypeId] {
420 &[]
421 }
422}
423
424pub struct Labeled<S: System> {
433 inner: S,
434 after: Vec<std::any::TypeId>,
435 before: Vec<std::any::TypeId>,
436}
437
438impl<S: System> Labeled<S> {
439 pub fn after<F: 'static, Marker>(mut self, system: F) -> Self
442 where
443 F: IntoSystem<Marker>,
444 {
445 let _ = system;
446 self.after.push(std::any::TypeId::of::<F>());
447 self
448 }
449
450 pub fn before<F: 'static, Marker>(mut self, system: F) -> Self
453 where
454 F: IntoSystem<Marker>,
455 {
456 let _ = system;
457 self.before.push(std::any::TypeId::of::<F>());
458 self
459 }
460}
461
462impl<S: System> System for Labeled<S> {
463 fn run(&mut self, world: &hecs::World, resources: &Resources) {
464 self.inner.run(world, resources)
465 }
466
467 fn name(&self) -> &'static str {
468 self.inner.name()
469 }
470
471 fn ordering_id(&self) -> std::any::TypeId {
472 self.inner.ordering_id()
473 }
474
475 fn after_ids(&self) -> &[std::any::TypeId] {
476 &self.after
477 }
478
479 fn before_ids(&self) -> &[std::any::TypeId] {
480 &self.before
481 }
482}
483
484impl<S: System> IntoSystem<()> for Labeled<S> {
485 type System = Self;
486
487 fn into_system(self) -> Self::System {
488 self
489 }
490}
491
492pub trait SystemOrderingExt<Marker>: IntoSystem<Marker> + Sized {
502 fn after<F: 'static, Marker2>(self, system: F) -> Labeled<Self::System>
504 where
505 F: IntoSystem<Marker2>,
506 {
507 let _ = system;
508 Labeled {
509 inner: self.into_system(),
510 after: vec![std::any::TypeId::of::<F>()],
511 before: Vec::new(),
512 }
513 }
514
515 fn before<F: 'static, Marker2>(self, system: F) -> Labeled<Self::System>
517 where
518 F: IntoSystem<Marker2>,
519 {
520 let _ = system;
521 Labeled {
522 inner: self.into_system(),
523 after: Vec::new(),
524 before: vec![std::any::TypeId::of::<F>()],
525 }
526 }
527}
528
529impl<T, Marker> SystemOrderingExt<Marker> for T where T: IntoSystem<Marker> {}
530
531pub struct FunctionSystem<F, Marker, State = ()> {
536 pub func: F,
537 state: State,
538 _marker: std::marker::PhantomData<Marker>,
539}
540
541pub trait IntoSystem<Marker> {
546 type System: System;
547
548 fn into_system(self) -> Self::System;
549}
550
551macro_rules! impl_system {
552 ($($param:ident),*) => {
553 impl<T, $($param),*> IntoSystem<($($param,)*)> for T
554 where
555 T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
556 for<'a> &'a mut T: FnMut($($param),*),
557 $($param: SystemParam + 'static),*
558 {
559 type System = FunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
560
561 fn into_system(self) -> Self::System {
562 FunctionSystem {
563 func: self,
564 state: Default::default(),
565 _marker: std::marker::PhantomData,
566 }
567 }
568 }
569
570 impl<T, $($param),*> System for FunctionSystem<T, ($($param,)*), ($($param::State,)*)>
571 where
572 T: for<'a> FnMut($($param::Item<'a>),*) + 'static,
573 $($param: SystemParam + 'static),*
574 {
575 fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
576 #[allow(non_snake_case)]
577 let ($($param,)*) = &mut self.state;
578 (self.func)($($param::fetch($param, _world, _resources)),*);
579 }
580
581 fn name(&self) -> &'static str {
582 std::any::type_name::<T>()
583 }
584
585 fn ordering_id(&self) -> std::any::TypeId {
586 std::any::TypeId::of::<T>()
587 }
588 }
589 };
590}
591
592impl_system!();
593impl_system!(A);
594impl_system!(A, B);
595impl_system!(A, B, C);
596impl_system!(A, B, C, D);
597impl_system!(A, B, C, D, E);
598impl_system!(A, B, C, D, E, F);
599impl_system!(A, B, C, D, E, F, G);
600impl_system!(A, B, C, D, E, F, G, H);
601impl_system!(A, B, C, D, E, F, G, H, I);
602impl_system!(A, B, C, D, E, F, G, H, I, J);
603impl_system!(A, B, C, D, E, F, G, H, I, J, K);
604impl_system!(A, B, C, D, E, F, G, H, I, J, K, L);
605
606pub struct OnceMark;
613
614pub struct OnceFunctionSystem<F, Marker, State = ()> {
621 func: F,
622 state: State,
623 done: bool,
624 _marker: std::marker::PhantomData<Marker>,
625}
626
627macro_rules! impl_auto_once_system {
628 ($($param:ident),*) => {
629 impl<T, $($param),*> IntoSystem<(OnceMark, $($param,)*)> for T
630 where
631 T: for<'a> FnMut($($param::Item<'a>),*) -> Option<()> + 'static,
632 for<'a> &'a mut T: FnMut($($param),*) -> Option<()>,
633 $($param: SystemParam + 'static),*
634 {
635 type System = OnceFunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
636
637 fn into_system(self) -> Self::System {
638 OnceFunctionSystem {
639 func: self,
640 state: Default::default(),
641 done: false,
642 _marker: std::marker::PhantomData,
643 }
644 }
645 }
646
647 impl<T, $($param),*> IntoSystem<(OnceMark, $($param,)*)> for OnceFunctionSystem<T, ($($param,)*), ($($param::State,)*)>
648 where
649 T: for<'a> FnMut($($param::Item<'a>),*) -> Option<()> + 'static,
650 $($param: SystemParam + 'static),*
651 {
652 type System = Self;
653
654 fn into_system(self) -> Self::System {
655 self
656 }
657 }
658
659 impl<T, $($param),*> System for OnceFunctionSystem<T, ($($param,)*), ($($param::State,)*)>
660 where
661 T: for<'a> FnMut($($param::Item<'a>),*) -> Option<()> + 'static,
662 $($param: SystemParam + 'static),*
663 {
664 fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
665 if self.done {
666 return;
667 }
668 #[allow(non_snake_case)]
669 let ($($param,)*) = &mut self.state;
670 let result = (self.func)($($param::fetch($param, _world, _resources)),*);
671 if result.is_some() {
672 self.done = true;
673 }
674 }
675
676 fn name(&self) -> &'static str {
677 std::any::type_name::<T>()
678 }
679
680 fn ordering_id(&self) -> std::any::TypeId {
681 std::any::TypeId::of::<T>()
682 }
683 }
684 };
685}
686
687impl_auto_once_system!();
688impl_auto_once_system!(A);
689impl_auto_once_system!(A, B);
690impl_auto_once_system!(A, B, C);
691impl_auto_once_system!(A, B, C, D);
692impl_auto_once_system!(A, B, C, D, E);
693impl_auto_once_system!(A, B, C, D, E, F);
694impl_auto_once_system!(A, B, C, D, E, F, G);
695impl_auto_once_system!(A, B, C, D, E, F, G, H);
696impl_auto_once_system!(A, B, C, D, E, F, G, H, I);
697impl_auto_once_system!(A, B, C, D, E, F, G, H, I, J);
698impl_auto_once_system!(A, B, C, D, E, F, G, H, I, J, K);
699impl_auto_once_system!(A, B, C, D, E, F, G, H, I, J, K, L);
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704
705 struct Health(i32);
706 struct Enemy;
707 struct Dead;
708
709 fn make_query<Q: hecs::Query>(world: &hecs::World) -> Query<'_, Q> {
710 Query { world, borrow: world.query::<Q>(), scratch: None }
711 }
712
713 #[test]
714 fn iter_yields_every_matching_entity() {
715 let mut world = hecs::World::new();
716 world.spawn((Health(10),));
717 world.spawn((Health(20),));
718
719 let mut query = make_query::<&Health>(&world);
720 let mut totals: Vec<i32> = query.iter().map(|h| h.0).collect();
721 totals.sort();
722 assert_eq!(totals, vec![10, 20]);
723 }
724
725 #[test]
726 fn iter_composes_with_standard_iterator_adapters() {
727 let mut world = hecs::World::new();
728 world.spawn((Health(5),));
729 world.spawn((Health(50),));
730
731 let mut query = make_query::<&Health>(&world);
732 let low_health_count = query.iter().filter(|h| h.0 < 10).count();
733 assert_eq!(low_health_count, 1);
734 }
735
736 #[test]
737 fn get_returns_some_for_a_matching_entity_and_none_otherwise() {
738 let mut world = hecs::World::new();
739 let matching = world.spawn((Health(7),));
740 let non_matching = world.spawn(()); let mut query = make_query::<&Health>(&world);
743 assert_eq!(query.get(matching).map(|h| h.0), Some(7));
744 assert!(query.get(non_matching).is_none());
745 }
746
747 #[test]
748 fn get_can_be_called_more_than_once_on_the_same_query() {
749 let mut world = hecs::World::new();
750 let a = world.spawn((Health(1),));
751 let b = world.spawn((Health(2),));
752
753 let mut query = make_query::<&Health>(&world);
754 assert_eq!(query.get(a).map(|h| h.0), Some(1));
755 assert_eq!(query.get(b).map(|h| h.0), Some(2));
756 }
757
758 #[test]
759 fn with_and_without_chain_and_narrow_by_component_presence() {
760 let mut world = hecs::World::new();
761 let alive_enemy = world.spawn((Health(1), Enemy));
762 world.spawn((Health(1), Enemy, Dead));
763 world.spawn((Health(1),));
764
765 let query = make_query::<&Health>(&world);
766 let mut narrowed = query.with::<&Enemy>().without::<&Dead>();
767
768 assert_eq!(narrowed.iter().count(), 1);
769 assert!(narrowed.get(alive_enemy).is_some());
770 }
771
772 #[test]
773 fn single_panics_on_zero_or_multiple_matches_get_single_does_not() {
774 let mut world = hecs::World::new();
775
776 assert!(make_query::<&Health>(&world).get_single().is_none());
777
778 world.spawn((Health(1),));
779 assert_eq!(make_query::<&Health>(&world).single().0, 1);
780
781 world.spawn((Health(2),));
782 assert!(make_query::<&Health>(&world).get_single().is_none());
783 }
784}