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 StagedFormulaCellChanged {
162 sheet: String,
163 row: u32,
164 col: u32,
165 old: Option<String>,
166 new: Option<String>,
167 },
168}
169
170#[derive(Debug, Default)]
172pub struct ChangeLog {
173 events: Vec<ChangeEvent>,
174 metas: Vec<ChangeEventMeta>,
175 enabled: bool,
176 max_changelog_events: Option<usize>,
178 compound_depth: usize,
180 seqs: Vec<u64>,
182 groups: Vec<Option<u64>>,
184 next_seq: u64,
185 group_stack: Vec<u64>,
187 next_group_id: u64,
188
189 current_meta: ChangeEventMeta,
190}
191
192impl ChangeLog {
193 pub fn new() -> Self {
194 Self {
195 events: Vec::new(),
196 metas: Vec::new(),
197 enabled: true,
198 max_changelog_events: None,
199 compound_depth: 0,
200 seqs: Vec::new(),
201 groups: Vec::new(),
202 next_seq: 0,
203 group_stack: Vec::new(),
204 next_group_id: 1,
205 current_meta: ChangeEventMeta::default(),
206 }
207 }
208
209 pub fn with_max_changelog_events(max: usize) -> Self {
210 let mut out = Self::new();
211 out.max_changelog_events = Some(max);
212 out
213 }
214
215 pub fn set_max_changelog_events(&mut self, max: Option<usize>) {
216 self.max_changelog_events = max;
217 self.enforce_cap();
218 }
219
220 fn enforce_cap(&mut self) {
221 let Some(max) = self.max_changelog_events else {
222 return;
223 };
224 if max == 0 {
225 self.clear();
226 return;
227 }
228 if self.events.len() <= max {
229 return;
230 }
231 let drop_n = self.events.len() - max;
232 self.events.drain(0..drop_n);
233 self.metas.drain(0..drop_n);
234 self.seqs.drain(0..drop_n);
235 self.groups.drain(0..drop_n);
236 }
237
238 pub fn record(&mut self, event: ChangeEvent) {
239 if self.enabled {
240 let seq = self.next_seq;
241 self.next_seq += 1;
242 let current_group = self.group_stack.last().copied();
243 self.events.push(event);
244 self.metas.push(self.current_meta.clone());
245 self.seqs.push(seq);
246 self.groups.push(current_group);
247 self.enforce_cap();
248 }
249 }
250
251 pub fn record_with_meta(&mut self, event: ChangeEvent, meta: ChangeEventMeta) {
253 if self.enabled {
254 let seq = self.next_seq;
255 self.next_seq += 1;
256 let current_group = self.group_stack.last().copied();
257 self.events.push(event);
258 self.metas.push(meta);
259 self.seqs.push(seq);
260 self.groups.push(current_group);
261 self.enforce_cap();
262 }
263 }
264
265 pub fn begin_compound(&mut self, description: String) {
267 self.compound_depth += 1;
268 if self.compound_depth == 1 {
269 let gid = self.next_group_id;
271 self.next_group_id += 1;
272 self.group_stack.push(gid);
273 } else {
274 if let Some(&gid) = self.group_stack.last() {
276 self.group_stack.push(gid);
277 }
278 }
279 if self.enabled {
280 self.record(ChangeEvent::CompoundStart {
281 description,
282 depth: self.compound_depth,
283 });
284 }
285 }
286
287 pub fn end_compound(&mut self) {
289 if self.compound_depth > 0 {
290 if self.enabled {
291 self.record(ChangeEvent::CompoundEnd {
292 depth: self.compound_depth,
293 });
294 }
295 self.compound_depth -= 1;
296 self.group_stack.pop();
297 }
298 }
299
300 pub fn events(&self) -> &[ChangeEvent] {
301 &self.events
302 }
303
304 pub fn event_meta(&self, index: usize) -> Option<&ChangeEventMeta> {
305 self.metas.get(index)
306 }
307
308 pub fn set_actor_id(&mut self, actor_id: Option<String>) {
309 self.current_meta.actor_id = actor_id;
310 }
311
312 pub fn set_correlation_id(&mut self, correlation_id: Option<String>) {
313 self.current_meta.correlation_id = correlation_id;
314 }
315
316 pub fn set_reason(&mut self, reason: Option<String>) {
317 self.current_meta.reason = reason;
318 }
319
320 pub fn truncate(&mut self, len: usize) {
322 self.events.truncate(len);
323 self.metas.truncate(len);
324 self.seqs.truncate(len);
325 self.groups.truncate(len);
326 }
327
328 pub fn clear(&mut self) {
329 self.events.clear();
330 self.metas.clear();
331 self.seqs.clear();
332 self.groups.clear();
333 self.compound_depth = 0;
334 self.group_stack.clear();
335 }
336
337 pub fn len(&self) -> usize {
338 self.events.len()
339 }
340
341 pub fn is_empty(&self) -> bool {
342 self.events.is_empty()
343 }
344
345 pub fn take_from(&mut self, index: usize) -> Vec<ChangeEvent> {
347 let events = self.events.split_off(index);
348 let _ = self.metas.split_off(index);
349 let _ = self.seqs.split_off(index);
350 let _ = self.groups.split_off(index);
351 events
352 }
353
354 pub fn set_enabled(&mut self, enabled: bool) {
356 self.enabled = enabled;
357 }
358
359 pub fn compound_depth(&self) -> usize {
361 self.compound_depth
362 }
363
364 pub fn meta(&self, index: usize) -> Option<(u64, Option<u64>)> {
366 self.seqs
367 .get(index)
368 .copied()
369 .zip(self.groups.get(index).copied())
370 }
371
372 pub fn last_group_indices(&self) -> Vec<usize> {
374 if let Some(&last_gid) = self.groups.iter().rev().flatten().next() {
375 let idxs: Vec<usize> = self
376 .groups
377 .iter()
378 .enumerate()
379 .filter_map(|(i, g)| if *g == Some(last_gid) { Some(i) } else { None })
380 .collect();
381 if !idxs.is_empty() {
382 return idxs;
383 }
384 }
385 self.events.len().checked_sub(1).into_iter().collect()
386 }
387}
388
389pub trait ChangeLogger {
391 fn record(&mut self, event: ChangeEvent);
392 fn set_enabled(&mut self, enabled: bool);
393 fn begin_compound(&mut self, description: String);
394 fn end_compound(&mut self);
395}
396
397impl ChangeLogger for ChangeLog {
398 fn record(&mut self, event: ChangeEvent) {
399 ChangeLog::record(self, event);
400 }
401
402 fn set_enabled(&mut self, enabled: bool) {
403 self.enabled = enabled;
404 }
405
406 fn begin_compound(&mut self, description: String) {
407 ChangeLog::begin_compound(self, description);
408 }
409
410 fn end_compound(&mut self) {
411 ChangeLog::end_compound(self);
412 }
413}
414
415pub struct NullChangeLogger;
417
418impl ChangeLogger for NullChangeLogger {
419 fn record(&mut self, _: ChangeEvent) {}
420 fn set_enabled(&mut self, _: bool) {}
421 fn begin_compound(&mut self, _: String) {}
422 fn end_compound(&mut self) {}
423}