Skip to main content

formualizer_eval/engine/
effects.rs

1//! Formal effects pipeline for evaluation (ticket 603).
2//!
3//! An `Effect` is an explicit, inspectable side-effect produced by formula
4//! evaluation.  Effects are *planned* deterministically from
5//! `(computed_value, workbook_state)` and *applied* sequentially.
6//!
7//! This separation enables:
8//! - Parallel computation with sequential apply
9//! - ChangeLog integration at the effect layer
10//! - Deterministic replay without re-evaluation
11
12use crate::engine::vertex::VertexId;
13use crate::reference::CellRef;
14use formualizer_common::LiteralValue;
15use std::sync::{Arc, Mutex};
16
17/// Opaque, one-use read evidence paired with its final planned anchor value.
18/// Only the dynamic-read planner creates these; ordinary and synthetic writes
19/// cannot borrow pending evidence by vertex ID.
20#[doc(hidden)]
21#[derive(Debug)]
22pub struct RecordedResult {
23    vertex: VertexId,
24    value: LiteralValue,
25    pass: Arc<()>,
26    reads: Mutex<Option<Vec<crate::engine::authority::host::ObservedRect>>>,
27}
28
29impl RecordedResult {
30    pub(crate) fn new(
31        vertex: VertexId,
32        value: LiteralValue,
33        pass: Arc<()>,
34        reads: Vec<crate::engine::authority::host::ObservedRect>,
35    ) -> Self {
36        Self {
37            vertex,
38            value,
39            pass,
40            reads: Mutex::new(Some(reads)),
41        }
42    }
43
44    pub(crate) fn vertex(&self) -> VertexId {
45        self.vertex
46    }
47    pub(crate) fn value(&self) -> &LiteralValue {
48        &self.value
49    }
50    pub(crate) fn belongs_to_pass(&self, pass: &Arc<()>) -> bool {
51        Arc::ptr_eq(&self.pass, pass)
52    }
53    pub(crate) fn take_reads(&self) -> Option<Vec<crate::engine::authority::host::ObservedRect>> {
54        self.reads.lock().unwrap().take()
55    }
56}
57
58/// An explicit, inspectable side-effect produced by formula evaluation.
59#[derive(Debug, Clone)]
60pub enum Effect {
61    /// Write a scalar value (or error) to a formula/named vertex.
62    WriteCell {
63        vertex_id: VertexId,
64        value: LiteralValue,
65    },
66    /// Write the designated evaluated result, carrying its own read proof.
67    /// Clones share one-use evidence and cannot change the paired value.
68    #[doc(hidden)]
69    WriteRecordedCell { result: Arc<RecordedResult> },
70    /// Commit a dynamic array spill to the grid.
71    SpillCommit {
72        anchor_vertex: VertexId,
73        anchor_cell: CellRef,
74        target_cells: Vec<CellRef>,
75        values: Vec<Vec<LiteralValue>>,
76    },
77    /// Clear a previous spill region (before resize or scalar downgrade).
78    SpillClear { anchor_vertex: VertexId },
79}
80
81/// A batch of effects from evaluating a single layer.
82/// Each entry pairs a vertex with its planned effects.
83pub type EffectBatch = Vec<(VertexId, Vec<Effect>)>;