1use ixa_derive::IxaEvent;
30
31use crate::entity::property::Property;
32use crate::entity::{ContextEntitiesExt, Entity, EntityId};
33use crate::{Context, IxaEvent};
34
35pub(crate) trait PartialPropertyChangeEvent {
37 fn emit_in_context(self: Box<Self>, context: &mut Context);
38}
39
40impl<E: Entity, P: Property<E>> PartialPropertyChangeEvent
41 for PartialPropertyChangeEventCore<E, P>
42{
43 fn emit_in_context(self: Box<Self>, context: &mut Context) {
45 let current_value: P = context.get_property(self.0.entity_id);
46 let property_value_store = context.get_property_value_store_mut::<E, P>();
47
48 property_value_store
49 .index
50 .add_entity(¤t_value.make_canonical(), self.0.entity_id);
51
52 context.emit_event(self.to_event(current_value));
59 }
60}
61
62#[repr(transparent)]
68pub(crate) struct PartialPropertyChangeEventCore<E: Entity, P: Property<E>>(
69 PropertyChangeEvent<E, P>,
70);
71impl<E: Entity, P: Property<E>> Clone for PartialPropertyChangeEventCore<E, P> {
74 fn clone(&self) -> Self {
75 *self
76 }
77}
78impl<E: Entity, P: Property<E>> Copy for PartialPropertyChangeEventCore<E, P> {}
79
80impl<E: Entity, P: Property<E>> PartialPropertyChangeEventCore<E, P> {
81 pub fn new(entity_id: EntityId<E>, previous_value: P) -> Self {
82 Self(PropertyChangeEvent {
83 entity_id,
84 current: previous_value,
85 previous: previous_value,
86 })
87 }
88
89 pub fn to_event(mut self, current_value: P) -> PropertyChangeEvent<E, P> {
90 self.0.current = current_value;
91 self.0
92 }
93}
94
95#[derive(IxaEvent)]
98#[allow(clippy::manual_non_exhaustive)]
99pub struct EntityCreatedEvent<E: Entity> {
100 pub entity_id: EntityId<E>,
102}
103impl<E: Entity> Copy for EntityCreatedEvent<E> {}
106impl<E: Entity> Clone for EntityCreatedEvent<E> {
107 fn clone(&self) -> Self {
108 *self
109 }
110}
111
112impl<E: Entity> EntityCreatedEvent<E> {
113 pub fn new(entity_id: EntityId<E>) -> Self {
114 Self { entity_id }
115 }
116}
117
118#[derive(IxaEvent)]
121#[allow(clippy::manual_non_exhaustive)]
122pub struct PropertyChangeEvent<E: Entity, P: Property<E>> {
123 pub entity_id: EntityId<E>,
125 pub current: P,
127 pub previous: P,
129}
130impl<E: Entity, P: Property<E>> Clone for PropertyChangeEvent<E, P> {
133 fn clone(&self) -> Self {
134 *self
135 }
136}
137impl<E: Entity, P: Property<E>> Copy for PropertyChangeEvent<E, P> {}
138
139#[cfg(test)]
140mod tests {
141 use std::cell::RefCell;
142 use std::rc::Rc;
143
144 use super::*;
145 use crate::{define_derived_property, define_entity, define_property, Context};
146
147 define_entity!(Person);
148
149 define_property!(struct Age(u8), Person );
150
151 define_derived_property!(
155 enum AgeGroup {
156 Child,
157 Adult,
158 },
159 Person,
160 [Age], [], |age| {
163 let age: Age = age;
164 if age.0 < 18 {
165 AgeGroup::Child
166 } else {
167 AgeGroup::Adult
168 }
169 }
170 );
171
172 define_property!(
173 enum RiskCategory {
174 High,
175 Low,
176 },
177 Person
178 );
179
180 define_property!(struct IsRunner(bool), Person, default_const = IsRunner(false));
181
182 define_property!(struct RunningShoes(u8), Person );
183
184 #[test]
185 fn observe_entity_addition() {
186 let mut context = Context::new();
187
188 let flag = Rc::new(RefCell::new(false));
189 let flag_clone = flag.clone();
190 context.subscribe_to_event(move |_context, event: EntityCreatedEvent<Person>| {
191 *flag_clone.borrow_mut() = true;
192 assert_eq!(event.entity_id.0, 0);
193 });
194
195 let _ = context
196 .add_entity::<Person, _>((Age(18), RunningShoes(33), RiskCategory::Low))
197 .unwrap();
198 context.execute();
199 assert!(*flag.borrow());
200 }
201
202 #[test]
203 fn observe_entity_property_change() {
204 let mut context = Context::new();
205
206 let flag = Rc::new(RefCell::new(false));
207 let flag_clone = flag.clone();
208 context.subscribe_to_event(
209 move |_context, event: PropertyChangeEvent<Person, RiskCategory>| {
210 *flag_clone.borrow_mut() = true;
211 assert_eq!(event.entity_id.0, 0, "Entity id is correct");
212 assert_eq!(
213 event.previous,
214 RiskCategory::Low,
215 "Previous value is correct"
216 );
217 assert_eq!(
218 event.current,
219 RiskCategory::High,
220 "Current value is correct"
221 );
222 },
223 );
224
225 let person_id = context
226 .add_entity((Age(9), RunningShoes(33), RiskCategory::Low))
227 .unwrap();
228
229 context.set_property(person_id, RiskCategory::High);
230 context.execute();
231 assert!(*flag.borrow());
232 }
233
234 #[test]
235 fn observe_entity_property_change_with_set() {
236 let mut context = Context::new();
237
238 let flag = Rc::new(RefCell::new(false));
239 let flag_clone = flag.clone();
240 context.subscribe_to_event(
241 move |_context, _event: PropertyChangeEvent<Person, RunningShoes>| {
242 *flag_clone.borrow_mut() = true;
243 },
244 );
245 let person_id = context
247 .add_entity((Age(9), RunningShoes(33), RiskCategory::Low))
248 .unwrap();
249 context.set_property(person_id, RunningShoes(42));
251 context.execute();
252 assert!(*flag.borrow());
253 }
254
255 #[test]
256 fn get_entity_property_change_event() {
257 let mut context = Context::new();
258 let person = context
259 .add_entity((Age(17), RunningShoes(33), RiskCategory::Low))
260 .unwrap();
261
262 let flag = Rc::new(RefCell::new(false));
263
264 let flag_clone = flag.clone();
265 context.subscribe_to_event(
266 move |_context, event: PropertyChangeEvent<Person, AgeGroup>| {
267 assert_eq!(event.entity_id.0, 0);
268 assert_eq!(event.previous, AgeGroup::Child);
269 assert_eq!(event.current, AgeGroup::Adult);
270 *flag_clone.borrow_mut() = true;
271 },
272 );
273 context.set_property(person, Age(18));
274 context.execute();
275 assert!(*flag.borrow());
276 }
277
278 #[test]
279 fn test_person_property_change_event_no_people() {
280 let mut context = Context::new();
281 context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, IsRunner>| {
283 unreachable!();
284 });
285
286 context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, AgeGroup>| {
288 unreachable!();
289 });
290 }
291}