formualizer_eval/engine/graph/
tables.rs1use crate::SheetId;
2use crate::engine::graph::DependencyGraph;
3use crate::engine::vertex::{VertexId, VertexKind};
4use crate::reference::RangeRef;
5use formualizer_common::{ExcelError, ExcelErrorKind};
6
7#[derive(Debug, Clone)]
9pub struct TableEntry {
10 pub name: String,
11 pub range: RangeRef,
12 pub headers: Vec<String>,
13 pub totals_row: bool,
14 pub vertex: VertexId,
15}
16
17impl TableEntry {
18 pub fn sheet_id(&self) -> SheetId {
19 self.range.start.sheet_id
20 }
21
22 pub fn col_index(&self, header: &str) -> Option<usize> {
23 self.headers
24 .iter()
25 .position(|h| h.eq_ignore_ascii_case(header))
26 }
27}
28
29impl DependencyGraph {
30 pub fn resolve_table_entry(&self, name: &str) -> Option<&TableEntry> {
31 self.tables.get(name)
32 }
33
34 pub fn table_by_vertex(&self, vertex: VertexId) -> Option<&TableEntry> {
35 self.table_vertex_lookup
36 .get(&vertex)
37 .and_then(|name| self.tables.get(name))
38 }
39
40 pub fn define_table(
41 &mut self,
42 name: &str,
43 range: RangeRef,
44 headers: Vec<String>,
45 totals_row: bool,
46 ) -> Result<(), ExcelError> {
47 if name.is_empty() {
48 return Err(ExcelError::new(ExcelErrorKind::Name)
49 .with_message("Table name cannot be empty".to_string()));
50 }
51
52 if self.tables.contains_key(name) {
53 return Err(ExcelError::new(ExcelErrorKind::Name)
54 .with_message(format!("Table already defined: {name}")));
55 }
56
57 let anchor = range.start;
58 let sheet_id = anchor.sheet_id;
59 let packed_coord = formualizer_common::Coord::new(anchor.coord.row(), anchor.coord.col());
60 let vertex = self.store.allocate(packed_coord, sheet_id, 0x01);
61 self.edges.add_vertex(packed_coord, vertex.0);
62 self.sheet_index_mut(sheet_id)
63 .add_vertex(packed_coord, vertex);
64 self.store.set_kind(vertex, VertexKind::Table);
65
66 self.register_table_range_deps(vertex, &range);
69
70 let entry = TableEntry {
71 name: name.to_string(),
72 range,
73 headers,
74 totals_row,
75 vertex,
76 };
77
78 self.tables.insert(name.to_string(), entry);
79 self.table_vertex_lookup.insert(vertex, name.to_string());
80 Ok(())
81 }
82
83 pub fn update_table(
84 &mut self,
85 name: &str,
86 new_range: RangeRef,
87 headers: Vec<String>,
88 totals_row: bool,
89 ) -> Result<(), ExcelError> {
90 let vertex = self.tables.get(name).map(|t| t.vertex).ok_or_else(|| {
91 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown table: {name}"))
92 })?;
93
94 self.remove_dependent_edges(vertex);
96 self.register_table_range_deps(vertex, &new_range);
97
98 if let Some(existing) = self.tables.get_mut(name) {
99 existing.range = new_range;
100 existing.headers = headers;
101 existing.totals_row = totals_row;
102 }
103
104 self.mark_dirty(vertex);
106 Ok(())
107 }
108
109 pub fn delete_table(&mut self, name: &str) -> Result<(), ExcelError> {
110 let Some(entry) = self.tables.remove(name) else {
111 return Err(ExcelError::new(ExcelErrorKind::Name)
112 .with_message(format!("Unknown table: {name}")));
113 };
114
115 let vertex = entry.vertex;
116 self.table_vertex_lookup.remove(&vertex);
117
118 self.remove_dependent_edges(vertex);
120
121 self.store.mark_deleted(vertex, true);
123 self.vertex_values.remove(&vertex);
124 self.vertex_formulas.remove(&vertex);
125 self.dirty_vertices.remove(&vertex);
126 self.volatile_vertices.remove(&vertex);
127
128 Ok(())
129 }
130
131 fn register_table_range_deps(&mut self, table_vertex: VertexId, range: &RangeRef) {
132 use crate::reference::SharedRangeRef;
133 use crate::reference::SharedSheetLocator;
134 use formualizer_common::AxisBound;
135
136 let sheet_loc = SharedSheetLocator::Id(range.start.sheet_id);
138 let sr = AxisBound::new(range.start.coord.row(), range.start.coord.row_abs());
139 let sc = AxisBound::new(range.start.coord.col(), range.start.coord.col_abs());
140 let er = AxisBound::new(range.end.coord.row(), range.end.coord.row_abs());
141 let ec = AxisBound::new(range.end.coord.col(), range.end.coord.col_abs());
142
143 if let Ok(r) = SharedRangeRef::from_parts(sheet_loc, Some(sr), Some(sc), Some(er), Some(ec))
144 {
145 self.add_range_dependent_edges(table_vertex, &[r.into_owned()], range.start.sheet_id);
146 }
147 }
148}