1use smallbox::space::S4;
30use smallbox::SmallBox;
31
32use crate::entity::property::Property;
33use crate::entity::{ContextEntitiesExt, Entity, EntityId};
34use crate::{Context, IxaEvent};
35
36pub(crate) type PartialPropertyChangeEventBox = SmallBox<dyn PartialPropertyChangeEvent, S4>;
49
50pub(crate) trait PartialPropertyChangeEvent {
53 fn emit_in_context(&mut self, context: &mut Context);
55}
56
57impl<E: Entity, P: Property<E>> PartialPropertyChangeEvent
58 for PartialPropertyChangeEventCore<E, P>
59{
60 fn emit_in_context(&mut self, context: &mut Context) {
62 self.0.current = context.get_property(self.0.entity_id);
63
64 {
65 let property_value_store = context.get_property_value_store::<E, P>();
67 if self.0.current != self.0.previous {
68 for counter in &property_value_store.value_change_counters {
69 counter
70 .borrow_mut()
71 .update(self.0.entity_id, self.0.current, context);
72 }
73 }
74 }
75
76 let property_value_store = context.get_property_value_store_mut::<E, P>();
78 if let Some(index) = property_value_store.index.as_mut() {
79 index.remove_entity(&self.0.previous, self.0.entity_id);
81 index.add_entity(&self.0.current, self.0.entity_id);
83 }
84
85 context.emit_event(self.to_event());
92 }
93}
94
95#[repr(transparent)]
102pub(crate) struct PartialPropertyChangeEventCore<E: Entity, P: Property<E>>(
103 PropertyChangeEvent<E, P>,
104);
105impl<E: Entity, P: Property<E>> Clone for PartialPropertyChangeEventCore<E, P> {
108 fn clone(&self) -> Self {
109 *self
110 }
111}
112impl<E: Entity, P: Property<E>> Copy for PartialPropertyChangeEventCore<E, P> {}
113
114impl<E: Entity, P: Property<E>> PartialPropertyChangeEventCore<E, P> {
115 pub fn new(entity_id: EntityId<E>, previous_value: P) -> Self {
116 Self(PropertyChangeEvent {
117 entity_id,
118 current: previous_value,
119 previous: previous_value,
120 })
121 }
122
123 pub fn to_event(self) -> PropertyChangeEvent<E, P> {
124 self.0
125 }
126}
127
128#[derive(IxaEvent)]
131pub struct EntityCreatedEvent<E: Entity> {
132 pub entity_id: EntityId<E>,
134}
135
136impl<E: Entity> EntityCreatedEvent<E> {
137 #[must_use]
138 pub fn new(entity_id: EntityId<E>) -> Self {
139 Self { entity_id }
140 }
141}
142
143#[derive(IxaEvent)]
146pub struct PropertyChangeEvent<E: Entity, P: Property<E>> {
147 pub entity_id: EntityId<E>,
149 pub current: P,
151 pub previous: P,
153}
154
155#[cfg(test)]
156mod tests {
157 use std::cell::RefCell;
158 use std::rc::Rc;
159
160 use super::*;
161 use crate::{define_derived_property, define_entity, define_property, with, Context};
162
163 define_entity!(Person);
164
165 define_property!(struct Age(u8), Person );
166
167 define_derived_property!(
171 enum AgeGroup {
172 Child,
173 Adult,
174 },
175 Person,
176 [Age], [], |age| {
179 let age: Age = age;
180 if age.0 < 18 {
181 AgeGroup::Child
182 } else {
183 AgeGroup::Adult
184 }
185 }
186 );
187
188 define_property!(
189 enum RiskCategory {
190 High,
191 Low,
192 },
193 Person
194 );
195
196 define_property!(struct IsRunner(bool), Person, default_const = IsRunner(false));
197
198 define_property!(struct RunningShoes(u8), Person );
199
200 #[test]
201 fn observe_entity_addition() {
202 let mut context = Context::new();
203
204 let flag = Rc::new(RefCell::new(false));
205 let flag_clone = flag.clone();
206 context.subscribe_to_event(move |_context, event: EntityCreatedEvent<Person>| {
207 *flag_clone.borrow_mut() = true;
208 assert_eq!(event.entity_id.0, 0);
209 });
210
211 let _ = context
212 .add_entity::<Person, _>(with!(Person, Age(18), RunningShoes(33), RiskCategory::Low))
213 .unwrap();
214 context.execute();
215 assert!(*flag.borrow());
216 }
217
218 #[test]
219 fn observe_entity_property_change() {
220 let mut context = Context::new();
221
222 let flag = Rc::new(RefCell::new(false));
223 let flag_clone = flag.clone();
224 context.subscribe_to_event(
225 move |_context, event: PropertyChangeEvent<Person, RiskCategory>| {
226 *flag_clone.borrow_mut() = true;
227 assert_eq!(event.entity_id.0, 0, "Entity id is correct");
228 assert_eq!(
229 event.previous,
230 RiskCategory::Low,
231 "Previous value is correct"
232 );
233 assert_eq!(
234 event.current,
235 RiskCategory::High,
236 "Current value is correct"
237 );
238 },
239 );
240
241 let person_id = context
242 .add_entity(with!(Person, Age(9), RunningShoes(33), RiskCategory::Low))
243 .unwrap();
244
245 context.set_property(person_id, RiskCategory::High);
246 context.execute();
247 assert!(*flag.borrow());
248 }
249
250 #[test]
251 fn observe_entity_property_change_with_set() {
252 let mut context = Context::new();
253
254 let flag = Rc::new(RefCell::new(false));
255 let flag_clone = flag.clone();
256 context.subscribe_to_event(
257 move |_context, _event: PropertyChangeEvent<Person, RunningShoes>| {
258 *flag_clone.borrow_mut() = true;
259 },
260 );
261 let person_id = context
263 .add_entity(with!(Person, Age(9), RunningShoes(33), RiskCategory::Low))
264 .unwrap();
265 context.set_property(person_id, RunningShoes(42));
267 context.execute();
268 assert!(*flag.borrow());
269 }
270
271 #[test]
272 fn get_entity_property_change_event() {
273 let mut context = Context::new();
274 let person = context
275 .add_entity(with!(Person, Age(17), RunningShoes(33), RiskCategory::Low))
276 .unwrap();
277
278 let flag = Rc::new(RefCell::new(false));
279
280 let flag_clone = flag.clone();
281 context.subscribe_to_event(
282 move |_context, event: PropertyChangeEvent<Person, AgeGroup>| {
283 assert_eq!(event.entity_id.0, 0);
284 assert_eq!(event.previous, AgeGroup::Child);
285 assert_eq!(event.current, AgeGroup::Adult);
286 *flag_clone.borrow_mut() = true;
287 },
288 );
289 context.set_property(person, Age(18));
290 context.execute();
291 assert!(*flag.borrow());
292 }
293
294 #[test]
295 fn test_person_property_change_event_no_people() {
296 let mut context = Context::new();
297 context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, IsRunner>| {
299 unreachable!();
300 });
301
302 context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, AgeGroup>| {
304 unreachable!();
305 });
306 }
307}