Experiments in applying Entity-Component-System patterns to durable data storage APIs.
1use tracing::trace;
2
3use crate::{Entity, EntityId, component::Bundle};
4
5use super::Component;
6use std::marker::PhantomData;
7
8pub(crate) mod ir;
9
10pub trait QueryData {
11 type Output<'a>: Sized;
12 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>>;
13 fn filter_expression() -> ir::FilterExpression;
14}
15
16pub trait QueryFilter {
17 fn filter_expression() -> ir::FilterExpression;
18}
19
20/// Matches if any of the Filters in `C` matches
21pub struct AnyOf<C>(PhantomData<C>);
22
23/// Matches if Entity has all components in `C`
24pub struct With<C>(PhantomData<C>);
25
26/// Matches if Entity has none of the components in `C`
27pub struct Without<C>(PhantomData<C>);
28
29/// Matches if any of the filters in `F` match
30pub struct Or<F>(F);
31
32pub trait QueryFilterValue: Sized {
33 fn filter_expression(&self) -> ir::FilterExpression;
34}
35
36pub struct Query<'a, D = Entity<'a>, F = (), V = ()>
37where
38 F: ?Sized,
39{
40 pub(crate) ecs: &'a crate::Ecs,
41 pub(crate) data: PhantomData<D>,
42 pub(crate) filter: PhantomData<F>,
43 pub(crate) filter_value: V,
44}
45
46impl<'a, C, F> Query<'a, C, F, ()> {
47 pub fn new(ecs: &'a crate::Ecs) -> Self {
48 Self {
49 ecs,
50 data: PhantomData,
51 filter: PhantomData,
52 filter_value: (),
53 }
54 }
55}
56
57impl<'a, C, F, V> Query<'a, C, F, V> {
58 pub fn with_filter(ecs: &'a crate::Ecs, filter_value: V) -> Self {
59 Self {
60 ecs,
61 data: PhantomData,
62 filter: PhantomData,
63 filter_value,
64 }
65 }
66}
67
68impl<'a, D, F, V> Query<'a, D, F, V>
69where
70 D: QueryData + 'a,
71 F: QueryFilter,
72 V: QueryFilterValue,
73{
74 pub fn iter(&self) -> impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V> {
75 self.try_iter().unwrap()
76 }
77
78 pub fn reverse_iter(&self) -> impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V> {
79 self.try_reverse_iter().unwrap()
80 }
81
82 pub fn entities(&self) -> impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V> {
83 self.try_entities().unwrap()
84 }
85
86 pub fn reverse_entities(&self) -> impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V> {
87 self.try_reverse_entities().unwrap()
88 }
89
90 pub fn try_iter(
91 &self,
92 ) -> Result<impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V>, crate::Error> {
93 Ok(self.try_entities()?.filter_map(|e| D::from_entity(e)))
94 }
95
96 pub fn try_reverse_iter(
97 &self,
98 ) -> Result<impl Iterator<Item = D::Output<'a>> + 'a + use<'a, D, F, V>, crate::Error> {
99 Ok(self
100 .try_reverse_entities()?
101 .filter_map(|e| D::from_entity(e)))
102 }
103
104 pub fn try_entities(
105 &self,
106 ) -> Result<impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V>, crate::Error> {
107 let mut query = self.as_sql_query();
108
109 query.order_by = ir::OrderBy::Asc;
110 self.ecs.fetch::<Entity>(query)
111 }
112
113 pub fn try_reverse_entities(
114 &self,
115 ) -> Result<impl Iterator<Item = Entity<'a>> + 'a + use<'a, D, F, V>, crate::Error> {
116 let mut query = self.as_sql_query();
117 query.order_by = ir::OrderBy::Desc;
118 self.ecs.fetch::<Entity>(query)
119 }
120
121 #[tracing::instrument(level = "debug", skip_all)]
122 fn as_sql_query(&self) -> ir::Query {
123 let filter = ir::FilterExpression::and([
124 D::filter_expression(),
125 F::filter_expression(),
126 self.filter_value.filter_expression(),
127 ]);
128
129 trace!(?filter);
130
131 ir::Query {
132 filter,
133 order_by: ir::OrderBy::Asc,
134 }
135 }
136}
137
138impl QueryData for () {
139 type Output<'a> = ();
140
141 fn from_entity<'a>(_e: Entity<'a>) -> Option<Self::Output<'a>> {
142 Some(())
143 }
144
145 fn filter_expression() -> ir::FilterExpression {
146 ir::FilterExpression::none()
147 }
148}
149
150impl QueryData for Entity<'_> {
151 type Output<'a> = Entity<'a>;
152
153 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> {
154 Some(e)
155 }
156
157 fn filter_expression() -> ir::FilterExpression {
158 ir::FilterExpression::none()
159 }
160}
161
162impl QueryData for EntityId {
163 type Output<'a> = EntityId;
164
165 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> {
166 Some(e.id())
167 }
168
169 fn filter_expression() -> ir::FilterExpression {
170 ir::FilterExpression::none()
171 }
172}
173
174impl<C: Component> QueryData for C {
175 type Output<'a> = C;
176
177 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> {
178 e.component::<C>()
179 }
180
181 fn filter_expression() -> ir::FilterExpression {
182 ir::FilterExpression::with_component(C::component_name())
183 }
184}
185
186impl QueryFilter for () {
187 fn filter_expression() -> ir::FilterExpression {
188 ir::FilterExpression::none()
189 }
190}
191
192impl<C: Bundle> Default for AnyOf<C> {
193 fn default() -> Self {
194 Self(PhantomData)
195 }
196}
197
198impl<C: Bundle> Default for With<C> {
199 fn default() -> Self {
200 Self(PhantomData)
201 }
202}
203
204impl<C: Bundle> Default for Without<C> {
205 fn default() -> Self {
206 Self(PhantomData)
207 }
208}
209
210impl<F: QueryFilter + Default> Default for Or<F> {
211 fn default() -> Self {
212 Self(F::default())
213 }
214}
215
216impl<C: Component> QueryFilter for C {
217 fn filter_expression() -> ir::FilterExpression {
218 ir::FilterExpression::with_component(C::component_name())
219 }
220}
221
222impl<C: Bundle> QueryFilter for AnyOf<C> {
223 fn filter_expression() -> ir::FilterExpression {
224 ir::FilterExpression::or(
225 C::component_names()
226 .iter()
227 .map(|c| ir::FilterExpression::with_component(c)),
228 )
229 }
230}
231
232impl<C: Component> QueryFilter for With<C> {
233 fn filter_expression() -> ir::FilterExpression {
234 ir::FilterExpression::with_component(C::component_name())
235 }
236}
237
238impl<C: Component> QueryFilter for Without<C> {
239 fn filter_expression() -> ir::FilterExpression {
240 ir::FilterExpression::without_component(C::component_name())
241 }
242}
243
244impl QueryFilterValue for () {
245 fn filter_expression(&self) -> ir::FilterExpression {
246 ir::FilterExpression::None
247 }
248}
249
250impl QueryFilterValue for EntityId {
251 fn filter_expression(&self) -> ir::FilterExpression {
252 ir::FilterExpression::entity(*self)
253 }
254}
255
256#[derive(PartialEq, Eq, Debug)]
257pub struct ComponentName(pub String);
258
259impl QueryFilterValue for ComponentName {
260 fn filter_expression(&self) -> ir::FilterExpression {
261 ir::FilterExpression::WithComponent(self.0.clone())
262 }
263}
264
265impl<V: QueryFilterValue> QueryFilterValue for &[V] {
266 fn filter_expression(&self) -> ir::FilterExpression {
267 ir::FilterExpression::And(self.iter().map(V::filter_expression).collect())
268 }
269}
270
271impl<C: Component> QueryFilterValue for C {
272 fn filter_expression(&self) -> ir::FilterExpression {
273 use rusqlite::types::ToSqlOutput;
274
275 let value = match C::to_rusqlite(self).unwrap() {
276 ToSqlOutput::Borrowed(v) => v.to_owned().into(),
277 ToSqlOutput::Owned(v) => v,
278 other => unreachable!("{other:?}"),
279 };
280
281 ir::FilterExpression::with_component_data(C::component_name(), value)
282 }
283}
284
285impl<C: QueryFilterValue + Component> QueryFilterValue for std::ops::Range<C> {
286 fn filter_expression(&self) -> ir::FilterExpression {
287 use rusqlite::types::ToSqlOutput;
288
289 let start = match C::to_rusqlite(&self.start).unwrap() {
290 ToSqlOutput::Borrowed(v) => v.to_owned().into(),
291 ToSqlOutput::Owned(v) => v,
292 other => unreachable!("{other:?}"),
293 };
294
295 let end = match C::to_rusqlite(&self.end).unwrap() {
296 ToSqlOutput::Borrowed(v) => v.to_owned().into(),
297 ToSqlOutput::Owned(v) => v,
298 other => unreachable!("{other:?}"),
299 };
300
301 ir::FilterExpression::WithComponentDataRange {
302 component: C::component_name().to_owned(),
303 start,
304 end,
305 }
306 }
307}
308
309impl<C: QueryFilterValue + Component> QueryFilterValue for std::ops::RangeTo<C> {
310 fn filter_expression(&self) -> ir::FilterExpression {
311 use rusqlite::types::ToSqlOutput;
312
313 let end = match C::to_rusqlite(&self.end).unwrap() {
314 ToSqlOutput::Borrowed(v) => v.to_owned().into(),
315 ToSqlOutput::Owned(v) => v,
316 other => unreachable!("{other:?}"),
317 };
318
319 ir::FilterExpression::WithComponentDataRange {
320 component: C::component_name().to_owned(),
321 start: rusqlite::types::Value::Null,
322 end,
323 }
324 }
325}
326
327impl<C: QueryFilterValue + Component> QueryFilterValue for std::ops::RangeFrom<C> {
328 fn filter_expression(&self) -> ir::FilterExpression {
329 use rusqlite::types::ToSqlOutput;
330
331 let start = match C::to_rusqlite(&self.start).unwrap() {
332 ToSqlOutput::Borrowed(v) => v.to_owned().into(),
333 ToSqlOutput::Owned(v) => v,
334 other => unreachable!("{other:?}"),
335 };
336
337 ir::FilterExpression::WithComponentDataRange {
338 component: C::component_name().to_owned(),
339 start,
340 end: rusqlite::types::Value::Null,
341 }
342 }
343}
344
345mod tuples {
346 use super::*;
347
348 macro_rules! query_data_impl {
349 ( $($ts:ident)* ) => {
350 impl<$($ts,)+> QueryData for ($($ts,)+)
351 where
352 $($ts: QueryData,)+
353 {
354 type Output<'a> = ($($ts::Output<'a>,)+);
355
356 fn from_entity<'a>(e: Entity<'a>) -> Option<Self::Output<'a>> {
357 Some(($($ts::from_entity(e)?,)+))
358 }
359
360
361 fn filter_expression() -> ir::FilterExpression{
362 ir::FilterExpression::and([
363 $(<$ts as QueryData>::filter_expression()),+
364 ])
365 }
366 }
367 }
368 }
369
370 macro_rules! filter_value_impl {
371 ( $($ts:ident)* ) => {
372
373 impl<$($ts,)+> QueryFilterValue for ($($ts,)+)
374 where
375 $($ts: QueryFilterValue,)+
376 {
377
378 fn filter_expression(&self) -> ir::FilterExpression{
379 #[allow(non_snake_case)]
380 let ($($ts,)+) = self;
381 ir::FilterExpression::and([
382 $($ts.filter_expression(),)+
383 ])
384 }
385 }
386 }
387 }
388
389 macro_rules! impl_query_filter {
390 ( $($ts:ident)* ) => {
391 impl<$($ts,)+> QueryFilter for ($($ts,)+)
392 where
393 $($ts: QueryFilter,)+
394 {
395
396 #[allow(non_snake_case)]
397 fn filter_expression() -> ir::FilterExpression{
398 ir::FilterExpression::and([
399 $(<$ts as QueryFilter>::filter_expression()),+
400 ])
401 }
402 }
403
404 impl<$($ts,)+> QueryFilter for Or<($($ts,)+)>
405 where
406 $($ts: QueryFilter,)+
407 {
408
409 #[allow(non_snake_case)]
410 fn filter_expression() -> ir::FilterExpression{
411 ir::FilterExpression::or([
412 $(<$ts as QueryFilter>::filter_expression()),+
413 ])
414 }
415 }
416
417 impl<$($ts,)+> QueryFilter for With<($($ts,)+)>
418 where
419 $($ts: Component,)+
420 {
421
422 fn filter_expression() -> ir::FilterExpression{
423 ir::FilterExpression::and([
424 $(ir::FilterExpression::with_component($ts::component_name()),)+
425 ])
426 }
427 }
428
429 impl<$($ts,)+> QueryFilter for Without<($($ts,)+)>
430 where
431 $($ts: Component,)+
432 {
433
434 fn filter_expression() -> ir::FilterExpression{
435 ir::FilterExpression::and([
436 $(ir::FilterExpression::without_component($ts::component_name()),)+
437 ])
438 }
439 }
440 };
441 }
442
443 crate::tuple_macros::for_each_tuple!(query_data_impl);
444 crate::tuple_macros::for_each_tuple!(filter_value_impl);
445 crate::tuple_macros::for_each_tuple!(impl_query_filter);
446}
447
448#[cfg(test)]
449mod tests {
450 use serde::{Deserialize, Serialize};
451
452 use super::*;
453 use crate as ecsdb;
454
455 #[derive(Debug, Serialize, Deserialize, Component)]
456 struct A;
457
458 #[derive(Debug, Serialize, Deserialize, Component)]
459 struct B;
460
461 #[test]
462 #[allow(unused)]
463 fn system_fns() {
464 fn sys_a(query: Query<A>) {}
465 fn sys_b(query: Query<(A, Without<B>)>) {}
466 fn sys_c(query: Query<Or<(A, B)>>) {}
467 }
468}