ixa/macros/
with.rs

1/// Creates a query matching all entities of a given type, optionally filtered by properties.
2///
3/// # Examples
4///
5/// ```ignore
6/// // Add an entity with default properties
7/// let query = with!(Person);
8/// context.add_entity(query)?;
9///
10/// // An inline query matching a single property
11/// let person = context.sample_entity(MyRng, with!(Person, Age(12)))?;
12///
13/// // A query matching multiple properties
14/// let query = with!(Person, Age(12), RiskCategory::High);
15/// let count = context.count_entities(query);
16/// ```
17#[macro_export]
18macro_rules! with {
19    // No properties - generates empty tuple query
20    ($entity:ty) => {
21        $crate::EntityPropertyTuple::<$entity, _>::new(())
22    };
23    // One or more properties
24    ($entity:ty, $($prop:expr),+ $(,)?) => {
25        $crate::EntityPropertyTuple::<$entity, _>::new(($($prop,)+))
26    };
27}
28
29#[cfg(test)]
30mod tests {
31    use crate::context::Context;
32    use crate::entity::ContextEntitiesExt;
33    use crate::random::ContextRandomExt;
34    use crate::{define_entity, define_property, define_rng, impl_property};
35
36    define_entity!(TestPerson);
37    define_property!(struct Age(u8), TestPerson, default_const = Age(0));
38    define_property!(
39        enum Risk {
40            High,
41            Low,
42        },
43        TestPerson
44    );
45    define_rng!(AllMacroTestRng);
46
47    #[test]
48    fn all_macro_with_add_entity() {
49        let mut context = Context::new();
50
51        // Use with! macro to add an entity
52        let person = context
53            .add_entity(with!(TestPerson, Age(42), Risk::High))
54            .unwrap();
55
56        // Verify properties were set correctly
57        assert_eq!(context.get_property::<TestPerson, Age>(person), Age(42));
58        assert_eq!(context.get_property::<TestPerson, Risk>(person), Risk::High);
59    }
60
61    #[test]
62    fn all_macro_with_sample_entity() {
63        let mut context = Context::new();
64        context.init_random(42);
65
66        // Add some entities
67        let p1 = context
68            .add_entity(with!(TestPerson, Age(30), Risk::High))
69            .unwrap();
70        let _ = context
71            .add_entity(with!(TestPerson, Age(30), Risk::Low))
72            .unwrap();
73        let _ = context
74            .add_entity(with!(TestPerson, Age(25), Risk::High))
75            .unwrap();
76
77        // Sample from entities matching the query
78        let sampled =
79            context.sample_entity(AllMacroTestRng, with!(TestPerson, Age(30), Risk::High));
80        assert_eq!(sampled, Some(p1));
81    }
82
83    #[test]
84    fn all_macro_with_sample_entity_no_match() {
85        let mut context = Context::new();
86        context.init_random(42);
87
88        // Add some entities that don't match the query
89        let _ = context
90            .add_entity(with!(TestPerson, Age(30), Risk::Low))
91            .unwrap();
92
93        // Sample should return None when no entities match
94        let sampled =
95            context.sample_entity(AllMacroTestRng, with!(TestPerson, Age(30), Risk::High));
96        assert_eq!(sampled, None);
97    }
98
99    // Demonstrate that `with!` can disambiguate entities in otherwise ambiguous cases.
100    use crate::entity::EntityId;
101    define_entity!(TestMammal);
102    define_entity!(TestAvian);
103    define_property!(struct IsBipedal(bool), TestMammal, default_const = IsBipedal(false));
104    impl_property!(IsBipedal, TestAvian, default_const = IsBipedal(true));
105
106    #[test]
107    fn all_macro_disambiguates_entities() {
108        let mut context = Context::new();
109
110        context
111            .add_entity(with!(TestAvian, IsBipedal(true)))
112            .unwrap();
113        context
114            .add_entity(with!(TestMammal, IsBipedal(true)))
115            .unwrap();
116
117        let result = context.query_result_iterator(with!(TestMammal, IsBipedal(true)));
118        assert_eq!(result.count(), 1);
119
120        let sampled_id = context
121            .sample_entity(AllMacroTestRng, with!(TestAvian, IsBipedal(true)))
122            .unwrap();
123        let expected_id: EntityId<TestAvian> = EntityId::new(0);
124        assert_eq!(sampled_id, expected_id);
125    }
126}