formualizer_eval/engine/graph/
sheets.rs1use super::ast_utils::update_internal_sheet_references;
2use super::*;
3use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
4
5impl DependencyGraph {
6 pub fn add_sheet(&mut self, name: &str) -> Result<SheetId, ExcelError> {
11 if let Some(id) = self.sheet_reg.get_id(name) {
12 return Ok(id);
13 }
14
15 let sheet_id = self.sheet_reg.id_for(name);
16 self.sheet_indexes.entry(sheet_id).or_default();
17
18 self.heal_orphaned_formulas(name);
20 Ok(sheet_id)
21 }
22
23 pub fn remove_sheet(&mut self, sheet_id: SheetId) -> Result<(), ExcelError> {
25 let old_name = self.sheet_reg.name(sheet_id).to_string();
26 if old_name.is_empty() {
27 return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Sheet does not exist"));
28 }
29
30 let sheet_count = self.sheet_reg.all_sheets().len();
31 if sheet_count <= 1 {
32 return Err(
33 ExcelError::new(ExcelErrorKind::Value).with_message("Cannot remove the last sheet")
34 );
35 }
36
37 self.begin_batch();
38
39 let vertices_to_delete: Vec<VertexId> = self
43 .grid_vertices_in_sheet(sheet_id)
44 .map(|(id, _)| id)
45 .collect();
46
47 let mut formulas_to_update: rustc_hash::FxHashSet<VertexId> =
50 rustc_hash::FxHashSet::default();
51
52 for &formula_id in self.vertex_formulas.keys() {
53 let deps = self.edges.out_edges(formula_id);
54 if deps
55 .iter()
56 .any(|&dep_id| self.store.sheet_id(dep_id) == sheet_id)
57 {
58 formulas_to_update.insert(formula_id);
59 }
60 }
61
62 for (&formula_id, ranges) in &self.formula_to_range_deps {
63 if ranges.iter().any(|r| match r.sheet {
64 SharedSheetLocator::Id(id) => id == sheet_id,
65 SharedSheetLocator::Name(ref n) => n.as_ref() == old_name,
66 SharedSheetLocator::Current => false,
67 }) {
68 formulas_to_update.insert(formula_id);
69 }
70 }
71
72 let formulas_to_update: Vec<VertexId> = formulas_to_update.into_iter().collect();
73
74 for &formula_id in &formulas_to_update {
75 self.tombstone_registry
76 .add_orphan(old_name.clone(), formula_id);
77 self.rewrite_formula_sheet_to_tombstone(formula_id, &old_name);
78 }
79
80 for formula_id in formulas_to_update {
81 self.mark_as_ref_error(formula_id);
82 }
83
84 let ref_err = LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref));
90 let mut name_vertices_to_update: Vec<VertexId> = Vec::new();
91 let mut dirty_vertices: Vec<VertexId> = Vec::new();
92
93 for nr in self.named_ranges.values_mut() {
94 match &nr.definition {
95 NamedDefinition::Cell(c) if c.sheet_id == sheet_id => {
96 nr.definition = NamedDefinition::Literal(ref_err.clone());
97 name_vertices_to_update.push(nr.vertex);
98 dirty_vertices.push(nr.vertex);
99 dirty_vertices.extend(nr.dependents.iter().copied());
100 }
101 NamedDefinition::Range(r)
102 if r.start.sheet_id == sheet_id || r.end.sheet_id == sheet_id =>
103 {
104 nr.definition = NamedDefinition::Literal(ref_err.clone());
105 name_vertices_to_update.push(nr.vertex);
106 dirty_vertices.push(nr.vertex);
107 dirty_vertices.extend(nr.dependents.iter().copied());
108 }
109 _ => {}
110 }
111 }
112 for nr in self.sheet_named_ranges.values_mut() {
113 match &nr.definition {
114 NamedDefinition::Cell(c) if c.sheet_id == sheet_id => {
115 nr.definition = NamedDefinition::Literal(ref_err.clone());
116 name_vertices_to_update.push(nr.vertex);
117 dirty_vertices.push(nr.vertex);
118 dirty_vertices.extend(nr.dependents.iter().copied());
119 }
120 NamedDefinition::Range(r)
121 if r.start.sheet_id == sheet_id || r.end.sheet_id == sheet_id =>
122 {
123 nr.definition = NamedDefinition::Literal(ref_err.clone());
124 name_vertices_to_update.push(nr.vertex);
125 dirty_vertices.push(nr.vertex);
126 dirty_vertices.extend(nr.dependents.iter().copied());
127 }
128 _ => {}
129 }
130 }
131
132 for vid in name_vertices_to_update {
134 self.update_vertex_value(vid, ref_err.clone());
135 }
136 for vid in dirty_vertices {
137 self.mark_vertex_dirty(vid);
138 }
139
140 for vertex_id in vertices_to_delete {
141 if let Some(cell_ref) = self.get_cell_ref_for_vertex(vertex_id) {
142 self.cell_to_vertex.remove(&cell_ref);
143 }
144
145 self.remove_all_edges(vertex_id);
146
147 if let Some(coord) = self.store.grid_addr(vertex_id)
148 && let Some(index) = self.sheet_indexes.get_mut(&sheet_id)
149 {
150 index.remove_vertex(coord, vertex_id);
151 }
152
153 self.clear_pending_name_references(vertex_id);
154 self.vertex_formulas.remove(&vertex_id);
155 self.vertex_values.remove(&vertex_id);
156
157 self.mark_deleted(vertex_id, true);
158 }
159
160 let sheet_names_to_remove: Vec<(SheetId, String)> = self
161 .sheet_named_ranges
162 .keys()
163 .filter(|(sid, _)| *sid == sheet_id)
164 .cloned()
165 .collect();
166
167 for key in sheet_names_to_remove {
168 if let Some(named_range) = self.sheet_named_ranges.remove(&key) {
169 if !self.config.case_sensitive_names {
170 let normalized = key.1.to_lowercase();
171 self.sheet_named_ranges_lookup
172 .remove(&(sheet_id, normalized));
173 } else {
174 self.sheet_named_ranges_lookup.remove(&key);
175 }
176 self.mark_named_vertex_deleted(&named_range);
177 }
178 }
179
180 self.sheet_indexes.remove(&sheet_id);
181
182 if self.default_sheet_id == sheet_id
183 && let Some(&new_default) = self.sheet_indexes.keys().next()
184 {
185 self.default_sheet_id = new_default;
186 }
187
188 self.sheet_reg.remove(sheet_id)?;
189 self.end_batch();
190
191 Ok(())
192 }
193
194 fn tombstone_marker(sheet_name: &str) -> String {
195 format!("__FZ_MISSING_SHEET__{sheet_name}")
196 }
197
198 fn rewrite_formula_sheet_to_tombstone(&mut self, vertex_id: VertexId, sheet_name: &str) {
199 let Some(ast_id) = self.vertex_formulas.get(&vertex_id).copied() else {
200 return;
201 };
202 let Some(ast) = self.data_store.retrieve_ast(ast_id, &self.sheet_reg) else {
203 return;
204 };
205
206 let marker = Self::tombstone_marker(sheet_name);
207 let mut updated_ast = ast.clone();
208 updated_ast.update_sheet_references(Some(sheet_name), &marker);
209
210 if updated_ast != ast {
211 let updated_ast_id = self.data_store.store_ast(&updated_ast, &self.sheet_reg);
212 self.vertex_formulas.insert(vertex_id, updated_ast_id);
213 }
214 }
215
216 fn heal_orphaned_formulas(&mut self, sheet_name: &str) {
217 let orphans = self.tombstone_registry.take_orphans(sheet_name);
218 let marker = Self::tombstone_marker(sheet_name);
219
220 for vertex_id in orphans {
221 let Some(ast_id) = self.vertex_formulas.get(&vertex_id).copied() else {
222 continue;
223 };
224 let Some(ast) = self.data_store.retrieve_ast(ast_id, &self.sheet_reg) else {
225 continue;
226 };
227
228 if !self.is_ref_error(vertex_id) {
231 continue;
232 }
233
234 let mut updated_ast = ast.clone();
236 updated_ast.update_sheet_references(Some(&marker), sheet_name);
237
238 if updated_ast == ast {
239 continue;
241 }
242
243 let updated_ast_id = self.data_store.store_ast(&updated_ast, &self.sheet_reg);
244 self.vertex_formulas.insert(vertex_id, updated_ast_id);
245 self.rebuild_formula_dependencies(vertex_id, &updated_ast);
246 }
247 }
248 pub fn rename_sheet(&mut self, sheet_id: SheetId, new_name: &str) -> Result<(), ExcelError> {
250 if new_name.is_empty() || new_name.len() > 255 {
251 return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Invalid sheet name"));
252 }
253
254 let old_name = self.sheet_reg.name(sheet_id).to_string();
255
256 if old_name.is_empty() {
257 return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Sheet does not exist"));
258 }
259
260 if let Some(existing_id) = self.sheet_reg.get_id(new_name) {
261 if existing_id != sheet_id {
262 return Err(ExcelError::new(ExcelErrorKind::Value)
263 .with_message(format!("Sheet '{new_name}' already exists")));
264 }
265 return Ok(());
266 }
267
268 self.sheet_reg.rename(sheet_id, new_name)?;
269
270 self.begin_batch();
271
272 self.heal_orphaned_formulas(new_name);
274
275 let formulas_to_update: Vec<VertexId> = self.vertex_formulas.keys().copied().collect();
277 for formula_id in formulas_to_update {
278 if let Some(ast_id) = self.vertex_formulas.get(&formula_id)
279 && let Some(ast) = self.data_store.retrieve_ast(*ast_id, &self.sheet_reg)
280 {
281 let mut updated_ast = ast.clone();
282 updated_ast.update_sheet_references(Some(&old_name), new_name);
283
284 if ast != updated_ast {
285 self.rebuild_formula_dependencies(formula_id, &updated_ast);
286 let updated_ast_id = self.data_store.store_ast(&updated_ast, &self.sheet_reg);
287 self.vertex_formulas.insert(formula_id, updated_ast_id);
288 }
289 }
290 }
291
292 self.end_batch();
293 Ok(())
294 }
295
296 pub fn duplicate_sheet(
298 &mut self,
299 source_sheet_id: SheetId,
300 new_name: &str,
301 ) -> Result<SheetId, ExcelError> {
302 if new_name.is_empty() || new_name.len() > 255 {
303 return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Invalid sheet name"));
304 }
305
306 let source_name = self.sheet_reg.name(source_sheet_id).to_string();
307 if source_name.is_empty() {
308 return Err(
309 ExcelError::new(ExcelErrorKind::Value).with_message("Source sheet does not exist")
310 );
311 }
312
313 if self.sheet_reg.get_id(new_name).is_some() {
314 return Err(ExcelError::new(ExcelErrorKind::Value)
315 .with_message(format!("Sheet '{new_name}' already exists")));
316 }
317
318 let new_sheet_id = self.add_sheet(new_name)?;
319
320 self.begin_batch();
321
322 let source_vertices: Vec<(VertexId, GridAddr)> =
323 self.grid_vertices_in_sheet(source_sheet_id).collect();
324
325 let mut vertex_mapping = FxHashMap::default();
326
327 for (old_id, coord) in &source_vertices {
328 let row = coord.row();
329 let col = coord.col();
330 let kind = self.store.kind(*old_id);
331
332 let new_id = self
333 .store
334 .allocate(VertexAddr::grid(*coord), new_sheet_id, 0x01);
335 self.edges.add_vertex(VertexAddr::grid(*coord), new_id.0);
336 self.sheet_index_mut(new_sheet_id)
337 .add_vertex(*coord, new_id);
338
339 self.store.set_kind(new_id, kind);
340
341 if let Some(&value_ref) = self.vertex_values.get(old_id) {
342 self.vertex_values.insert(new_id, value_ref);
343 }
344
345 vertex_mapping.insert(*old_id, new_id);
346
347 let cell_ref = CellRef::new(new_sheet_id, Coord::new(row, col, true, true));
348 self.cell_to_vertex.insert(cell_ref, new_id);
349 }
350
351 let sheet_names: Vec<(String, NamedRange)> = self
352 .sheet_named_ranges
353 .iter()
354 .filter(|((sid, _), _)| *sid == source_sheet_id)
355 .map(|((_, name), range)| (name.clone(), range.clone()))
356 .collect();
357
358 for (name, mut named_range) in sheet_names {
359 named_range.scope = NameScope::Sheet(new_sheet_id);
360
361 match &mut named_range.definition {
362 NamedDefinition::Cell(cell_ref) if cell_ref.sheet_id == source_sheet_id => {
363 cell_ref.sheet_id = new_sheet_id;
364 }
365 NamedDefinition::Range(range_ref) => {
366 if range_ref.start.sheet_id == source_sheet_id {
367 range_ref.start.sheet_id = new_sheet_id;
368 range_ref.end.sheet_id = new_sheet_id;
369 }
370 }
371 _ => {}
372 }
373
374 named_range.dependents.clear();
375 let name_vertex = self.allocate_name_vertex(named_range.scope);
376 if matches!(named_range.definition, NamedDefinition::Range(_)) {
377 self.store.set_kind(name_vertex, VertexKind::NamedArray);
378 } else {
379 self.store.set_kind(name_vertex, VertexKind::NamedScalar);
380 }
381 named_range.vertex = name_vertex;
382
383 let referenced_names = self.rebuild_name_dependencies(
384 name_vertex,
385 &named_range.definition,
386 named_range.scope,
387 )?;
388 if !referenced_names.is_empty() {
389 self.attach_vertex_to_names(name_vertex, &referenced_names);
390 }
391
392 self.sheet_named_ranges
393 .insert((new_sheet_id, name.clone()), named_range);
394 self.sheet_named_ranges_lookup
395 .insert((new_sheet_id, self.name_lookup_key(&name)), name.clone());
396 self.name_vertex_lookup
397 .insert(name_vertex, (NameScope::Sheet(new_sheet_id), name));
398 }
399
400 for (old_id, _) in &source_vertices {
401 if let Some(&new_id) = vertex_mapping.get(old_id)
402 && let Some(&ast_id) = self.vertex_formulas.get(old_id)
403 && let Some(ast) = self.data_store.retrieve_ast(ast_id, &self.sheet_reg)
404 {
405 let updated_ast = update_internal_sheet_references(
406 &ast,
407 &source_name,
408 new_name,
409 source_sheet_id,
410 new_sheet_id,
411 );
412
413 let new_ast_id = self.data_store.store_ast(&updated_ast, &self.sheet_reg);
414 self.vertex_formulas.insert(new_id, new_ast_id);
415
416 if let Ok((deps, range_deps, _, name_vertices)) =
417 self.extract_dependencies(&updated_ast, new_sheet_id)
418 {
419 let mapped_deps: Vec<VertexId> = deps
420 .iter()
421 .map(|&dep_id| vertex_mapping.get(&dep_id).copied().unwrap_or(dep_id))
422 .collect();
423
424 self.add_dependent_edges(new_id, &mapped_deps);
425 self.add_range_dependent_edges(new_id, &range_deps, new_sheet_id);
426
427 if !name_vertices.is_empty() {
428 self.attach_vertex_to_names(new_id, &name_vertices);
429 }
430 }
431 }
432 }
433
434 self.end_batch();
435
436 Ok(new_sheet_id)
437 }
438}