ecsx/change_tracker/
mod.rs1use core::mem;
2
3use alloc::vec::Vec;
4
5use crate::{Component, Entity, PreparedQuery, Substrate, With, Without};
6
7pub struct ChangeTracker<T: Component> {
20 added: PreparedQuery<Without<(Entity, &'static T), &'static Previous<T>>>,
21 changed: PreparedQuery<(Entity, &'static T, &'static mut Previous<T>)>,
22 removed: PreparedQuery<Without<With<Entity, &'static Previous<T>>, &'static T>>,
23
24 added_components: Vec<(Entity, T)>,
25 removed_components: Vec<Entity>,
26}
27
28impl<T: Component> ChangeTracker<T> {
29 pub fn new() -> Self {
31 Self {
32 added: PreparedQuery::new(),
33 changed: PreparedQuery::new(),
34 removed: PreparedQuery::new(),
35
36 added_components: Vec::new(),
37 removed_components: Vec::new(),
38 }
39 }
40
41 pub fn track<'a>(&'a mut self, world: &'a mut Substrate) -> Changes<'a, T>
43 where
44 T: Clone + PartialEq,
45 {
46 Changes {
47 tracker: self,
48 world,
49 added: false,
50 changed: false,
51 removed: false,
52 }
53 }
54}
55
56impl<T: Component> Default for ChangeTracker<T> {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62struct Previous<T>(T);
63
64pub struct Changes<'a, T>
66where
67 T: Component + Clone + PartialEq,
68{
69 tracker: &'a mut ChangeTracker<T>,
70 world: &'a mut Substrate,
71 added: bool,
72 changed: bool,
73 removed: bool,
74}
75
76impl<'a, T> Changes<'a, T>
77where
78 T: Component + Clone + PartialEq,
79{
80 pub fn added(&mut self) -> impl ExactSizeIterator<Item = (Entity, &T)> + '_ {
83 self.tracker.added_components.clear();
84 self.added = true;
85 let tracker = &mut *self.tracker;
86 let added = &mut tracker.added;
87 let added_components = &mut tracker.added_components;
88 DrainOnDrop(
89 added
90 .query_mut(self.world)
91 .inspect(move |&(e, x)| added_components.push((e, x.clone()))),
92 )
93 }
94
95 pub fn changed(&mut self) -> impl Iterator<Item = (Entity, T, &T)> + '_ {
98 self.changed = true;
99 DrainOnDrop(
100 self.tracker
101 .changed
102 .query_mut(self.world)
103 .filter_map(|(e, new, old)| {
104 (*new != old.0).then(|| {
105 let old = mem::replace(&mut old.0, new.clone());
106 (e, old, new)
107 })
108 }),
109 )
110 }
111
112 pub fn removed(&mut self) -> impl ExactSizeIterator<Item = (Entity, T)> + '_ {
115 let tracker = &mut *self.tracker;
116 let removed = &mut tracker.removed;
117 let removed_components = &mut tracker.removed_components;
118 let world = &mut *self.world;
119
120 removed_components.clear();
121 self.removed = true;
122 removed_components.extend(removed.query_mut(world));
125 DrainOnDrop(
126 removed_components
127 .drain(..)
128 .map(move |e| (e, world.remove_one::<Previous<T>>(e).unwrap().0)),
129 )
130 }
131}
132
133impl<T: Component> Drop for Changes<'_, T>
134where
135 T: Component + Clone + PartialEq,
136{
137 fn drop(&mut self) {
138 if !self.added {
139 _ = self.added();
140 }
141 for (entity, component) in self.tracker.added_components.drain(..) {
142 self.world.insert_one(entity, Previous(component)).unwrap();
143 }
144 if !self.changed {
145 _ = self.changed();
146 }
147 if !self.removed {
148 _ = self.removed();
149 }
150 }
151}
152
153struct DrainOnDrop<T: Iterator>(T);
156
157impl<T: Iterator> Iterator for DrainOnDrop<T> {
158 type Item = T::Item;
159
160 fn next(&mut self) -> Option<Self::Item> {
161 self.0.next()
162 }
163}
164
165impl<T: ExactSizeIterator> ExactSizeIterator for DrainOnDrop<T> {
166 fn len(&self) -> usize {
167 self.0.len()
168 }
169}
170
171impl<T: Iterator> Drop for DrainOnDrop<T> {
172 fn drop(&mut self) {
173 for _ in &mut self.0 {}
174 }
175}
176
177#[cfg(test)]
178mod tests;