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}
56
57#[derive(Debug, Clone)]
59struct PropertySnapshot {
60 serialized: String,
62}
63
64impl std::fmt::Debug for TrackerEntry {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct("TrackerEntry")
67 .field("id", &self.id)
68 .field("type_name", &self.type_name)
69 .field("state", &self.state)
70 .finish()
71 }
72}
73
74impl ChangeTracker {
75 pub fn new() -> Self {
76 Self {
77 entries: Vec::new(),
78 auto_detect_changes: true,
79 next_id: 0,
80 }
81 }
82
83 pub fn track_entity_with_snapshot(
88 &mut self,
89 type_id: std::any::TypeId,
90 type_name: &str,
91 state: EntityState,
92 snapshot: HashMap<String, String>,
93 ) -> u64 {
94 let id = self.next_id;
95 self.next_id += 1;
96
97 self.entries.push(TrackerEntry {
98 id,
99 type_id,
100 type_name: type_name.to_string(),
101 state,
102 snapshot: snapshot
103 .into_iter()
104 .map(|(k, v)| (k, PropertySnapshot { serialized: v }))
105 .collect(),
106 });
107
108 id
109 }
110
111 pub fn track_entity(
114 &mut self,
115 type_id: std::any::TypeId,
116 type_name: &str,
117 state: EntityState,
118 ) -> u64 {
119 self.track_entity_with_snapshot(type_id, type_name, state, HashMap::new())
120 }
121
122 pub fn detect_changes_with_properties(
126 &mut self,
127 current_properties: &[(u64, HashMap<String, String>)],
128 ) {
129 let current_map: HashMap<u64, &HashMap<String, String>> = current_properties
131 .iter()
132 .map(|(id, props)| (*id, props))
133 .collect();
134
135 for entry in &mut self.entries {
136 if entry.state != EntityState::Unchanged {
138 continue;
139 }
140
141 if let Some(current) = current_map.get(&entry.id) {
142 for (prop_name, snapshot) in &entry.snapshot {
143 let changed = match current.get(prop_name) {
144 Some(current_val) => current_val != &snapshot.serialized,
145 None => true, };
147
148 if changed {
149 entry.state = EntityState::Modified;
150 break; }
152 }
153 }
154 }
155 }
156
157 pub fn update_snapshot(&mut self, entry_id: u64, properties: HashMap<String, String>) {
160 if let Some(entry) = self.entries.iter_mut().find(|e| e.id == entry_id) {
161 entry.snapshot = properties
162 .into_iter()
163 .map(|(k, v)| (k, PropertySnapshot { serialized: v }))
164 .collect();
165 }
166 }
167
168 pub fn has_changes(&self) -> bool {
170 self.entries.iter().any(|e| {
171 matches!(
172 e.state,
173 EntityState::Added | EntityState::Modified | EntityState::Deleted
174 )
175 })
176 }
177
178 pub fn clear(&mut self) {
180 self.entries.clear();
181 }
182
183 pub fn entries(&self) -> Vec<EntityEntry> {
185 self.entries
186 .iter()
187 .map(|e| EntityEntry {
188 entry_id: e.id,
189 type_id: e.type_id,
190 type_name: e.type_name.clone(),
191 state: e.state,
192 modified_properties: Vec::new(),
193 })
194 .collect()
195 }
196
197 pub fn count_by_state(&self, state: EntityState) -> usize {
199 self.entries.iter().filter(|e| e.state == state).count()
200 }
201
202 pub fn entries_by_state(&self, state: EntityState) -> Vec<EntityEntry> {
204 self.entries()
205 .into_iter()
206 .filter(|e| e.state == state)
207 .collect()
208 }
209
210 pub fn accept_all_changes(&mut self) {
215 self.entries.retain(|e| e.state != EntityState::Deleted);
216 for entry in &mut self.entries {
217 if entry.state == EntityState::Added || entry.state == EntityState::Modified {
218 entry.state = EntityState::Unchanged;
219 }
220 }
221 }
222
223 pub fn reject_all_changes(&mut self) {
225 self.entries.retain(|e| e.state != EntityState::Added);
226 for entry in &mut self.entries {
227 if entry.state == EntityState::Modified || entry.state == EntityState::Deleted {
228 entry.state = EntityState::Unchanged;
229 }
230 }
231 }
232
233 pub fn detach(&mut self, entry_id: u64) {
235 self.entries.retain(|e| e.id != entry_id);
236 }
237
238 pub fn is_auto_detect_changes_enabled(&self) -> bool {
239 self.auto_detect_changes
240 }
241
242 pub fn set_auto_detect_changes(&mut self, enabled: bool) {
243 self.auto_detect_changes = enabled;
244 }
245}
246
247impl Default for ChangeTracker {
248 fn default() -> Self {
249 Self::new()
250 }
251}
252
253#[derive(Debug)]
258pub struct TrackedEntity<T> {
259 pub entity: T,
260 pub entry_id: u64,
261 pub state: EntityState,
262}
263
264impl<T> TrackedEntity<T> {
265 pub fn new(entity: T, entry_id: u64, state: EntityState) -> Self {
266 Self {
267 entity,
268 entry_id,
269 state,
270 }
271 }
272}