Skip to main content

formualizer_workbook/
transaction.rs

1use crate::traits::{CellData, SpreadsheetWriter};
2use std::collections::BTreeMap;
3
4/// Write operation journal entry.
5#[derive(Clone, Debug)]
6pub enum WriteOp {
7    Cell {
8        sheet: String,
9        row: u32,
10        col: u32,
11        data: CellData,
12    },
13    Range {
14        sheet: String,
15        cells: BTreeMap<(u32, u32), CellData>,
16    },
17    Clear {
18        sheet: String,
19        start: (u32, u32),
20        end: (u32, u32),
21    },
22    CreateSheet {
23        name: String,
24    },
25    DeleteSheet {
26        name: String,
27    },
28    RenameSheet {
29        old: String,
30        new: String,
31    },
32}
33
34/// Transaction wrapper applying a set of write operations atomically to an underlying writer.
35/// Atomicity guarantee is best-effort: either all operations are applied in insertion order
36/// or none (if dropped without commit). Durability depends on backend flush/save semantics.
37pub struct WriteTransaction<'a, W: SpreadsheetWriter> {
38    writer: &'a mut W,
39    operations: Vec<WriteOp>,
40    committed: bool,
41    validated: bool,
42}
43
44impl<'a, W: SpreadsheetWriter> WriteTransaction<'a, W> {
45    pub fn new(writer: &'a mut W) -> Self {
46        Self {
47            writer,
48            operations: Vec::new(),
49            committed: false,
50            validated: false,
51        }
52    }
53
54    pub fn write_cell(&mut self, sheet: &str, row: u32, col: u32, data: CellData) -> &mut Self {
55        self.operations.push(WriteOp::Cell {
56            sheet: sheet.to_string(),
57            row,
58            col,
59            data,
60        });
61        self
62    }
63
64    pub fn write_range(&mut self, sheet: &str, cells: BTreeMap<(u32, u32), CellData>) -> &mut Self {
65        self.operations.push(WriteOp::Range {
66            sheet: sheet.to_string(),
67            cells,
68        });
69        self
70    }
71
72    pub fn clear_range(&mut self, sheet: &str, start: (u32, u32), end: (u32, u32)) -> &mut Self {
73        self.operations.push(WriteOp::Clear {
74            sheet: sheet.to_string(),
75            start,
76            end,
77        });
78        self
79    }
80
81    pub fn create_sheet(&mut self, name: &str) -> &mut Self {
82        self.operations.push(WriteOp::CreateSheet {
83            name: name.to_string(),
84        });
85        self
86    }
87
88    pub fn delete_sheet(&mut self, name: &str) -> &mut Self {
89        self.operations.push(WriteOp::DeleteSheet {
90            name: name.to_string(),
91        });
92        self
93    }
94
95    pub fn rename_sheet(&mut self, old: &str, new: &str) -> &mut Self {
96        self.operations.push(WriteOp::RenameSheet {
97            old: old.to_string(),
98            new: new.to_string(),
99        });
100        self
101    }
102
103    /// Validate operations for basic invariants (order-sensitive checks can be added later).
104    pub fn validate(&mut self) -> Result<(), W::Error> {
105        self.validated = true; // placeholder for future validation logic
106        Ok(())
107    }
108
109    /// Apply all operations; calls flush() at end for durability. Consumes self.
110    pub fn commit(mut self) -> Result<(), W::Error> {
111        if self.committed {
112            return Ok(());
113        }
114        if !self.validated {
115            self.validate()?;
116        }
117        for op in &self.operations {
118            match op {
119                WriteOp::Cell {
120                    sheet,
121                    row,
122                    col,
123                    data,
124                } => {
125                    self.writer.write_cell(sheet, *row, *col, data.clone())?;
126                }
127                WriteOp::Range { sheet, cells } => {
128                    self.writer.write_range(sheet, cells.clone())?;
129                }
130                WriteOp::Clear { sheet, start, end } => {
131                    self.writer.clear_range(sheet, *start, *end)?;
132                }
133                WriteOp::CreateSheet { name } => {
134                    self.writer.create_sheet(name)?;
135                }
136                WriteOp::DeleteSheet { name } => {
137                    self.writer.delete_sheet(name)?;
138                }
139                WriteOp::RenameSheet { old, new } => {
140                    self.writer.rename_sheet(old, new)?;
141                }
142            }
143        }
144        // Attempt durability operations; propagate flush error if occurs
145        self.writer.flush()?;
146        // save may be a heavier operation; ignore error? propagate for now
147        self.writer.save()?;
148        self.committed = true;
149        Ok(())
150    }
151
152    /// Explicit rollback just marks as committed (journal dropped without applying).
153    pub fn rollback(mut self) {
154        self.committed = true;
155    }
156
157    pub fn operations(&self) -> &[WriteOp] {
158        &self.operations
159    }
160}
161
162impl<'a, W: SpreadsheetWriter> Drop for WriteTransaction<'a, W> {
163    fn drop(&mut self) {
164        // If not committed, journal is discarded (rollback semantics)
165    }
166}