1use crate::entity::EntityState;
11use std::collections::HashMap;
12
13#[derive(Debug)]
15pub struct ChangeTracker {
16 entries: Vec<TrackerEntry>,
17 auto_detect_changes: bool,
18 next_id: u64,
20}
21
22#[derive(Debug, Clone)]
24pub struct EntityEntry {
25 pub entry_id: u64,
26 pub type_id: std::any::TypeId,
27 pub type_name: String,
28 pub state: EntityState,
29 pub modified_properties: Vec<String>,
31}
32
33#[derive(Debug, Clone)]
41pub struct EntityEntryView {
42 pub type_id: std::any::TypeId,
43 pub type_name: String,
44 pub state: EntityState,
45}
46
47struct TrackerEntry {
49 id: u64,
50 type_id: std::any::TypeId,
51 type_name: String,
52 state: EntityState,
53 snapshot: HashMap<String, PropertySnapshot>,
55 modified_properties: Vec<String>,
59}
60
61#[derive(Debug, Clone)]
63struct PropertySnapshot {
64 serialized: String,
66}
67
68impl std::fmt::Debug for TrackerEntry {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.debug_struct("TrackerEntry")
71 .field("id", &self.id)
72 .field("type_name", &self.type_name)
73 .field("state", &self.state)
74 .finish()
75 }
76}
77
78impl ChangeTracker {
79 pub fn new() -> Self {
80 Self {
81 entries: Vec::new(),
82 auto_detect_changes: true,
83 next_id: 0,
84 }
85 }
86
87 pub fn track_entity_with_snapshot(
92 &mut self,
93 type_id: std::any::TypeId,
94 type_name: &str,
95 state: EntityState,
96 snapshot: HashMap<String, String>,
97 ) -> u64 {
98 let id = self.next_id;
99 self.next_id += 1;
100
101 self.entries.push(TrackerEntry {
102 id,
103 type_id,
104 type_name: type_name.to_string(),
105 state,
106 snapshot: snapshot
107 .into_iter()
108 .map(|(k, v)| (k, PropertySnapshot { serialized: v }))
109 .collect(),
110 modified_properties: Vec::new(),
111 });
112
113 id
114 }
115
116 pub fn track_entity(
119 &mut self,
120 type_id: std::any::TypeId,
121 type_name: &str,
122 state: EntityState,
123 ) -> u64 {
124 self.track_entity_with_snapshot(type_id, type_name, state, HashMap::new())
125 }
126
127 pub fn detect_changes_with_properties(
133 &mut self,
134 current_properties: &[(u64, HashMap<String, String>)],
135 ) {
136 let current_map: HashMap<u64, &HashMap<String, String>> = current_properties
138 .iter()
139 .map(|(id, props)| (*id, props))
140 .collect();
141
142 for entry in &mut self.entries {
143 if entry.state != EntityState::Unchanged {
145 continue;
146 }
147
148 if let Some(current) = current_map.get(&entry.id) {
149 let mut changed_props: Vec<String> = Vec::new();
150 for (prop_name, snapshot) in &entry.snapshot {
151 let changed = match current.get(prop_name) {
152 Some(current_val) => current_val != &snapshot.serialized,
153 None => true, };
155
156 if changed {
157 changed_props.push(prop_name.clone());
158 }
159 }
160
161 if !changed_props.is_empty() {
162 entry.state = EntityState::Modified;
163 entry.modified_properties = changed_props;
164 } else {
165 entry.modified_properties.clear();
166 }
167 }
168 }
169 }
170
171 pub fn update_snapshot(&mut self, entry_id: u64, properties: HashMap<String, String>) {
174 if let Some(entry) = self.entries.iter_mut().find(|e| e.id == entry_id) {
175 entry.snapshot = properties
176 .into_iter()
177 .map(|(k, v)| (k, PropertySnapshot { serialized: v }))
178 .collect();
179 }
180 }
181
182 pub fn has_changes(&self) -> bool {
184 self.entries.iter().any(|e| {
185 matches!(
186 e.state,
187 EntityState::Added | EntityState::Modified | EntityState::Deleted
188 )
189 })
190 }
191
192 pub fn clear(&mut self) {
194 self.entries.clear();
195 }
196
197 pub fn entries(&self) -> Vec<EntityEntry> {
199 self.entries
200 .iter()
201 .map(|e| EntityEntry {
202 entry_id: e.id,
203 type_id: e.type_id,
204 type_name: e.type_name.clone(),
205 state: e.state,
206 modified_properties: e.modified_properties.clone(),
207 })
208 .collect()
209 }
210
211 pub fn count_by_state(&self, state: EntityState) -> usize {
213 self.entries.iter().filter(|e| e.state == state).count()
214 }
215
216 pub fn entries_by_state(&self, state: EntityState) -> Vec<EntityEntry> {
218 self.entries()
219 .into_iter()
220 .filter(|e| e.state == state)
221 .collect()
222 }
223
224 pub fn accept_all_changes(&mut self) {
229 self.entries.retain(|e| e.state != EntityState::Deleted);
230 for entry in &mut self.entries {
231 if entry.state == EntityState::Added || entry.state == EntityState::Modified {
232 entry.state = EntityState::Unchanged;
233 entry.modified_properties.clear();
234 }
235 }
236 }
237
238 pub fn reject_all_changes(&mut self) {
240 self.entries.retain(|e| e.state != EntityState::Added);
241 for entry in &mut self.entries {
242 if entry.state == EntityState::Modified || entry.state == EntityState::Deleted {
243 entry.state = EntityState::Unchanged;
244 entry.modified_properties.clear();
245 }
246 }
247 }
248
249 pub fn detach(&mut self, entry_id: u64) {
251 self.entries.retain(|e| e.id != entry_id);
252 }
253
254 pub fn is_auto_detect_changes_enabled(&self) -> bool {
255 self.auto_detect_changes
256 }
257
258 pub fn set_auto_detect_changes(&mut self, enabled: bool) {
259 self.auto_detect_changes = enabled;
260 }
261}
262
263impl Default for ChangeTracker {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269#[derive(Debug)]
274pub struct TrackedEntity<T> {
275 pub entity: T,
276 pub entry_id: u64,
277 pub state: EntityState,
278}
279
280impl<T> TrackedEntity<T> {
281 pub fn new(entity: T, entry_id: u64, state: EntityState) -> Self {
282 Self {
283 entity,
284 entry_id,
285 state,
286 }
287 }
288}