formualizer_eval/engine/graph/editor/
change_log.rs1use crate::SheetId;
9use crate::engine::named_range::{NameScope, NamedDefinition};
10use crate::engine::row_visibility::RowVisibilitySource;
11use crate::engine::vertex::VertexId;
12use crate::reference::CellRef;
13use formualizer_common::Coord as AbsCoord;
14use formualizer_common::LiteralValue;
15use formualizer_parse::parser::ASTNode;
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct SpillSnapshot {
19 pub target_cells: Vec<CellRef>,
21 pub values: Vec<Vec<LiteralValue>>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Default)]
30pub struct ChangeEventMeta {
31 pub actor_id: Option<String>,
32 pub correlation_id: Option<String>,
33 pub reason: Option<String>,
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub enum ChangeEvent {
39 SetValue {
41 addr: CellRef,
42 old_value: Option<LiteralValue>,
43 old_formula: Option<ASTNode>,
44 new: LiteralValue,
45 },
46 SetFormula {
47 addr: CellRef,
48 old_value: Option<LiteralValue>,
49 old_formula: Option<ASTNode>,
50 new: ASTNode,
51 },
52 SetRowVisibility {
53 sheet_id: SheetId,
54 row0: u32,
55 source: RowVisibilitySource,
56 old_hidden: bool,
57 new_hidden: bool,
58 },
59 AddVertex {
61 id: VertexId,
62 coord: AbsCoord,
63 sheet_id: SheetId,
64 value: Option<LiteralValue>,
65 formula: Option<ASTNode>,
66 kind: Option<crate::engine::vertex::VertexKind>,
67 flags: Option<u8>,
68 },
69 RemoveVertex {
70 id: VertexId,
71 old_value: Option<LiteralValue>,
73 old_formula: Option<ASTNode>,
74 old_dependencies: Vec<VertexId>, old_dependents: Vec<VertexId>, coord: Option<AbsCoord>,
77 sheet_id: Option<SheetId>,
78 kind: Option<crate::engine::vertex::VertexKind>,
79 flags: Option<u8>,
80 },
81
82 CompoundStart {
84 description: String, depth: usize,
86 },
87 CompoundEnd {
88 depth: usize,
89 },
90
91 VertexMoved {
93 id: VertexId,
94 sheet_id: SheetId,
95 old_coord: AbsCoord,
96 new_coord: AbsCoord,
97 },
98 FormulaAdjusted {
99 id: VertexId,
100 addr: Option<CellRef>,
102 old_ast: ASTNode,
103 new_ast: ASTNode,
104 },
105 NamedRangeAdjusted {
106 name: String,
107 scope: NameScope,
108 old_definition: NamedDefinition,
109 new_definition: NamedDefinition,
110 },
111 EdgeAdded {
112 from: VertexId,
113 to: VertexId,
114 },
115 EdgeRemoved {
116 from: VertexId,
117 to: VertexId,
118 },
119
120 DefineName {
122 name: String,
123 scope: NameScope,
124 definition: NamedDefinition,
125 },
126 UpdateName {
127 name: String,
128 scope: NameScope,
129 old_definition: NamedDefinition,
130 new_definition: NamedDefinition,
131 },
132 DeleteName {
133 name: String,
134 scope: NameScope,
135 old_definition: Option<NamedDefinition>,
136 },
137
138 SpillCommitted {
140 anchor: VertexId,
141 old: Option<SpillSnapshot>,
142 new: SpillSnapshot,
143 },
144 SpillCleared {
145 anchor: VertexId,
146 old: SpillSnapshot,
147 },
148}
149
150#[derive(Debug, Default)]
152pub struct ChangeLog {
153 events: Vec<ChangeEvent>,
154 metas: Vec<ChangeEventMeta>,
155 enabled: bool,
156 max_changelog_events: Option<usize>,
158 compound_depth: usize,
160 seqs: Vec<u64>,
162 groups: Vec<Option<u64>>,
164 next_seq: u64,
165 group_stack: Vec<u64>,
167 next_group_id: u64,
168
169 current_meta: ChangeEventMeta,
170}
171
172impl ChangeLog {
173 pub fn new() -> Self {
174 Self {
175 events: Vec::new(),
176 metas: Vec::new(),
177 enabled: true,
178 max_changelog_events: None,
179 compound_depth: 0,
180 seqs: Vec::new(),
181 groups: Vec::new(),
182 next_seq: 0,
183 group_stack: Vec::new(),
184 next_group_id: 1,
185 current_meta: ChangeEventMeta::default(),
186 }
187 }
188
189 pub fn with_max_changelog_events(max: usize) -> Self {
190 let mut out = Self::new();
191 out.max_changelog_events = Some(max);
192 out
193 }
194
195 pub fn set_max_changelog_events(&mut self, max: Option<usize>) {
196 self.max_changelog_events = max;
197 self.enforce_cap();
198 }
199
200 fn enforce_cap(&mut self) {
201 let Some(max) = self.max_changelog_events else {
202 return;
203 };
204 if max == 0 {
205 self.clear();
206 return;
207 }
208 if self.events.len() <= max {
209 return;
210 }
211 let drop_n = self.events.len() - max;
212 self.events.drain(0..drop_n);
213 self.metas.drain(0..drop_n);
214 self.seqs.drain(0..drop_n);
215 self.groups.drain(0..drop_n);
216 }
217
218 pub fn record(&mut self, event: ChangeEvent) {
219 if self.enabled {
220 let seq = self.next_seq;
221 self.next_seq += 1;
222 let current_group = self.group_stack.last().copied();
223 self.events.push(event);
224 self.metas.push(self.current_meta.clone());
225 self.seqs.push(seq);
226 self.groups.push(current_group);
227 self.enforce_cap();
228 }
229 }
230
231 pub fn record_with_meta(&mut self, event: ChangeEvent, meta: ChangeEventMeta) {
233 if self.enabled {
234 let seq = self.next_seq;
235 self.next_seq += 1;
236 let current_group = self.group_stack.last().copied();
237 self.events.push(event);
238 self.metas.push(meta);
239 self.seqs.push(seq);
240 self.groups.push(current_group);
241 self.enforce_cap();
242 }
243 }
244
245 pub fn begin_compound(&mut self, description: String) {
247 self.compound_depth += 1;
248 if self.compound_depth == 1 {
249 let gid = self.next_group_id;
251 self.next_group_id += 1;
252 self.group_stack.push(gid);
253 } else {
254 if let Some(&gid) = self.group_stack.last() {
256 self.group_stack.push(gid);
257 }
258 }
259 if self.enabled {
260 self.record(ChangeEvent::CompoundStart {
261 description,
262 depth: self.compound_depth,
263 });
264 }
265 }
266
267 pub fn end_compound(&mut self) {
269 if self.compound_depth > 0 {
270 if self.enabled {
271 self.record(ChangeEvent::CompoundEnd {
272 depth: self.compound_depth,
273 });
274 }
275 self.compound_depth -= 1;
276 self.group_stack.pop();
277 }
278 }
279
280 pub fn events(&self) -> &[ChangeEvent] {
281 &self.events
282 }
283
284 pub fn patch_last_cell_event_old_state(
285 &mut self,
286 addr: CellRef,
287 old_value: Option<LiteralValue>,
288 old_formula: Option<ASTNode>,
289 ) {
290 for ev in self.events.iter_mut().rev() {
294 match ev {
295 ChangeEvent::SetValue {
296 addr: a,
297 old_value: ov,
298 old_formula: of,
299 ..
300 }
301 | ChangeEvent::SetFormula {
302 addr: a,
303 old_value: ov,
304 old_formula: of,
305 ..
306 } if *a == addr => {
307 if ov.is_none() {
308 *ov = old_value;
309 }
310 if of.is_none() {
311 *of = old_formula;
312 }
313 break;
314 }
315 _ => {}
316 }
317 }
318 }
319
320 pub fn event_meta(&self, index: usize) -> Option<&ChangeEventMeta> {
321 self.metas.get(index)
322 }
323
324 pub fn set_actor_id(&mut self, actor_id: Option<String>) {
325 self.current_meta.actor_id = actor_id;
326 }
327
328 pub fn set_correlation_id(&mut self, correlation_id: Option<String>) {
329 self.current_meta.correlation_id = correlation_id;
330 }
331
332 pub fn set_reason(&mut self, reason: Option<String>) {
333 self.current_meta.reason = reason;
334 }
335
336 pub fn truncate(&mut self, len: usize) {
338 self.events.truncate(len);
339 self.metas.truncate(len);
340 self.seqs.truncate(len);
341 self.groups.truncate(len);
342 }
343
344 pub fn clear(&mut self) {
345 self.events.clear();
346 self.metas.clear();
347 self.seqs.clear();
348 self.groups.clear();
349 self.compound_depth = 0;
350 self.group_stack.clear();
351 }
352
353 pub fn len(&self) -> usize {
354 self.events.len()
355 }
356
357 pub fn is_empty(&self) -> bool {
358 self.events.is_empty()
359 }
360
361 pub fn take_from(&mut self, index: usize) -> Vec<ChangeEvent> {
363 let events = self.events.split_off(index);
364 let _ = self.metas.split_off(index);
365 let _ = self.seqs.split_off(index);
366 let _ = self.groups.split_off(index);
367 events
368 }
369
370 pub fn set_enabled(&mut self, enabled: bool) {
372 self.enabled = enabled;
373 }
374
375 pub fn compound_depth(&self) -> usize {
377 self.compound_depth
378 }
379
380 pub fn meta(&self, index: usize) -> Option<(u64, Option<u64>)> {
382 self.seqs
383 .get(index)
384 .copied()
385 .zip(self.groups.get(index).copied())
386 }
387
388 pub fn last_group_indices(&self) -> Vec<usize> {
390 if let Some(&last_gid) = self.groups.iter().rev().flatten().next() {
391 let idxs: Vec<usize> = self
392 .groups
393 .iter()
394 .enumerate()
395 .filter_map(|(i, g)| if *g == Some(last_gid) { Some(i) } else { None })
396 .collect();
397 if !idxs.is_empty() {
398 return idxs;
399 }
400 }
401 self.events.len().checked_sub(1).into_iter().collect()
402 }
403}
404
405pub trait ChangeLogger {
407 fn record(&mut self, event: ChangeEvent);
408 fn set_enabled(&mut self, enabled: bool);
409 fn begin_compound(&mut self, description: String);
410 fn end_compound(&mut self);
411}
412
413impl ChangeLogger for ChangeLog {
414 fn record(&mut self, event: ChangeEvent) {
415 ChangeLog::record(self, event);
416 }
417
418 fn set_enabled(&mut self, enabled: bool) {
419 self.enabled = enabled;
420 }
421
422 fn begin_compound(&mut self, description: String) {
423 ChangeLog::begin_compound(self, description);
424 }
425
426 fn end_compound(&mut self) {
427 ChangeLog::end_compound(self);
428 }
429}
430
431pub struct NullChangeLogger;
433
434impl ChangeLogger for NullChangeLogger {
435 fn record(&mut self, _: ChangeEvent) {}
436 fn set_enabled(&mut self, _: bool) {}
437 fn begin_compound(&mut self, _: String) {}
438 fn end_compound(&mut self) {}
439}