formualizer_eval/engine/graph/
sheets.rs

1use super::ast_utils::{update_internal_sheet_references, update_sheet_references_in_ast};
2use super::*;
3
4impl DependencyGraph {
5    /// Add a new sheet to the workbook.
6    ///
7    /// Creates a new sheet with the given name. If a sheet with this name
8    /// already exists, returns its ID without error (idempotent operation).
9    pub fn add_sheet(&mut self, name: &str) -> Result<SheetId, ExcelError> {
10        if let Some(id) = self.sheet_reg.get_id(name) {
11            return Ok(id);
12        }
13
14        let sheet_id = self.sheet_reg.id_for(name);
15        self.sheet_indexes.entry(sheet_id).or_default();
16        Ok(sheet_id)
17    }
18
19    /// Remove a sheet from the workbook.
20    pub fn remove_sheet(&mut self, sheet_id: SheetId) -> Result<(), ExcelError> {
21        if self.sheet_reg.name(sheet_id).is_empty() {
22            return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Sheet does not exist"));
23        }
24
25        let sheet_count = self.sheet_reg.all_sheets().len();
26        if sheet_count <= 1 {
27            return Err(
28                ExcelError::new(ExcelErrorKind::Value).with_message("Cannot remove the last sheet")
29            );
30        }
31
32        self.begin_batch();
33
34        let vertices_to_delete: Vec<VertexId> = self.vertices_in_sheet(sheet_id).collect();
35
36        let mut formulas_to_update = Vec::new();
37        for &formula_id in self.vertex_formulas.keys() {
38            let deps = self.edges.out_edges(formula_id);
39            for dep_id in deps {
40                if self.store.sheet_id(dep_id) == sheet_id {
41                    formulas_to_update.push(formula_id);
42                    break;
43                }
44            }
45        }
46
47        for formula_id in formulas_to_update {
48            self.mark_as_ref_error(formula_id);
49        }
50
51        for vertex_id in vertices_to_delete {
52            if let Some(cell_ref) = self.get_cell_ref_for_vertex(vertex_id) {
53                self.cell_to_vertex.remove(&cell_ref);
54            }
55
56            self.remove_all_edges(vertex_id);
57
58            let coord = self.store.coord(vertex_id);
59            if let Some(index) = self.sheet_indexes.get_mut(&sheet_id) {
60                index.remove_vertex(coord, vertex_id);
61            }
62
63            self.vertex_formulas.remove(&vertex_id);
64            self.vertex_values.remove(&vertex_id);
65
66            self.mark_deleted(vertex_id, true);
67        }
68
69        let sheet_names_to_remove: Vec<(SheetId, String)> = self
70            .sheet_named_ranges
71            .keys()
72            .filter(|(sid, _)| *sid == sheet_id)
73            .cloned()
74            .collect();
75
76        for key in sheet_names_to_remove {
77            if let Some(named_range) = self.sheet_named_ranges.remove(&key) {
78                self.mark_named_vertex_deleted(&named_range);
79            }
80        }
81
82        self.sheet_indexes.remove(&sheet_id);
83
84        if self.default_sheet_id == sheet_id
85            && let Some(&new_default) = self.sheet_indexes.keys().next()
86        {
87            self.default_sheet_id = new_default;
88        }
89
90        self.sheet_reg.remove(sheet_id)?;
91        self.end_batch();
92
93        Ok(())
94    }
95
96    /// Rename an existing sheet.
97    pub fn rename_sheet(&mut self, sheet_id: SheetId, new_name: &str) -> Result<(), ExcelError> {
98        if new_name.is_empty() || new_name.len() > 255 {
99            return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Invalid sheet name"));
100        }
101
102        let old_name = self.sheet_reg.name(sheet_id);
103        if old_name.is_empty() {
104            return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Sheet does not exist"));
105        }
106
107        if let Some(existing_id) = self.sheet_reg.get_id(new_name) {
108            if existing_id != sheet_id {
109                return Err(ExcelError::new(ExcelErrorKind::Value)
110                    .with_message(format!("Sheet '{new_name}' already exists")));
111            }
112            return Ok(());
113        }
114
115        let old_name = old_name.to_string();
116        self.sheet_reg.rename(sheet_id, new_name)?;
117
118        let formulas_to_update: Vec<VertexId> = self.vertex_formulas.keys().copied().collect();
119        for formula_id in formulas_to_update {
120            let ast_id = match self.get_formula_id(formula_id) {
121                Some(ast_id) => ast_id,
122                None => continue,
123            };
124            let ast = match self.data_store.retrieve_ast(ast_id, &self.sheet_reg) {
125                Some(ast) => ast,
126                None => continue,
127            };
128
129            let updated_ast = update_sheet_references_in_ast(&ast, &old_name, new_name);
130            if ast != updated_ast {
131                let updated_ast_id = self.data_store.store_ast(&updated_ast, &self.sheet_reg);
132                self.vertex_formulas.insert(formula_id, updated_ast_id);
133                self.mark_vertex_dirty(formula_id);
134            }
135        }
136
137        Ok(())
138    }
139
140    /// Duplicate an existing sheet.
141    pub fn duplicate_sheet(
142        &mut self,
143        source_sheet_id: SheetId,
144        new_name: &str,
145    ) -> Result<SheetId, ExcelError> {
146        if new_name.is_empty() || new_name.len() > 255 {
147            return Err(ExcelError::new(ExcelErrorKind::Value).with_message("Invalid sheet name"));
148        }
149
150        let source_name = self.sheet_reg.name(source_sheet_id).to_string();
151        if source_name.is_empty() {
152            return Err(
153                ExcelError::new(ExcelErrorKind::Value).with_message("Source sheet does not exist")
154            );
155        }
156
157        if self.sheet_reg.get_id(new_name).is_some() {
158            return Err(ExcelError::new(ExcelErrorKind::Value)
159                .with_message(format!("Sheet '{new_name}' already exists")));
160        }
161
162        let new_sheet_id = self.add_sheet(new_name)?;
163
164        self.begin_batch();
165
166        let source_vertices: Vec<(VertexId, AbsCoord)> = self
167            .vertices_in_sheet(source_sheet_id)
168            .map(|id| (id, self.store.coord(id)))
169            .collect();
170
171        let mut vertex_mapping = FxHashMap::default();
172
173        for (old_id, coord) in &source_vertices {
174            let row = coord.row();
175            let col = coord.col();
176            let kind = self.store.kind(*old_id);
177
178            let new_id = self.store.allocate(*coord, new_sheet_id, 0x01);
179            self.edges.add_vertex(*coord, new_id.0);
180            self.sheet_index_mut(new_sheet_id)
181                .add_vertex(*coord, new_id);
182
183            self.store.set_kind(new_id, kind);
184
185            if let Some(&value_ref) = self.vertex_values.get(old_id) {
186                self.vertex_values.insert(new_id, value_ref);
187            }
188
189            vertex_mapping.insert(*old_id, new_id);
190
191            let cell_ref = CellRef::new(new_sheet_id, Coord::new(row, col, true, true));
192            self.cell_to_vertex.insert(cell_ref, new_id);
193        }
194
195        for (old_id, _) in &source_vertices {
196            if let Some(&new_id) = vertex_mapping.get(old_id)
197                && let Some(&ast_id) = self.vertex_formulas.get(old_id)
198                && let Some(ast) = self.data_store.retrieve_ast(ast_id, &self.sheet_reg)
199            {
200                let updated_ast = update_internal_sheet_references(
201                    &ast,
202                    &source_name,
203                    new_name,
204                    source_sheet_id,
205                    new_sheet_id,
206                );
207
208                let new_ast_id = self.data_store.store_ast(&updated_ast, &self.sheet_reg);
209                self.vertex_formulas.insert(new_id, new_ast_id);
210
211                if let Ok((deps, range_deps, _, name_vertices)) =
212                    self.extract_dependencies(&updated_ast, new_sheet_id)
213                {
214                    let mapped_deps: Vec<VertexId> = deps
215                        .iter()
216                        .map(|&dep_id| vertex_mapping.get(&dep_id).copied().unwrap_or(dep_id))
217                        .collect();
218
219                    self.add_dependent_edges(new_id, &mapped_deps);
220                    self.add_range_dependent_edges(new_id, &range_deps, new_sheet_id);
221
222                    if !name_vertices.is_empty() {
223                        self.attach_vertex_to_names(new_id, &name_vertices);
224                    }
225                }
226            }
227        }
228
229        let sheet_names: Vec<(String, NamedRange)> = self
230            .sheet_named_ranges
231            .iter()
232            .filter(|((sid, _), _)| *sid == source_sheet_id)
233            .map(|((_, name), range)| (name.clone(), range.clone()))
234            .collect();
235
236        for (name, mut named_range) in sheet_names {
237            named_range.scope = NameScope::Sheet(new_sheet_id);
238
239            match &mut named_range.definition {
240                NamedDefinition::Cell(cell_ref) if cell_ref.sheet_id == source_sheet_id => {
241                    cell_ref.sheet_id = new_sheet_id;
242                }
243                NamedDefinition::Range(range_ref) => {
244                    if range_ref.start.sheet_id == source_sheet_id {
245                        range_ref.start.sheet_id = new_sheet_id;
246                        range_ref.end.sheet_id = new_sheet_id;
247                    }
248                }
249                _ => {}
250            }
251
252            named_range.dependents.clear();
253            let name_vertex = self.allocate_name_vertex(named_range.scope);
254            if matches!(named_range.definition, NamedDefinition::Range(_)) {
255                self.store.set_kind(name_vertex, VertexKind::NamedArray);
256            } else {
257                self.store.set_kind(name_vertex, VertexKind::NamedScalar);
258            }
259            named_range.vertex = name_vertex;
260
261            let referenced_names = self.rebuild_name_dependencies(
262                name_vertex,
263                &named_range.definition,
264                named_range.scope,
265            );
266            if !referenced_names.is_empty() {
267                self.attach_vertex_to_names(name_vertex, &referenced_names);
268            }
269
270            self.sheet_named_ranges
271                .insert((new_sheet_id, name.clone()), named_range);
272            self.name_vertex_lookup
273                .insert(name_vertex, (NameScope::Sheet(new_sheet_id), name));
274        }
275
276        self.end_batch();
277
278        Ok(new_sheet_id)
279    }
280}