1use crate::SheetId;
9use crate::engine::addr::GridAddr;
10use crate::engine::named_range::{NameScope, NamedDefinition};
11use crate::engine::row_visibility::RowVisibilitySource;
12use crate::engine::vertex::VertexId;
13use crate::reference::CellRef;
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: GridAddr,
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<GridAddr>,
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: GridAddr,
96 new_coord: GridAddr,
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, Clone, PartialEq)]
177pub(crate) struct FormulaRunAdjusted {
178 pub(crate) first: VertexId,
179 pub(crate) len: u32,
180 pub(crate) sheet_id: SheetId,
181 pub(crate) col: u32,
182 pub(crate) row0: u32,
183 pub(crate) old_first: ASTNode,
184 pub(crate) new_first: ASTNode,
185}
186
187impl FormulaRunAdjusted {
188 #[inline]
189 pub(crate) fn len(&self) -> usize {
190 self.len as usize
191 }
192
193 pub(crate) fn event(&self, i: u32) -> ChangeEvent {
195 let relocate = |ast: &ASTNode| {
196 if i == 0 {
197 return ast.clone();
198 }
199 crate::engine::template::relocate::instantiate_member_ast(ast, i64::from(i), 0)
202 .expect("a run member's formula relocates")
203 };
204 ChangeEvent::FormulaAdjusted {
205 id: VertexId(self.first.0 + i),
206 addr: Some(CellRef::new(
207 self.sheet_id,
208 crate::reference::Coord::new(self.row0 + i, self.col, true, true),
209 )),
210 old_ast: relocate(&self.old_first),
211 new_ast: relocate(&self.new_first),
212 }
213 }
214
215 pub(crate) fn events(&self) -> impl Iterator<Item = ChangeEvent> + '_ {
217 (0..self.len).map(|i| self.event(i))
218 }
219}
220
221#[derive(Debug)]
225struct LazyEntry {
226 pos: usize,
227 seq0: u64,
228 group: Option<u64>,
229 meta: ChangeEventMeta,
230 run: std::sync::Arc<FormulaRunAdjusted>,
231}
232
233#[derive(Debug, Default)]
239struct Retained {
240 events: Vec<ChangeEvent>,
241 metas: Vec<ChangeEventMeta>,
242 seqs: Vec<u64>,
243 groups: Vec<Option<u64>>,
244 lazy: Vec<LazyEntry>,
245}
246
247impl Retained {
248 fn expand(&mut self) -> usize {
253 let Some(first) = self.lazy.first() else {
254 return 0;
255 };
256 let start = first.pos;
257 let tail_events = self.events.split_off(start);
258 let tail_metas = self.metas.split_off(start);
259 let tail_seqs = self.seqs.split_off(start);
260 let tail_groups = self.groups.split_off(start);
261 let tail_len = tail_events.len();
262 let added: usize = self.lazy.iter().map(|e| e.run.len()).sum();
263 self.events.reserve(tail_len + added);
264 self.metas.reserve(tail_len + added);
265 self.seqs.reserve(tail_len + added);
266 self.groups.reserve(tail_len + added);
267 let mut tail = tail_events
268 .into_iter()
269 .zip(tail_metas)
270 .zip(tail_seqs.into_iter().zip(tail_groups));
271 let mut p = start;
272 for entry in std::mem::take(&mut self.lazy) {
273 while p < entry.pos {
274 let ((event, meta), (seq, group)) = tail.next().expect("record position in range");
275 self.events.push(event);
276 self.metas.push(meta);
277 self.seqs.push(seq);
278 self.groups.push(group);
279 p += 1;
280 }
281 for (i, event) in entry.run.events().enumerate() {
282 self.events.push(event);
283 self.metas.push(entry.meta.clone());
284 self.seqs.push(entry.seq0 + i as u64);
285 self.groups.push(entry.group);
286 }
287 }
288 for ((event, meta), (seq, group)) in tail {
289 self.events.push(event);
290 self.metas.push(meta);
291 self.seqs.push(seq);
292 self.groups.push(group);
293 }
294 tail_len + added
295 }
296}
297
298struct RetainedCell {
309 inner: std::cell::UnsafeCell<Retained>,
310 expanded: std::sync::OnceLock<()>,
311 len: usize,
313 pending_records: usize,
315 #[cfg(test)]
318 work: std::sync::atomic::AtomicUsize,
319}
320
321unsafe impl Sync for RetainedCell {}
330
331const _: () = {
332 const fn assert_send_sync<T: Send + Sync>() {}
333 assert_send_sync::<Retained>();
334};
335
336impl Default for RetainedCell {
337 fn default() -> Self {
338 Self {
339 inner: std::cell::UnsafeCell::new(Retained::default()),
340 expanded: std::sync::OnceLock::from(()),
341 len: 0,
342 pending_records: 0,
343 #[cfg(test)]
344 work: std::sync::atomic::AtomicUsize::new(0),
345 }
346 }
347}
348
349impl std::fmt::Debug for RetainedCell {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 f.debug_struct("RetainedCell")
352 .field("len", &self.len)
353 .field("expanded", &self.expanded.get().is_some())
354 .finish()
355 }
356}
357
358impl RetainedCell {
359 #[inline]
360 fn note_work(&self, _work: usize) {
361 #[cfg(test)]
362 self.work
363 .fetch_add(_work, std::sync::atomic::Ordering::Relaxed);
364 }
365
366 fn get(&self) -> &Retained {
368 self.expanded.get_or_init(|| {
369 let inner = unsafe { &mut *self.inner.get() };
372 self.note_work(inner.expand());
373 });
374 unsafe { &*self.inner.get() }
376 }
377
378 fn settle(&mut self) -> &mut Retained {
380 if self.expanded.get().is_none() {
381 let work = self.inner.get_mut().expand();
382 self.note_work(work);
383 self.expanded = std::sync::OnceLock::from(());
384 self.pending_records = 0;
385 }
386 self.inner.get_mut()
387 }
388
389 fn plain_len(&mut self) -> usize {
391 self.inner.get_mut().events.len()
392 }
393
394 fn push(&mut self, event: ChangeEvent, meta: ChangeEventMeta, seq: u64, group: Option<u64>) {
395 let r = self.inner.get_mut();
396 r.events.push(event);
397 r.metas.push(meta);
398 r.seqs.push(seq);
399 r.groups.push(group);
400 self.len += 1;
401 }
402
403 fn push_run(&mut self, entry: LazyEntry) {
404 self.len += entry.run.len();
405 self.inner.get_mut().lazy.push(entry);
406 self.pending_records += 1;
407 self.expanded = std::sync::OnceLock::new();
408 }
409
410 fn drain_front(&mut self, n: usize) {
411 let r = self.settle();
412 r.events.drain(0..n);
413 r.metas.drain(0..n);
414 r.seqs.drain(0..n);
415 r.groups.drain(0..n);
416 self.len = r.events.len();
417 }
418
419 fn truncate(&mut self, len: usize) {
420 let r = self.settle();
421 r.events.truncate(len);
422 r.metas.truncate(len);
423 r.seqs.truncate(len);
424 r.groups.truncate(len);
425 self.len = r.events.len();
426 }
427
428 fn split_off(&mut self, index: usize) -> Vec<ChangeEvent> {
429 let r = self.settle();
430 let events = r.events.split_off(index);
431 let _ = r.metas.split_off(index);
432 let _ = r.seqs.split_off(index);
433 let _ = r.groups.split_off(index);
434 self.len = r.events.len();
435 events
436 }
437
438 fn clear(&mut self) {
439 *self = Self {
440 #[cfg(test)]
441 work: std::sync::atomic::AtomicUsize::new(
442 self.work.load(std::sync::atomic::Ordering::Relaxed),
443 ),
444 ..Self::default()
445 };
446 }
447}
448
449#[derive(Debug, Default)]
451pub struct ChangeLog {
452 enabled: bool,
453 max_changelog_events: Option<usize>,
455 compound_depth: usize,
457 next_seq: u64,
458 group_stack: Vec<u64>,
460 next_group_id: u64,
461
462 current_meta: ChangeEventMeta,
463
464 retained: RetainedCell,
469}
470
471#[derive(Debug)]
476pub(crate) struct MutationCapture {
477 events: Vec<ChangeEvent>,
478 lazy: Vec<(usize, std::sync::Arc<FormulaRunAdjusted>)>,
481 compound_depth: usize,
482 current_meta: ChangeEventMeta,
483}
484
485impl MutationCapture {
486 pub(crate) fn new(current_meta: ChangeEventMeta) -> Self {
487 Self {
488 events: Vec::new(),
489 lazy: Vec::new(),
490 compound_depth: 0,
491 current_meta,
492 }
493 }
494
495 pub(crate) fn len(&self) -> usize {
498 self.events.len()
499 }
500
501 pub(crate) fn events(&self) -> &[ChangeEvent] {
506 &self.events
507 }
508
509 pub(crate) fn lazy_len(&self) -> usize {
511 self.lazy.len()
512 }
513
514 pub(crate) fn record_lazy(&mut self, run: FormulaRunAdjusted) {
516 if run.len == 0 {
517 return;
518 }
519 self.lazy
520 .push((self.events.len(), std::sync::Arc::new(run)));
521 }
522
523 pub(crate) fn expanded_events_from(&self, start: usize, lazy_start: usize) -> Vec<ChangeEvent> {
526 let lazy = &self.lazy[lazy_start.min(self.lazy.len())..];
527 let mut out = Vec::with_capacity(
528 self.events.len().saturating_sub(start)
529 + lazy.iter().map(|(_, r)| r.len()).sum::<usize>(),
530 );
531 let mut k = 0;
532 for p in start..=self.events.len() {
533 while k < lazy.len() && lazy[k].0 <= p {
534 out.extend(lazy[k].1.events());
535 k += 1;
536 }
537 if let Some(e) = self.events.get(p) {
538 out.push(e.clone());
539 }
540 }
541 out
542 }
543
544 pub(crate) fn close_compounds(&mut self) {
545 while self.compound_depth > 0 {
546 self.end_compound();
547 }
548 }
549
550 fn push(&mut self, event: ChangeEvent) {
551 self.events.push(event);
552 }
553}
554
555pub(crate) fn compound_start_description<'a>(
560 end: usize,
561 event: impl Fn(usize) -> &'a ChangeEvent,
562) -> Option<&'a str> {
563 if !matches!(event(end), ChangeEvent::CompoundEnd { .. }) {
564 return None;
565 }
566 let mut open = 1usize;
567 for i in (0..end).rev() {
568 match event(i) {
569 ChangeEvent::CompoundEnd { .. } => open += 1,
570 ChangeEvent::CompoundStart { description, .. } => {
571 open -= 1;
572 if open == 0 {
573 return Some(description.as_str());
574 }
575 }
576 _ => {}
577 }
578 }
579 None
580}
581
582impl ChangeLogger for MutationCapture {
583 fn record(&mut self, event: ChangeEvent) {
584 self.push(event);
585 }
586
587 fn set_enabled(&mut self, _: bool) {}
588
589 fn begin_compound(&mut self, description: String) {
590 self.compound_depth += 1;
591 self.push(ChangeEvent::CompoundStart {
592 description,
593 depth: self.compound_depth,
594 });
595 }
596
597 fn end_compound(&mut self) {
598 if self.compound_depth == 0 {
599 return;
600 }
601 self.push(ChangeEvent::CompoundEnd {
602 depth: self.compound_depth,
603 });
604 self.compound_depth -= 1;
605 }
606}
607
608impl ChangeLog {
609 pub fn new() -> Self {
610 Self {
611 enabled: true,
612 max_changelog_events: None,
613 compound_depth: 0,
614 next_seq: 0,
615 group_stack: Vec::new(),
616 next_group_id: 1,
617 current_meta: ChangeEventMeta::default(),
618 retained: RetainedCell::default(),
619 }
620 }
621
622 pub fn with_max_changelog_events(max: usize) -> Self {
623 let mut out = Self::new();
624 out.max_changelog_events = Some(max);
625 out
626 }
627
628 pub fn set_max_changelog_events(&mut self, max: Option<usize>) {
629 self.max_changelog_events = max;
630 self.enforce_cap();
631 }
632
633 fn enforce_cap(&mut self) {
634 let Some(max) = self.max_changelog_events else {
635 return;
636 };
637 if max == 0 {
638 self.retained.clear();
639 return;
640 }
641 if self.len() <= max {
642 return;
643 }
644 let drop_n = self.len() - max;
645 self.retained.drain_front(drop_n);
646 }
647
648 fn replay_lazy(
649 &mut self,
650 run: std::sync::Arc<FormulaRunAdjusted>,
651 meta: &ChangeEventMeta,
652 retain: bool,
653 ) {
654 if !self.enabled {
655 return;
656 }
657 let seq0 = self.next_seq;
658 self.next_seq += run.len as u64;
659 if retain {
660 let entry = LazyEntry {
661 pos: self.retained.plain_len(),
662 seq0,
663 group: self.group_stack.last().copied(),
664 meta: meta.clone(),
665 run,
666 };
667 self.retained.push_run(entry);
668 }
669 }
670
671 fn replay_record(&mut self, event: ChangeEvent, meta: &ChangeEventMeta, retain: bool) {
672 if !self.enabled {
673 return;
674 }
675 let seq = self.next_seq;
676 self.next_seq += 1;
677 if retain {
678 let group = self.group_stack.last().copied();
679 self.retained.push(event, meta.clone(), seq, group);
680 }
681 }
682
683 fn replay_begin_compound(&mut self, description: String, meta: &ChangeEventMeta, retain: bool) {
684 self.compound_depth += 1;
685 if self.compound_depth == 1 {
686 let gid = self.next_group_id;
687 self.next_group_id += 1;
688 self.group_stack.push(gid);
689 } else if let Some(&gid) = self.group_stack.last() {
690 self.group_stack.push(gid);
691 }
692 self.replay_record(
693 ChangeEvent::CompoundStart {
694 description,
695 depth: self.compound_depth,
696 },
697 meta,
698 retain,
699 );
700 }
701
702 fn replay_end_compound(&mut self, meta: &ChangeEventMeta, retain: bool) {
703 if self.compound_depth == 0 {
704 return;
705 }
706 self.replay_record(
707 ChangeEvent::CompoundEnd {
708 depth: self.compound_depth,
709 },
710 meta,
711 retain,
712 );
713 self.compound_depth -= 1;
714 self.group_stack.pop();
715 }
716
717 fn replay_capture(&mut self, capture: MutationCapture, retain: bool) {
718 let mut lazy = capture.lazy.into_iter().peekable();
719 for (p, event) in capture.events.into_iter().enumerate() {
720 while let Some((_, run)) = lazy.next_if(|(pos, _)| *pos <= p) {
721 self.replay_lazy(run, &capture.current_meta, retain);
722 }
723 match event {
724 ChangeEvent::CompoundStart { description, .. } => {
725 self.replay_begin_compound(description, &capture.current_meta, retain);
726 }
727 ChangeEvent::CompoundEnd { .. } => {
728 self.replay_end_compound(&capture.current_meta, retain);
729 }
730 event => self.replay_record(event, &capture.current_meta, retain),
731 }
732 }
733 for (_, run) in lazy {
734 self.replay_lazy(run, &capture.current_meta, retain);
735 }
736 if retain {
737 self.enforce_cap();
738 }
739 }
740
741 pub(crate) fn current_meta(&self) -> ChangeEventMeta {
742 self.current_meta.clone()
743 }
744
745 pub(crate) fn publish_capture(&mut self, capture: MutationCapture) {
746 self.replay_capture(capture, true);
747 }
748
749 pub(crate) fn discard_capture(&mut self, capture: MutationCapture) {
750 self.replay_capture(capture, false);
751 }
752
753 pub fn record(&mut self, event: ChangeEvent) {
754 if self.enabled {
755 let seq = self.next_seq;
756 self.next_seq += 1;
757 let current_group = self.group_stack.last().copied();
758 self.retained
759 .push(event, self.current_meta.clone(), seq, current_group);
760 self.enforce_cap();
761 }
762 }
763
764 pub fn record_with_meta(&mut self, event: ChangeEvent, meta: ChangeEventMeta) {
766 if self.enabled {
767 let seq = self.next_seq;
768 self.next_seq += 1;
769 let current_group = self.group_stack.last().copied();
770 self.retained.push(event, meta, seq, current_group);
771 self.enforce_cap();
772 }
773 }
774
775 pub fn begin_compound(&mut self, description: String) {
777 self.compound_depth += 1;
778 if self.compound_depth == 1 {
779 let gid = self.next_group_id;
781 self.next_group_id += 1;
782 self.group_stack.push(gid);
783 } else {
784 if let Some(&gid) = self.group_stack.last() {
786 self.group_stack.push(gid);
787 }
788 }
789 if self.enabled {
790 self.record(ChangeEvent::CompoundStart {
791 description,
792 depth: self.compound_depth,
793 });
794 }
795 }
796
797 pub fn end_compound(&mut self) {
799 if self.compound_depth > 0 {
800 if self.enabled {
801 self.record(ChangeEvent::CompoundEnd {
802 depth: self.compound_depth,
803 });
804 }
805 self.compound_depth -= 1;
806 self.group_stack.pop();
807 }
808 }
809
810 #[cfg(test)]
812 pub(crate) fn unexpanded_run_records(&self) -> usize {
813 if self.retained.expanded.get().is_some() {
814 0
815 } else {
816 self.retained.pending_records
817 }
818 }
819
820 #[cfg(test)]
824 pub(crate) fn expansion_work(&self) -> usize {
825 self.retained
826 .work
827 .load(std::sync::atomic::Ordering::Relaxed)
828 }
829
830 pub fn events(&self) -> &[ChangeEvent] {
831 &self.retained.get().events
832 }
833
834 pub fn event_meta(&self, index: usize) -> Option<&ChangeEventMeta> {
835 self.retained.get().metas.get(index)
836 }
837
838 pub fn set_actor_id(&mut self, actor_id: Option<String>) {
839 self.current_meta.actor_id = actor_id;
840 }
841
842 pub fn set_correlation_id(&mut self, correlation_id: Option<String>) {
843 self.current_meta.correlation_id = correlation_id;
844 }
845
846 pub fn set_reason(&mut self, reason: Option<String>) {
847 self.current_meta.reason = reason;
848 }
849
850 pub fn truncate(&mut self, len: usize) {
852 self.retained.truncate(len);
853 }
854
855 pub fn clear(&mut self) {
856 self.retained.clear();
857 self.compound_depth = 0;
858 self.group_stack.clear();
859 }
860
861 pub fn len(&self) -> usize {
862 self.retained.len
863 }
864
865 pub fn is_empty(&self) -> bool {
866 self.len() == 0
867 }
868
869 pub fn take_from(&mut self, index: usize) -> Vec<ChangeEvent> {
871 self.retained.split_off(index)
872 }
873
874 pub fn set_enabled(&mut self, enabled: bool) {
876 self.enabled = enabled;
877 }
878
879 pub fn compound_depth(&self) -> usize {
881 self.compound_depth
882 }
883
884 pub fn meta(&self, index: usize) -> Option<(u64, Option<u64>)> {
886 let r = self.retained.get();
887 r.seqs.get(index).copied().zip(r.groups.get(index).copied())
888 }
889
890 pub fn last_group_indices(&self) -> Vec<usize> {
892 let groups = &self.retained.get().groups;
893 if let Some(&last_gid) = groups.iter().rev().flatten().next() {
894 let idxs: Vec<usize> = groups
895 .iter()
896 .enumerate()
897 .filter_map(|(i, g)| if *g == Some(last_gid) { Some(i) } else { None })
898 .collect();
899 if !idxs.is_empty() {
900 return idxs;
901 }
902 }
903 self.len().checked_sub(1).into_iter().collect()
904 }
905}
906
907pub trait ChangeLogger {
909 fn record(&mut self, event: ChangeEvent);
910 fn set_enabled(&mut self, enabled: bool);
911 fn begin_compound(&mut self, description: String);
912 fn end_compound(&mut self);
913}
914
915impl ChangeLogger for ChangeLog {
916 fn record(&mut self, event: ChangeEvent) {
917 ChangeLog::record(self, event);
918 }
919
920 fn set_enabled(&mut self, enabled: bool) {
921 self.enabled = enabled;
922 }
923
924 fn begin_compound(&mut self, description: String) {
925 ChangeLog::begin_compound(self, description);
926 }
927
928 fn end_compound(&mut self) {
929 ChangeLog::end_compound(self);
930 }
931}
932
933pub struct NullChangeLogger;
935
936impl ChangeLogger for NullChangeLogger {
937 fn record(&mut self, _: ChangeEvent) {}
938 fn set_enabled(&mut self, _: bool) {}
939 fn begin_compound(&mut self, _: String) {}
940 fn end_compound(&mut self) {}
941}