1mod query_impls;
2
3use std::any::TypeId;
4use std::marker::PhantomData;
5use std::sync::{Mutex, OnceLock};
6
7use crate::entity::entity_set::{EntitySet, EntitySetIterator};
8use crate::entity::multi_property::type_ids_to_multi_property_index;
9use crate::entity::property_list::{PropertyInitializationList, PropertyList};
10use crate::entity::property_store::PropertyStore;
11use crate::entity::Entity;
12use crate::hashing::HashMap;
13use crate::prelude::EntityId;
14use crate::{Context, IxaError};
15
16pub struct EntityPropertyTuple<E: Entity, T> {
32 inner: T,
33 _marker: PhantomData<E>,
34}
35
36impl<E: Entity, T: Copy> Copy for EntityPropertyTuple<E, T> {}
38
39impl<E: Entity, T: Clone> Clone for EntityPropertyTuple<E, T> {
40 fn clone(&self) -> Self {
41 Self {
42 inner: self.inner.clone(),
43 _marker: PhantomData,
44 }
45 }
46}
47
48impl<E: Entity, T: std::fmt::Debug> std::fmt::Debug for EntityPropertyTuple<E, T> {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 f.debug_struct("EntityPropertyTuple")
51 .field("inner", &self.inner)
52 .finish()
53 }
54}
55
56impl<E: Entity, T> EntityPropertyTuple<E, T> {
57 pub fn new(inner: T) -> Self {
59 Self {
60 inner,
61 _marker: PhantomData,
62 }
63 }
64
65 pub fn inner(&self) -> &T {
67 &self.inner
68 }
69
70 pub fn into_inner(self) -> T {
72 self.inner
73 }
74}
75
76impl<E: Entity, T: QueryInternal<E>> QueryInternal<E> for EntityPropertyTuple<E, T> {
77 type QueryParts<'a>
78 = T::QueryParts<'a>
79 where
80 Self: 'a;
81
82 fn get_type_ids(&self) -> Vec<TypeId> {
83 self.inner.get_type_ids()
84 }
85
86 fn multi_property_id(&self) -> Option<usize> {
87 self.inner.multi_property_id()
88 }
89
90 fn is_empty_query(&self) -> bool {
91 self.inner.is_empty_query()
92 }
93
94 fn query_parts(&self) -> Self::QueryParts<'_> {
95 self.inner.query_parts()
96 }
97
98 fn new_query_result<'c>(&self, context: &'c Context) -> EntitySet<'c, E> {
99 self.inner.new_query_result(context)
100 }
101
102 fn match_entity(&self, entity_id: EntityId<E>, context: &Context) -> bool {
103 self.inner.match_entity(entity_id, context)
104 }
105
106 fn filter_entities(&self, entities: &mut Vec<EntityId<E>>, context: &Context) {
107 self.inner.filter_entities(entities, context)
108 }
109}
110
111impl<E: Entity, T: PropertyList<E>> PropertyList<E> for EntityPropertyTuple<E, T> {
112 fn validate() -> Result<(), IxaError> {
113 T::validate()
114 }
115
116 fn contains_properties(property_type_ids: &[TypeId]) -> bool {
117 T::contains_properties(property_type_ids)
118 }
119
120 fn set_values_for_new_entity(
121 &self,
122 entity_id: EntityId<E>,
123 property_store: &mut PropertyStore<E>,
124 ) {
125 let tuple = *self;
126 tuple
127 .into_inner()
128 .set_values_for_new_entity(entity_id, property_store)
129 }
130
131 fn get_values_for_entity(context: &Context, entity_id: EntityId<E>) -> Self {
132 EntityPropertyTuple::new(T::get_values_for_entity(context, entity_id))
133 }
134}
135
136impl<E: Entity, PL: PropertyList<E>> PropertyInitializationList<E> for EntityPropertyTuple<E, PL> {}
137
138pub trait QueryInternal<E: Entity>: 'static {
140 type QueryParts<'a>: AsRef<[&'a dyn std::any::Any]>
142 where
143 Self: 'a;
144
145 fn get_type_ids(&self) -> Vec<TypeId>;
147
148 fn multi_property_id(&self) -> Option<usize> {
150 static REGISTRY: OnceLock<Mutex<HashMap<TypeId, &'static Option<usize>>>> = OnceLock::new();
153
154 let map = REGISTRY.get_or_init(|| Mutex::new(HashMap::default()));
155 let mut map = map.lock().unwrap();
156 let type_id = TypeId::of::<Self>();
157 let entry = *map.entry(type_id).or_insert_with(|| {
158 let mut types = self.get_type_ids();
159 types.sort_unstable();
160 Box::leak(Box::new(type_ids_to_multi_property_index(types.as_slice())))
161 });
162
163 *entry
164 }
165
166 fn is_empty_query(&self) -> bool {
168 false
169 }
170
171 fn query_parts(&self) -> Self::QueryParts<'_>;
173
174 fn new_query_result<'c>(&self, context: &'c Context) -> EntitySet<'c, E>;
176
177 fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
179 self.new_query_result(context).into_iter()
180 }
181
182 fn match_entity(&self, entity_id: EntityId<E>, context: &Context) -> bool;
184
185 fn filter_entities(&self, entities: &mut Vec<EntityId<E>>, context: &Context);
187}
188
189pub trait Query<E: Entity>: QueryInternal<E> {}
197
198impl<E: Entity, QI: QueryInternal<E>> Query<E> for EntityPropertyTuple<E, QI> {}
199impl<E: Entity> Query<E> for E {}
200
201#[cfg(test)]
202mod tests {
203
204 use crate::prelude::*;
205 use crate::{
206 define_derived_property, define_entity, define_multi_property, define_property, Context,
207 };
208
209 define_entity!(Person);
210
211 define_property!(struct Age(u8), Person, default_const = Age(0));
212 define_property!(struct County(u32), Person, default_const = County(0));
213 define_property!(struct Height(u32), Person, default_const = Height(0));
214 define_property!(
215 enum RiskCategory {
216 High,
217 Low,
218 },
219 Person
220 );
221
222 define_multi_property!((Age, County), Person);
223
224 #[test]
225 fn with_query_results() {
226 let mut context = Context::new();
227 let _ = context
228 .add_entity(with!(Person, RiskCategory::High))
229 .unwrap();
230
231 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
232 assert_eq!(people.into_iter().count(), 1);
233 });
234 }
235
236 #[test]
237 fn with_query_results_empty() {
238 let context = Context::new();
239
240 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
241 assert_eq!(people.into_iter().count(), 0);
242 });
243 }
244
245 #[test]
246 fn query_entity_count() {
247 let mut context = Context::new();
248 let _ = context
249 .add_entity(with!(Person, RiskCategory::High))
250 .unwrap();
251
252 assert_eq!(
253 context.query_entity_count(with!(Person, RiskCategory::High)),
254 1
255 );
256 }
257
258 #[test]
259 fn query_entity_count_empty() {
260 let context = Context::new();
261
262 assert_eq!(
263 context.query_entity_count(with!(Person, RiskCategory::High)),
264 0
265 );
266 }
267
268 #[test]
269 fn with_query_results_macro_index_first() {
270 let mut context = Context::new();
271 let _ = context
272 .add_entity(with!(Person, RiskCategory::High))
273 .unwrap();
274 context.index_property::<_, RiskCategory>();
275 assert!(context.is_property_indexed::<Person, RiskCategory>());
276
277 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
278 assert_eq!(people.into_iter().count(), 1);
279 });
280 }
281
282 #[test]
283 fn with_query_results_macro_index_second() {
284 let mut context = Context::new();
285 let _ = context.add_entity(with!(Person, RiskCategory::High));
286
287 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
288 assert_eq!(people.into_iter().count(), 1);
289 });
290 assert!(!context.is_property_indexed::<Person, RiskCategory>());
291
292 context.index_property::<Person, RiskCategory>();
293 assert!(context.is_property_indexed::<Person, RiskCategory>());
294
295 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
296 assert_eq!(people.into_iter().count(), 1);
297 });
298 }
299
300 #[test]
301 fn with_query_results_macro_change() {
302 let mut context = Context::new();
303 let person1 = context
304 .add_entity(with!(Person, RiskCategory::High))
305 .unwrap();
306
307 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
308 assert_eq!(people.into_iter().count(), 1);
309 });
310
311 context.with_query_results(with!(Person, RiskCategory::Low), &mut |people| {
312 assert_eq!(people.into_iter().count(), 0);
313 });
314
315 context.set_property(person1, RiskCategory::Low);
316 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
317 assert_eq!(people.into_iter().count(), 0);
318 });
319
320 context.with_query_results(with!(Person, RiskCategory::Low), &mut |people| {
321 assert_eq!(people.into_iter().count(), 1);
322 });
323 }
324
325 #[test]
326 fn with_query_results_index_after_add() {
327 let mut context = Context::new();
328 let _ = context
329 .add_entity(with!(Person, RiskCategory::High))
330 .unwrap();
331 context.index_property::<Person, RiskCategory>();
332 assert!(context.is_property_indexed::<Person, RiskCategory>());
333 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
334 assert_eq!(people.into_iter().count(), 1);
335 });
336 }
337
338 #[test]
339 fn with_query_results_add_after_index() {
340 let mut context = Context::new();
341 let _ = context
342 .add_entity(with!(Person, RiskCategory::High))
343 .unwrap();
344 context.index_property::<Person, RiskCategory>();
345 assert!(context.is_property_indexed::<Person, RiskCategory>());
346 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
347 assert_eq!(people.into_iter().count(), 1);
348 });
349
350 let _ = context
351 .add_entity(with!(Person, RiskCategory::High))
352 .unwrap();
353 context.with_query_results(with!(Person, RiskCategory::High), &mut |people| {
354 assert_eq!(people.into_iter().count(), 2);
355 });
356 }
357
358 #[test]
359 fn with_query_results_cast_value() {
360 let mut context = Context::new();
361 let _ = context
362 .add_entity(with!(Person, Age(42), RiskCategory::High))
363 .unwrap();
364
365 context.with_query_results(with!(Person, Age(42)), &mut |people| {
366 assert_eq!(people.into_iter().count(), 1);
367 });
368 }
369
370 #[test]
371 fn with_query_results_intersection() {
372 let mut context = Context::new();
373 let _ = context
374 .add_entity(with!(Person, Age(42), RiskCategory::High))
375 .unwrap();
376 let _ = context
377 .add_entity(with!(Person, Age(42), RiskCategory::Low))
378 .unwrap();
379 let _ = context
380 .add_entity(with!(Person, Age(40), RiskCategory::Low))
381 .unwrap();
382
383 context.with_query_results(with!(Person, Age(42), RiskCategory::High), &mut |people| {
384 assert_eq!(people.into_iter().count(), 1);
385 });
386 }
387
388 #[test]
389 fn with_query_results_intersection_non_macro() {
390 let mut context = Context::new();
391 let _ = context
392 .add_entity(with!(Person, Age(42), RiskCategory::High))
393 .unwrap();
394 let _ = context
395 .add_entity(with!(Person, Age(42), RiskCategory::Low))
396 .unwrap();
397 let _ = context
398 .add_entity(with!(Person, Age(40), RiskCategory::Low))
399 .unwrap();
400
401 context.with_query_results(with!(Person, Age(42), RiskCategory::High), &mut |people| {
402 assert_eq!(people.into_iter().count(), 1);
403 });
404 }
405
406 #[test]
407 fn with_query_results_intersection_one_indexed() {
408 let mut context = Context::new();
409 let _ = context
410 .add_entity(with!(Person, Age(42), RiskCategory::High))
411 .unwrap();
412 let _ = context
413 .add_entity(with!(Person, Age(42), RiskCategory::Low))
414 .unwrap();
415 let _ = context
416 .add_entity(with!(Person, Age(40), RiskCategory::Low))
417 .unwrap();
418
419 context.index_property::<Person, Age>();
420 context.with_query_results(with!(Person, Age(42), RiskCategory::High), &mut |people| {
421 assert_eq!(people.into_iter().count(), 1);
422 });
423 }
424
425 #[test]
426 fn query_derived_prop() {
427 let mut context = Context::new();
428 define_derived_property!(struct Senior(bool), Person, [Age], |age| Senior(age.0 >= 65));
429
430 let person = context
431 .add_entity(with!(Person, Age(64), RiskCategory::High))
432 .unwrap();
433 context
434 .add_entity(with!(Person, Age(88), RiskCategory::High))
435 .unwrap();
436
437 let mut not_seniors = Vec::new();
438 context.with_query_results(with!(Person, Senior(false)), &mut |people| {
439 not_seniors = people.to_owned_vec();
440 });
441 let mut seniors = Vec::new();
442 context.with_query_results(with!(Person, Senior(true)), &mut |people| {
443 seniors = people.to_owned_vec();
444 });
445 assert_eq!(seniors.len(), 1, "One senior");
446 assert_eq!(not_seniors.len(), 1, "One non-senior");
447
448 context.set_property(person, Age(65));
449
450 context.with_query_results(with!(Person, Senior(false)), &mut |people| {
451 not_seniors = people.to_owned_vec()
452 });
453 context.with_query_results(with!(Person, Senior(true)), &mut |people| {
454 seniors = people.to_owned_vec()
455 });
456
457 assert_eq!(seniors.len(), 2, "Two seniors");
458 assert_eq!(not_seniors.len(), 0, "No non-seniors");
459 }
460
461 #[test]
462 fn query_derived_prop_with_index() {
463 let mut context = Context::new();
464 define_derived_property!(struct Senior(bool), Person, [Age], |age| Senior(age.0 >= 65));
465
466 context.index_property::<Person, Senior>();
467 let person = context
468 .add_entity(with!(Person, Age(64), RiskCategory::Low))
469 .unwrap();
470 let _ = context.add_entity(with!(Person, Age(88), RiskCategory::Low));
471
472 let mut not_seniors = Vec::new();
473 context.with_query_results(with!(Person, Senior(false)), &mut |people| {
474 not_seniors = people.to_owned_vec()
475 });
476 let mut seniors = Vec::new();
477 context.with_query_results(with!(Person, Senior(true)), &mut |people| {
478 seniors = people.to_owned_vec()
479 });
480 assert_eq!(seniors.len(), 1, "One senior");
481 assert_eq!(not_seniors.len(), 1, "One non-senior");
482
483 context.set_property(person, Age(65));
484
485 context.with_query_results(with!(Person, Senior(false)), &mut |people| {
486 not_seniors = people.to_owned_vec()
487 });
488 context.with_query_results(with!(Person, Senior(true)), &mut |people| {
489 seniors = people.to_owned_vec()
490 });
491
492 assert_eq!(seniors.len(), 2, "Two seniors");
493 assert_eq!(not_seniors.len(), 0, "No non-seniors");
494 }
495
496 define_multi_property!((Age, County, Height), Person);
498 define_multi_property!((County, Height), Person);
499
500 #[test]
501 fn query_derived_prop_with_optimized_index() {
502 let mut context = Context::new();
503 define_derived_property!(
505 struct Ach(u8, u32, u32),
506 Person,
507 [Age, County, Height],
508 [],
509 |age, county, height| Ach(age.0, county.0, height.0)
510 );
511
512 let _ = context.add_entity(with!(
514 Person,
515 Age(64),
516 County(2),
517 Height(120),
518 RiskCategory::Low
519 ));
520 let _ = context.add_entity(with!(
521 Person,
522 Age(88),
523 County(2),
524 Height(130),
525 RiskCategory::Low
526 ));
527 let p2 = context
528 .add_entity(with!(
529 Person,
530 Age(8),
531 County(1),
532 Height(140),
533 RiskCategory::Low
534 ))
535 .unwrap();
536 let p3 = context
537 .add_entity(with!(
538 Person,
539 Age(28),
540 County(1),
541 Height(140),
542 RiskCategory::Low
543 ))
544 .unwrap();
545 let p4 = context
546 .add_entity(with!(
547 Person,
548 Age(28),
549 County(2),
550 Height(160),
551 RiskCategory::Low
552 ))
553 .unwrap();
554 let p5 = context
555 .add_entity(with!(
556 Person,
557 Age(28),
558 County(2),
559 Height(160),
560 RiskCategory::Low
561 ))
562 .unwrap();
563
564 context.with_query_results(with!(Person, Ach(28, 2, 160)), &mut |people| {
566 assert!(people.contains(p4));
567 assert!(people.contains(p5));
568 assert_eq!(people.into_iter().count(), 2, "Should have 2 matches");
569 });
570
571 context.with_query_results(
573 with!(Person, Age(28), County(2), Height(160)),
574 &mut |people| {
575 assert!(people.contains(p4));
576 assert!(people.contains(p5));
577 assert_eq!(people.into_iter().count(), 2, "Should have 2 matches");
578 },
579 );
580
581 context.with_query_results(
583 with!(Person, County(2), Height(160), Age(28)),
584 &mut |people| {
585 assert!(people.contains(p4));
586 assert!(people.contains(p5));
587 assert_eq!(people.into_iter().count(), 2, "Should have 2 matches");
588 },
589 );
590
591 context.with_query_results(
593 with!(Person, Height(160), County(2), Age(28)),
594 &mut |people| {
595 assert!(people.contains(p4));
596 assert!(people.contains(p5));
597 assert_eq!(people.into_iter().count(), 2, "Should have 2 matches");
598 },
599 );
600
601 context.with_query_results(
603 with!(Person, Height(140), County(1), Age(28)),
604 &mut |people| {
605 assert!(people.contains(p3));
606 assert_eq!(people.into_iter().count(), 1, "Should have 1 matches");
607 },
608 );
609
610 context.set_property(p2, Age(28));
611 context.with_query_results(
613 with!(Person, Height(140), County(1), Age(28)),
614 &mut |people| {
615 assert!(people.contains(p2));
616 assert!(people.contains(p3));
617 assert_eq!(people.into_iter().count(), 2, "Should have 2 matches");
618 },
619 );
620
621 context.with_query_results(with!(Person, Height(140), County(1)), &mut |people| {
622 assert!(people.contains(p2));
623 assert!(people.contains(p3));
624 assert_eq!(people.into_iter().count(), 2, "Should have 2 matches");
625 });
626 }
627
628 #[test]
629 fn test_match_entity() {
630 let mut context = Context::new();
631 let person = context
632 .add_entity(with!(
633 Person,
634 Age(28),
635 County(2),
636 Height(160),
637 RiskCategory::Low
638 ))
639 .unwrap();
640 assert!(context.match_entity(person, with!(Person, Age(28), County(2), Height(160))));
641 assert!(!context.match_entity(person, with!(Person, Age(13), County(2), Height(160))));
642 assert!(!context.match_entity(person, with!(Person, Age(28), County(33), Height(160))));
643 assert!(!context.match_entity(person, with!(Person, Age(28), County(2), Height(9))));
644 }
645
646 #[test]
647 fn filter_entities_for_unindexed_query() {
648 let mut context = Context::new();
649 let mut people = Vec::new();
650
651 for idx in 0..10 {
652 let person = context
653 .add_entity(with!(
654 Person,
655 Age(28),
656 County(idx % 2),
657 Height(160),
658 RiskCategory::Low
659 ))
660 .unwrap();
661 people.push(person);
662 }
663
664 context.filter_entities(
665 &mut people,
666 with!(Person, Age(28), County(0), Height(160), RiskCategory::Low),
667 );
668
669 let expected = (0..5)
670 .map(|idx| PersonId::new(idx * 2))
671 .collect::<Vec<PersonId>>();
672 assert_eq!(people, expected);
673 }
674
675 #[test]
676 fn filter_entities_for_indexed_query() {
677 let mut context = Context::new();
678 let mut people = Vec::new();
679
680 context.index_property::<Person, (Age, County)>();
681
682 for idx in 0..10 {
683 let person = context
684 .add_entity(with!(
685 Person,
686 Age(28),
687 County(idx % 2),
688 Height(160),
689 RiskCategory::Low
690 ))
691 .unwrap();
692 people.push(person);
693 }
694
695 context.filter_entities(&mut people, with!(Person, County(0), Age(28)));
696
697 let expected = (0..5)
698 .map(|idx| PersonId::new(idx * 2))
699 .collect::<Vec<PersonId>>();
700 assert_eq!(people, expected);
701 }
702
703 #[test]
704 fn entity_property_tuple_basic() {
705 use super::EntityPropertyTuple;
706
707 let mut context = Context::new();
708 let p1 = context
709 .add_entity(with!(Person, Age(42), RiskCategory::High))
710 .unwrap();
711 let _ = context
712 .add_entity(with!(Person, Age(42), RiskCategory::Low))
713 .unwrap();
714 let _ = context
715 .add_entity(with!(Person, Age(30), RiskCategory::High))
716 .unwrap();
717
718 let query: EntityPropertyTuple<Person, _> =
720 EntityPropertyTuple::new((Age(42), RiskCategory::High));
721
722 context.with_query_results(query, &mut |people| {
723 assert!(people.contains(p1));
724 assert_eq!(people.into_iter().count(), 1);
725 });
726
727 assert!(context.match_entity(p1, query));
729
730 assert_eq!(context.query_entity_count(query), 1);
732 }
733
734 #[test]
735 fn entity_property_tuple_empty_query() {
736 use super::EntityPropertyTuple;
737
738 let mut context = Context::new();
739 let _ = context
740 .add_entity(with!(Person, Age(42), RiskCategory::High))
741 .unwrap();
742 let _ = context
743 .add_entity(with!(Person, Age(30), RiskCategory::Low))
744 .unwrap();
745
746 let query: EntityPropertyTuple<Person, _> = EntityPropertyTuple::new(());
748
749 assert_eq!(context.query_entity_count(query), 2);
750 }
751
752 #[test]
753 fn entity_property_tuple_singleton() {
754 use super::EntityPropertyTuple;
755
756 let mut context = Context::new();
757 let _ = context
758 .add_entity(with!(Person, Age(42), RiskCategory::High))
759 .unwrap();
760 let _ = context
761 .add_entity(with!(Person, Age(42), RiskCategory::Low))
762 .unwrap();
763 let _ = context
764 .add_entity(with!(Person, Age(30), RiskCategory::High))
765 .unwrap();
766
767 let query: EntityPropertyTuple<Person, _> = EntityPropertyTuple::new((Age(42),));
769
770 assert_eq!(context.query_entity_count(query), 2);
771 }
772
773 #[test]
774 fn entity_property_tuple_inner_access() {
775 use super::EntityPropertyTuple;
776
777 let query: EntityPropertyTuple<Person, _> =
778 EntityPropertyTuple::new((Age(42), RiskCategory::High));
779
780 let inner = query.inner();
782 assert_eq!(inner.0, Age(42));
783 assert_eq!(inner.1, RiskCategory::High);
784
785 let (age, risk) = query.into_inner();
787 assert_eq!(age, Age(42));
788 assert_eq!(risk, RiskCategory::High);
789 }
790
791 #[test]
792 fn all_macro_no_properties() {
793 use crate::with;
794
795 let mut context = Context::new();
796 let _ = context
797 .add_entity(with!(Person, Age(42), RiskCategory::High))
798 .unwrap();
799 let _ = context
800 .add_entity(with!(Person, Age(30), RiskCategory::Low))
801 .unwrap();
802
803 let query = with!(Person);
805 assert_eq!(context.query_entity_count(query), 2);
806 }
807
808 #[test]
809 fn all_macro_single_property() {
810 use crate::with;
811
812 let mut context = Context::new();
813 let _ = context
814 .add_entity(with!(Person, Age(42), RiskCategory::High))
815 .unwrap();
816 let _ = context
817 .add_entity(with!(Person, Age(42), RiskCategory::Low))
818 .unwrap();
819 let _ = context
820 .add_entity(with!(Person, Age(30), RiskCategory::High))
821 .unwrap();
822
823 let query = with!(Person, Age(42));
825 assert_eq!(context.query_entity_count(query), 2);
826 }
827
828 #[test]
829 fn all_macro_multiple_properties() {
830 use crate::with;
831
832 let mut context = Context::new();
833 let p1 = context
834 .add_entity(with!(Person, Age(42), RiskCategory::High))
835 .unwrap();
836 let _ = context
837 .add_entity(with!(Person, Age(42), RiskCategory::Low))
838 .unwrap();
839 let _ = context
840 .add_entity(with!(Person, Age(30), RiskCategory::High))
841 .unwrap();
842
843 let query = with!(Person, Age(42), RiskCategory::High);
845 assert_eq!(context.query_entity_count(query), 1);
846
847 context.with_query_results(query, &mut |people| {
848 assert!(people.contains(p1));
849 });
850 }
851
852 #[test]
853 fn all_macro_with_trailing_comma() {
854 use crate::with;
855
856 let mut context = Context::new();
857 let _ = context
858 .add_entity(with!(Person, Age(42), RiskCategory::High))
859 .unwrap();
860
861 let query = with!(Person, Age(42));
863 assert_eq!(context.query_entity_count(query), 1);
864
865 let query = with!(Person, Age(42), RiskCategory::High);
866 assert_eq!(context.query_entity_count(query), 1);
867 }
868
869 #[test]
870 fn entity_property_tuple_as_property_list() {
871 use super::EntityPropertyTuple;
872 use crate::entity::property_list::PropertyList;
873
874 assert!(EntityPropertyTuple::<Person, (Age,)>::validate().is_ok());
876 assert!(EntityPropertyTuple::<Person, (Age, RiskCategory)>::validate().is_ok());
877
878 assert!(EntityPropertyTuple::<Person, (Age,)>::contains_properties(
880 &[Age::type_id()]
881 ));
882 assert!(
883 EntityPropertyTuple::<Person, (Age, RiskCategory)>::contains_properties(&[
884 Age::type_id()
885 ])
886 );
887 assert!(
888 EntityPropertyTuple::<Person, (Age, RiskCategory)>::contains_properties(&[
889 Age::type_id(),
890 RiskCategory::type_id()
891 ])
892 );
893 }
894
895 #[test]
896 fn all_macro_as_property_list_for_add_entity() {
897 use crate::with;
898
899 let mut context = Context::new();
900
901 let props = with!(Person, Age(42), RiskCategory::High);
903 let person = context.add_entity(props).unwrap();
904
905 assert_eq!(context.get_property::<Person, Age>(person), Age(42));
907 assert_eq!(
908 context.get_property::<Person, RiskCategory>(person),
909 RiskCategory::High
910 );
911 }
912}