thallium_ecs/
query_parameters.rs

1use crate::{
2    component_container::ComponentContainer,
3    query::ComponentContainerTrait,
4    system::{Borrow, BorrowType, SystemRunState},
5    Component, Ref, RefMut,
6};
7use parking_lot::{
8    MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLockReadGuard, RwLockWriteGuard,
9};
10use std::any::TypeId;
11
12/// The type parameter for a [`Query`](crate::Query)
13pub trait QueryParameter {
14    /// The lock returned from [`QueryParameter::lock`]
15    type ComponentContainerLock<'a>;
16    /// The component container that is used to get [`Component`]s from
17    type ComponentContainer<'a>: ComponentContainerTrait<'a>;
18
19    /// Locks any needed state, the first step to creating a [`Query`](crate::Query)
20    fn lock<'a>(state: &SystemRunState<'a>) -> Self::ComponentContainerLock<'a>;
21    /// Constructs the component container from the locked state, the final state to creating a [`Query`](crate::Query)
22    fn construct<'a>(
23        lock: &'a mut Self::ComponentContainerLock<'_>,
24    ) -> Self::ComponentContainer<'a>;
25
26    /// Returns an iterator over all the [`Component`] types that will be locked
27    fn get_component_types() -> impl Iterator<Item = Borrow>;
28}
29
30impl<C> QueryParameter for Ref<'_, C>
31where
32    C: Component,
33{
34    type ComponentContainerLock<'a> = Option<MappedRwLockReadGuard<'a, ComponentContainer<C>>>;
35    type ComponentContainer<'a> = Option<&'a ComponentContainer<C>>;
36
37    fn lock<'a>(state: &SystemRunState<'a>) -> Self::ComponentContainerLock<'a> {
38        Some(RwLockReadGuard::map(
39            state
40                .components
41                .get(&TypeId::of::<C>())?
42                .try_read()
43                .expect("the lock should always be available"),
44            |components| components.downcast_ref::<C>(),
45        ))
46    }
47
48    fn construct<'a>(
49        lock: &'a mut Self::ComponentContainerLock<'_>,
50    ) -> Self::ComponentContainer<'a> {
51        Some(lock.as_mut()?)
52    }
53
54    fn get_component_types() -> impl Iterator<Item = Borrow> {
55        std::iter::once(Borrow {
56            id: TypeId::of::<C>(),
57            name: std::any::type_name::<C>(),
58            borrow_type: BorrowType::Immutable,
59        })
60    }
61}
62
63impl<C> QueryParameter for RefMut<'_, C>
64where
65    C: Component,
66{
67    type ComponentContainerLock<'a> = Option<MappedRwLockWriteGuard<'a, ComponentContainer<C>>>;
68    type ComponentContainer<'a> = Option<&'a mut ComponentContainer<C>>;
69
70    fn lock<'a>(state: &SystemRunState<'a>) -> Self::ComponentContainerLock<'a> {
71        Some(RwLockWriteGuard::map(
72            state
73                .components
74                .get(&TypeId::of::<C>())?
75                .try_write()
76                .expect("the lock should always be available"),
77            |components| components.downcast_mut::<C>(),
78        ))
79    }
80
81    fn construct<'a>(
82        lock: &'a mut Self::ComponentContainerLock<'_>,
83    ) -> Self::ComponentContainer<'a> {
84        Some(lock.as_mut()?)
85    }
86
87    fn get_component_types() -> impl Iterator<Item = Borrow> {
88        std::iter::once(Borrow {
89            id: TypeId::of::<C>(),
90            name: std::any::type_name::<C>(),
91            borrow_type: BorrowType::Mutable,
92        })
93    }
94}
95
96pub struct OptionalComponentContainer<T>(pub(crate) T);
97
98impl<P> QueryParameter for Option<P>
99where
100    P: QueryParameter,
101{
102    type ComponentContainerLock<'a> = P::ComponentContainerLock<'a>;
103    type ComponentContainer<'a> = OptionalComponentContainer<P::ComponentContainer<'a>>;
104
105    fn lock<'a>(state: &SystemRunState<'a>) -> Self::ComponentContainerLock<'a> {
106        P::lock(state)
107    }
108
109    fn construct<'a>(
110        lock: &'a mut Self::ComponentContainerLock<'_>,
111    ) -> Self::ComponentContainer<'a> {
112        OptionalComponentContainer(P::construct(lock))
113    }
114
115    fn get_component_types() -> impl Iterator<Item = Borrow> {
116        P::get_component_types()
117    }
118}
119
120macro_rules! query_parameter_tuple {
121    ($($param:ident),*) => {
122        impl<$($param),*> QueryParameter for ($($param,)*)
123        where
124            $($param: QueryParameter,)*
125        {
126            type ComponentContainerLock<'a> = ($($param::ComponentContainerLock<'a>,)*);
127            type ComponentContainer<'a> = ($($param::ComponentContainer<'a>,)*);
128
129            #[allow(clippy::unused_unit)]
130            fn lock<'a>(state: &SystemRunState<'a>) -> Self::ComponentContainerLock<'a> {
131                _ = state;
132                ($($param::lock(state),)*)
133            }
134
135            #[allow(clippy::unused_unit)]
136            fn construct<'this>(state: &'this mut Self::ComponentContainerLock<'_>) -> Self::ComponentContainer<'this> {
137                #[allow(non_snake_case)]
138                let ($($param,)*) = state;
139                ($($param::construct($param),)*)
140            }
141
142            fn get_component_types() -> impl Iterator<Item = Borrow> {
143                std::iter::empty()
144                    $(
145                        .chain($param::get_component_types())
146                    )*
147            }
148        }
149    };
150}
151
152query_parameter_tuple!();
153query_parameter_tuple!(A);
154query_parameter_tuple!(A, B);
155query_parameter_tuple!(A, B, C);
156query_parameter_tuple!(A, B, C, D);
157query_parameter_tuple!(A, B, C, D, E);
158query_parameter_tuple!(A, B, C, D, E, F);
159query_parameter_tuple!(A, B, C, D, E, F, G);
160query_parameter_tuple!(A, B, C, D, E, F, G, H);
161query_parameter_tuple!(A, B, C, D, E, F, G, H, I);
162query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J);
163query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J, K);
164query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
165query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
166query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
167query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
168query_parameter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);