Skip to main content

formualizer_eval/engine/
eval_delta.rs

1use formualizer_common::{PackedSheetCell, SheetId};
2
3/// Opt-in control for evaluation delta collection.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum DeltaMode {
6    /// Do not collect deltas (default).
7    #[default]
8    Off,
9    /// Collect changed grid cell addresses (no values).
10    Cells,
11}
12
13/// Engine-level evaluation deltas for a single evaluation pass.
14#[derive(Debug, Clone, Default, PartialEq, Eq)]
15pub struct EvalDelta {
16    pub changed_cells: Vec<PackedSheetCell>,
17}
18
19impl EvalDelta {
20    pub fn is_empty(&self) -> bool {
21        self.changed_cells.is_empty()
22    }
23}
24
25pub(crate) struct DeltaCollector {
26    pub(crate) mode: DeltaMode,
27    changed_cells: Vec<PackedSheetCell>,
28}
29
30impl DeltaCollector {
31    pub(crate) fn new(mode: DeltaMode) -> Self {
32        Self {
33            mode,
34            changed_cells: Vec::new(),
35        }
36    }
37
38    #[inline]
39    pub(crate) fn record_cell(&mut self, sheet_id: SheetId, row0: u32, col0: u32) {
40        if self.mode == DeltaMode::Off {
41            return;
42        }
43        if let Some(packed) = PackedSheetCell::try_new(sheet_id, row0, col0) {
44            self.changed_cells.push(packed);
45        }
46    }
47
48    #[inline]
49    pub(crate) fn record_packed(&mut self, packed: PackedSheetCell) {
50        if self.mode == DeltaMode::Off {
51            return;
52        }
53        self.changed_cells.push(packed);
54    }
55
56    pub(crate) fn finish(mut self) -> EvalDelta {
57        if self.mode == DeltaMode::Off || self.changed_cells.is_empty() {
58            return EvalDelta::default();
59        }
60        self.changed_cells.sort_unstable();
61        self.changed_cells.dedup();
62        EvalDelta {
63            changed_cells: self.changed_cells,
64        }
65    }
66}