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 StagedFormulaStateChanged {
150 before: Vec<(String, u32, u32, String)>,
151 after: Vec<(String, u32, u32, String)>,
152 },
153}
154
155#[derive(Debug, Default)]
157pub struct ChangeLog {
158 events: Vec<ChangeEvent>,
159 metas: Vec<ChangeEventMeta>,
160 enabled: bool,
161 max_changelog_events: Option<usize>,
163 compound_depth: usize,
165 seqs: Vec<u64>,
167 groups: Vec<Option<u64>>,
169 next_seq: u64,
170 group_stack: Vec<u64>,
172 next_group_id: u64,
173
174 current_meta: ChangeEventMeta,
175}
176
177impl ChangeLog {
178 pub fn new() -> Self {
179 Self {
180 events: Vec::new(),
181 metas: Vec::new(),
182 enabled: true,
183 max_changelog_events: None,
184 compound_depth: 0,
185 seqs: Vec::new(),
186 groups: Vec::new(),
187 next_seq: 0,
188 group_stack: Vec::new(),
189 next_group_id: 1,
190 current_meta: ChangeEventMeta::default(),
191 }
192 }
193
194 pub fn with_max_changelog_events(max: usize) -> Self {
195 let mut out = Self::new();
196 out.max_changelog_events = Some(max);
197 out
198 }
199
200 pub fn set_max_changelog_events(&mut self, max: Option<usize>) {
201 self.max_changelog_events = max;
202 self.enforce_cap();
203 }
204
205 fn enforce_cap(&mut self) {
206 let Some(max) = self.max_changelog_events else {
207 return;
208 };
209 if max == 0 {
210 self.clear();
211 return;
212 }
213 if self.events.len() <= max {
214 return;
215 }
216 let drop_n = self.events.len() - max;
217 self.events.drain(0..drop_n);
218 self.metas.drain(0..drop_n);
219 self.seqs.drain(0..drop_n);
220 self.groups.drain(0..drop_n);
221 }
222
223 pub fn record(&mut self, event: ChangeEvent) {
224 if self.enabled {
225 let seq = self.next_seq;
226 self.next_seq += 1;
227 let current_group = self.group_stack.last().copied();
228 self.events.push(event);
229 self.metas.push(self.current_meta.clone());
230 self.seqs.push(seq);
231 self.groups.push(current_group);
232 self.enforce_cap();
233 }
234 }
235
236 pub fn record_with_meta(&mut self, event: ChangeEvent, meta: ChangeEventMeta) {
238 if self.enabled {
239 let seq = self.next_seq;
240 self.next_seq += 1;
241 let current_group = self.group_stack.last().copied();
242 self.events.push(event);
243 self.metas.push(meta);
244 self.seqs.push(seq);
245 self.groups.push(current_group);
246 self.enforce_cap();
247 }
248 }
249
250 pub fn begin_compound(&mut self, description: String) {
252 self.compound_depth += 1;
253 if self.compound_depth == 1 {
254 let gid = self.next_group_id;
256 self.next_group_id += 1;
257 self.group_stack.push(gid);
258 } else {
259 if let Some(&gid) = self.group_stack.last() {
261 self.group_stack.push(gid);
262 }
263 }
264 if self.enabled {
265 self.record(ChangeEvent::CompoundStart {
266 description,
267 depth: self.compound_depth,
268 });
269 }
270 }
271
272 pub fn end_compound(&mut self) {
274 if self.compound_depth > 0 {
275 if self.enabled {
276 self.record(ChangeEvent::CompoundEnd {
277 depth: self.compound_depth,
278 });
279 }
280 self.compound_depth -= 1;
281 self.group_stack.pop();
282 }
283 }
284
285 pub fn events(&self) -> &[ChangeEvent] {
286 &self.events
287 }
288
289 pub fn patch_last_cell_event_old_state(
290 &mut self,
291 addr: CellRef,
292 old_value: Option<LiteralValue>,
293 old_formula: Option<ASTNode>,
294 ) {
295 for ev in self.events.iter_mut().rev() {
299 match ev {
300 ChangeEvent::SetValue {
301 addr: a,
302 old_value: ov,
303 old_formula: of,
304 ..
305 }
306 | ChangeEvent::SetFormula {
307 addr: a,
308 old_value: ov,
309 old_formula: of,
310 ..
311 } if *a == addr => {
312 if ov.is_none() {
313 *ov = old_value;
314 }
315 if of.is_none() {
316 *of = old_formula;
317 }
318 break;
319 }
320 _ => {}
321 }
322 }
323 }
324
325 pub fn event_meta(&self, index: usize) -> Option<&ChangeEventMeta> {
326 self.metas.get(index)
327 }
328
329 pub fn set_actor_id(&mut self, actor_id: Option<String>) {
330 self.current_meta.actor_id = actor_id;
331 }
332
333 pub fn set_correlation_id(&mut self, correlation_id: Option<String>) {
334 self.current_meta.correlation_id = correlation_id;
335 }
336
337 pub fn set_reason(&mut self, reason: Option<String>) {
338 self.current_meta.reason = reason;
339 }
340
341 pub fn truncate(&mut self, len: usize) {
343 self.events.truncate(len);
344 self.metas.truncate(len);
345 self.seqs.truncate(len);
346 self.groups.truncate(len);
347 }
348
349 pub fn clear(&mut self) {
350 self.events.clear();
351 self.metas.clear();
352 self.seqs.clear();
353 self.groups.clear();
354 self.compound_depth = 0;
355 self.group_stack.clear();
356 }
357
358 pub fn len(&self) -> usize {
359 self.events.len()
360 }
361
362 pub fn is_empty(&self) -> bool {
363 self.events.is_empty()
364 }
365
366 pub fn take_from(&mut self, index: usize) -> Vec<ChangeEvent> {
368 let events = self.events.split_off(index);
369 let _ = self.metas.split_off(index);
370 let _ = self.seqs.split_off(index);
371 let _ = self.groups.split_off(index);
372 events
373 }
374
375 pub fn set_enabled(&mut self, enabled: bool) {
377 self.enabled = enabled;
378 }
379
380 pub fn compound_depth(&self) -> usize {
382 self.compound_depth
383 }
384
385 pub fn meta(&self, index: usize) -> Option<(u64, Option<u64>)> {
387 self.seqs
388 .get(index)
389 .copied()
390 .zip(self.groups.get(index).copied())
391 }
392
393 pub fn last_group_indices(&self) -> Vec<usize> {
395 if let Some(&last_gid) = self.groups.iter().rev().flatten().next() {
396 let idxs: Vec<usize> = self
397 .groups
398 .iter()
399 .enumerate()
400 .filter_map(|(i, g)| if *g == Some(last_gid) { Some(i) } else { None })
401 .collect();
402 if !idxs.is_empty() {
403 return idxs;
404 }
405 }
406 self.events.len().checked_sub(1).into_iter().collect()
407 }
408}
409
410pub trait ChangeLogger {
412 fn record(&mut self, event: ChangeEvent);
413 fn set_enabled(&mut self, enabled: bool);
414 fn begin_compound(&mut self, description: String);
415 fn end_compound(&mut self);
416}
417
418impl ChangeLogger for ChangeLog {
419 fn record(&mut self, event: ChangeEvent) {
420 ChangeLog::record(self, event);
421 }
422
423 fn set_enabled(&mut self, enabled: bool) {
424 self.enabled = enabled;
425 }
426
427 fn begin_compound(&mut self, description: String) {
428 ChangeLog::begin_compound(self, description);
429 }
430
431 fn end_compound(&mut self) {
432 ChangeLog::end_compound(self);
433 }
434}
435
436pub struct NullChangeLogger;
438
439impl ChangeLogger for NullChangeLogger {
440 fn record(&mut self, _: ChangeEvent) {}
441 fn set_enabled(&mut self, _: bool) {}
442 fn begin_compound(&mut self, _: String) {}
443 fn end_compound(&mut self) {}
444}