bevy_stat_query/
querier.rs

1use std::fmt::Debug;
2
3use crate::attribute::Attribute;
4use crate::plugin::GlobalStatRelations;
5use crate::stat::StatExt;
6use crate::{
7    plugin::GlobalStatDefaults, Buffer, QualifierFlag, QualifierQuery, Stat, StatInst, StatStream,
8};
9use crate::{validate, StatValue, StatValuePair};
10use bevy_ecs::reflect::ReflectComponent;
11use bevy_ecs::{
12    component::Component,
13    entity::Entity,
14    query::With,
15    system::{Query, Res, SystemParam},
16};
17use bevy_reflect::Reflect;
18use serde::{Deserialize, Serialize};
19
20/// The core marker component. Stat querying is only allowed on entities marked as [`StatEntity`].
21#[derive(Debug, Component, Clone, PartialEq, Eq, Default, Serialize, Deserialize, Reflect)]
22#[reflect(Component)]
23pub struct StatEntity;
24
25/// A root [`SystemParam`] that curates all entities marked as [`StatEntity`].
26///
27/// Join with [`StatStream`]s via [`StatEntities::join`] to start querying.
28#[derive(Debug, SystemParam)]
29pub struct StatEntities<'w, 's, Q: QualifierFlag> {
30    defaults: Option<Res<'w, GlobalStatDefaults>>,
31    relations: Option<Res<'w, GlobalStatRelations<Q>>>,
32    entities: Query<'w, 's, Entity, With<StatEntity>>,
33}
34
35impl<'w, 's, Q: QualifierFlag> StatEntities<'w, 's, Q> {
36    pub fn join<'t, S: StatStream<Qualifier = Q>>(
37        &'t self,
38        stream: S,
39    ) -> JoinedQuerier<'w, 's, 't, Q, S> {
40        JoinedQuerier { base: self, stream }
41    }
42}
43
44pub struct JoinedQuerier<'w, 's, 't, Q: QualifierFlag, S: StatStream<Qualifier = Q>> {
45    base: &'t StatEntities<'w, 's, Q>,
46    stream: S,
47}
48
49impl<'w, 's, 't, Q: QualifierFlag, S: StatStream<Qualifier = Q>> JoinedQuerier<'w, 's, 't, Q, S> {
50    pub fn join<T: StatStream<Qualifier = Q>>(
51        self,
52        stream: T,
53    ) -> JoinedQuerier<'w, 's, 't, Q, (S, T)> {
54        JoinedQuerier {
55            base: self.base,
56            stream: (self.stream, stream),
57        }
58    }
59
60    pub fn query_stat<T: Stat>(
61        &self,
62        entity: Entity,
63        qualifier: &QualifierQuery<Q>,
64        stat: &T,
65    ) -> Option<T::Value> {
66        self.query_stat_erased(entity, qualifier, stat.as_entry())
67            .map(|x| unsafe { x.into() })
68    }
69
70    pub fn query_relation<T: Stat>(
71        &self,
72        from: Entity,
73        to: Entity,
74        qualifier: &QualifierQuery<Q>,
75        stat: &T,
76    ) -> Option<T::Value> {
77        self.query_relation_erased(from, to, qualifier, stat.as_entry())
78            .map(|x| unsafe { x.into() })
79    }
80
81    pub fn eval_stat<T: Stat>(
82        &self,
83        entity: Entity,
84        qualifier: &QualifierQuery<Q>,
85        stat: &T,
86    ) -> Option<<T::Value as StatValue>::Out> {
87        self.query_stat(entity, qualifier, stat).map(|x| x.eval())
88    }
89
90    pub fn eval_relation<T: Stat>(
91        &self,
92        from: Entity,
93        to: Entity,
94        qualifier: &QualifierQuery<Q>,
95        stat: &T,
96    ) -> Option<<T::Value as StatValue>::Out> {
97        self.query_relation(from, to, qualifier, stat)
98            .map(|x| x.eval())
99    }
100
101    pub fn has_attribute<'a>(&self, entity: Entity, attribute: impl Into<Attribute<'a>>) -> bool {
102        self.has_attribute_erased(entity, attribute.into())
103    }
104}
105
106impl<Q: QualifierFlag, S: StatStream<Qualifier = Q>> ErasedQuerier<Q>
107    for JoinedQuerier<'_, '_, '_, Q, S>
108{
109    fn query_stat_erased(
110        &self,
111        entity: Entity,
112        query: &QualifierQuery<Q>,
113        stat: StatInst,
114    ) -> Option<Buffer> {
115        let value = if let Some(defaults) = &self.base.defaults {
116            defaults.get_dyn(stat)
117        } else {
118            (stat.vtable.default)()
119        };
120        let mut pair = StatValuePair { stat, value };
121        if let Some(relations) = &self.base.relations {
122            relations.stream_stat(entity, query, &mut pair, Querier(self));
123        }
124        self.stream
125            .stream_stat(entity, query, &mut pair, Querier(self));
126        Some(pair.value)
127    }
128
129    fn query_relation_erased(
130        &self,
131        from: Entity,
132        to: Entity,
133        query: &QualifierQuery<Q>,
134        stat: StatInst,
135    ) -> Option<Buffer> {
136        let value = if let Some(defaults) = &self.base.defaults {
137            defaults.get_dyn(stat)
138        } else {
139            (stat.vtable.default)()
140        };
141        let mut pair = StatValuePair { stat, value };
142        self.stream
143            .stream_relation(&self.stream, from, to, query, &mut pair, Querier(self));
144        Some(pair.value)
145    }
146
147    fn has_attribute_erased(&self, entity: Entity, attribute: Attribute) -> bool {
148        self.stream.has_attribute(entity, attribute)
149    }
150}
151
152/// An erased type that can query for stats on entities in the world.
153///
154/// Notable implementors are [`NoopQuerier`] and [`JoinedQuerier`].
155trait ErasedQuerier<Q: QualifierFlag> {
156    /// Query for a stat in its component form.
157    fn query_stat_erased(
158        &self,
159        entity: Entity,
160        query: &QualifierQuery<Q>,
161        stat: StatInst,
162    ) -> Option<Buffer>;
163
164    /// Query for a relation stat in its component form.
165    fn query_relation_erased(
166        &self,
167        from: Entity,
168        to: Entity,
169        query: &QualifierQuery<Q>,
170        stat: StatInst,
171    ) -> Option<Buffer>;
172
173    /// Query for the existence of a string attribute.
174    fn has_attribute_erased(&self, entity: Entity, attribute: Attribute) -> bool;
175}
176
177/// An erased type that can query for stats on entities in the world.
178pub struct Querier<'t, Q: QualifierFlag>(&'t dyn ErasedQuerier<Q>);
179
180impl<Q: QualifierFlag> Clone for Querier<'_, Q> {
181    fn clone(&self) -> Self {
182        *self
183    }
184}
185
186impl<Q: QualifierFlag> Copy for Querier<'_, Q> {}
187
188impl<Q: QualifierFlag> Debug for Querier<'_, Q> {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        f.debug_struct("Querier").finish_non_exhaustive()
191    }
192}
193
194impl<Q: QualifierFlag> Querier<'_, Q> {
195    /// Create a noop querier.
196    pub fn noop() -> Querier<'static, Q> {
197        static _Q: NoopQuerier = NoopQuerier;
198        Querier(&_Q)
199    }
200
201    /// Query for a stat in its component form.
202    pub fn query_stat<S: Stat>(
203        &self,
204        entity: Entity,
205        qualifier: &QualifierQuery<Q>,
206        stat: &S,
207    ) -> Option<S::Value> {
208        validate::<S::Value>();
209        self.0
210            .query_stat_erased(entity, qualifier, stat.as_entry())
211            .map(|x| unsafe { x.into() })
212    }
213
214    /// Query for a relation stat in its component form.
215    pub fn query_relation<S: Stat>(
216        &self,
217        from: Entity,
218        to: Entity,
219        qualifier: &QualifierQuery<Q>,
220        stat: &S,
221    ) -> Option<S::Value> {
222        validate::<S::Value>();
223        self.0
224            .query_relation_erased(from, to, qualifier, stat.as_entry())
225            .map(|x| unsafe { x.into() })
226    }
227
228    /// Query for a stat in its evaluated form.
229    pub fn eval_stat<S: Stat>(
230        &self,
231        entity: Entity,
232        qualifier: &QualifierQuery<Q>,
233        stat: &S,
234    ) -> Option<<S::Value as StatValue>::Out> {
235        validate::<S::Value>();
236        self.query_stat(entity, qualifier, stat)
237            .map(|x| StatValue::eval(&x))
238    }
239
240    /// Query for a relation stat in its evaluated form.
241    pub fn eval_relation<S: Stat>(
242        &self,
243        from: Entity,
244        to: Entity,
245        qualifier: &QualifierQuery<Q>,
246        stat: &S,
247    ) -> Option<<S::Value as StatValue>::Out> {
248        validate::<S::Value>();
249        self.query_relation(from, to, qualifier, stat)
250            .map(|x| StatValue::eval(&x))
251    }
252
253    /// Query for the existence of an attribute.
254    pub fn has_attribute<'a>(&self, entity: Entity, attribute: impl Into<Attribute<'a>>) -> bool {
255        self.0.has_attribute_erased(entity, attribute.into())
256    }
257}
258
259/// A [`Querier`] that does not provide the ability to query other entities.
260pub struct NoopQuerier;
261
262impl<Q: QualifierFlag> ErasedQuerier<Q> for NoopQuerier {
263    fn query_relation_erased(
264        &self,
265        _: Entity,
266        _: Entity,
267        _: &QualifierQuery<Q>,
268        _: StatInst,
269    ) -> Option<Buffer> {
270        None
271    }
272
273    fn query_stat_erased(&self, _: Entity, _: &QualifierQuery<Q>, _: StatInst) -> Option<Buffer> {
274        None
275    }
276
277    fn has_attribute_erased(&self, _: Entity, _: Attribute) -> bool {
278        false
279    }
280}