1use crate::formula::deps::{mark_dirty, recalculate, set_formula, DepGraph};
2use crate::model::{CellPos, Sheet};
3
4pub struct SheetEngine<'a> {
10 sheet: &'a mut Sheet,
11 deps: &'a mut DepGraph,
12}
13
14impl<'a> SheetEngine<'a> {
15 pub fn new(sheet: &'a mut Sheet, deps: &'a mut DepGraph) -> Self {
16 Self { sheet, deps }
17 }
18
19 pub fn set_cell_raw(&mut self, pos: CellPos, raw: &str) {
20 if raw.starts_with('=') {
21 set_formula(self.sheet, self.deps, pos, raw);
22 } else {
23 self.sheet.set_cell(pos, raw);
24 self.deps.remove(pos);
25 }
26 mark_dirty(self.sheet, self.deps, pos);
27 }
28
29 pub fn clear_cell(&mut self, pos: CellPos) {
30 self.sheet.clear_cell(pos);
31 self.deps.remove(pos);
32 mark_dirty(self.sheet, self.deps, pos);
33 }
34
35 pub fn write_cell_raw(&mut self, pos: CellPos, raw: &str) {
36 if raw.is_empty() {
37 self.clear_cell(pos);
38 } else {
39 self.set_cell_raw(pos, raw);
40 }
41 }
42
43 pub fn write_cell_raw_and_recalculate(&mut self, pos: CellPos, raw: &str) {
44 self.write_cell_raw(pos, raw);
45 self.recalculate();
46 }
47
48 pub fn set_cell_raw_and_recalculate(&mut self, pos: CellPos, raw: &str) {
49 self.set_cell_raw(pos, raw);
50 self.recalculate();
51 }
52
53 pub fn recalculate(&mut self) {
54 recalculate(self.sheet, self.deps);
55 }
56
57 pub fn sort_by_column_and_recalculate(&mut self, col: usize, ascending: bool) {
58 self.sheet.sort_by_column(col, ascending);
59 self.rebuild_formulas_and_recalculate();
60 }
61
62 pub fn rebuild_formulas_and_recalculate(&mut self) {
63 *self.deps = DepGraph::new();
64 let formula_cells: Vec<_> = self
65 .sheet
66 .cells
67 .iter()
68 .filter(|(_, cell)| cell.raw.starts_with('='))
69 .map(|(pos, cell)| (*pos, cell.raw.clone()))
70 .collect();
71 for (pos, raw) in formula_cells {
72 set_formula(self.sheet, self.deps, pos, &raw);
73 }
74 self.recalculate();
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::model::CellValue;
82
83 #[test]
84 fn write_value_updates_dependents() {
85 let mut sheet = Sheet::new();
86 let mut deps = DepGraph::new();
87 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw((0, 0), "10");
88 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 1), "=A1+5");
89
90 assert_eq!(
91 sheet.get_cell((0, 1)).unwrap().value,
92 CellValue::Number(15.0)
93 );
94
95 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 0), "20");
96
97 assert_eq!(
98 sheet.get_cell((0, 1)).unwrap().value,
99 CellValue::Number(25.0)
100 );
101 }
102
103 #[test]
104 fn rewriting_formula_as_value_drops_edges() {
105 let mut sheet = Sheet::new();
106 let mut deps = DepGraph::new();
107 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw((0, 0), "10");
108 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 1), "=A1+5");
109
110 assert!(deps.dependencies.contains_key(&(0, 1)));
111
112 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 1), "plain");
113
114 assert!(!deps.dependencies.contains_key(&(0, 1)));
115 assert!(!deps
116 .dependents
117 .get(&(0, 0))
118 .map(|set| set.contains(&(0, 1)))
119 .unwrap_or(false));
120 }
121
122 #[test]
123 fn set_empty_cell_preserves_extent() {
124 let mut sheet = Sheet::new();
125 let mut deps = DepGraph::new();
126
127 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw((1, 1), "");
128
129 assert_eq!(sheet.row_count, 2);
130 assert_eq!(sheet.col_count, 2);
131 assert!(sheet.get_cell((1, 1)).is_some());
132 }
133
134 #[test]
135 fn clear_cell_removes_cell_and_updates_dependents() {
136 let mut sheet = Sheet::new();
137 let mut deps = DepGraph::new();
138 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw((0, 0), "10");
139 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 1), "=A1+5");
140
141 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 0), "");
142
143 assert!(sheet.get_cell((0, 0)).is_none());
144 assert_eq!(
145 sheet.get_cell((0, 1)).unwrap().value,
146 CellValue::Number(5.0)
147 );
148 }
149
150 #[test]
151 fn sort_rebuilds_formula_graph() {
152 let mut sheet = Sheet::new();
153 let mut deps = DepGraph::new();
154 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw((0, 0), "2");
155 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw((1, 0), "1");
156 SheetEngine::new(&mut sheet, &mut deps).write_cell_raw_and_recalculate((0, 1), "=A1*10");
157
158 SheetEngine::new(&mut sheet, &mut deps).sort_by_column_and_recalculate(0, true);
159 SheetEngine::new(&mut sheet, &mut deps).set_cell_raw_and_recalculate((0, 0), "3");
160
161 assert_eq!(
162 sheet.get_cell((1, 1)).unwrap().value,
163 CellValue::Number(30.0)
164 );
165 }
166
167 #[test]
168 fn rebuilds_formula_graph_for_loaded_sheet() {
169 let mut sheet = Sheet::new();
170 sheet.set_cell((0, 0), "10");
171 sheet.set_cell((0, 1), "=A1+5");
172 let mut deps = DepGraph::new();
173
174 SheetEngine::new(&mut sheet, &mut deps).rebuild_formulas_and_recalculate();
175
176 assert!(deps.dependencies.contains_key(&(0, 1)));
177 assert_eq!(
178 sheet.get_cell((0, 1)).unwrap().value,
179 CellValue::Number(15.0)
180 );
181 }
182}