formualizer_eval/engine/graph/
sources.rs1use crate::SheetId;
2use crate::engine::graph::DependencyGraph;
3use crate::engine::named_range::NameScope;
4use crate::engine::vertex::{VertexId, VertexKind};
5use formualizer_common::{Coord as AbsCoord, ExcelError, ExcelErrorKind};
6
7#[derive(Debug, Clone)]
8pub struct SourceScalarEntry {
9 pub name: String,
10 pub vertex: VertexId,
11 pub version: Option<u64>,
12}
13
14#[derive(Debug, Clone)]
15pub struct SourceTableEntry {
16 pub name: String,
17 pub vertex: VertexId,
18 pub version: Option<u64>,
19}
20
21impl DependencyGraph {
22 fn next_source_coord(&mut self) -> AbsCoord {
23 const COLS: u32 = 16_384;
24 const SOURCE_ROW_OFFSET: u32 = 524_288;
25
26 let seq = self.source_vertex_seq;
27 self.source_vertex_seq = self.source_vertex_seq.wrapping_add(1);
28
29 let row = (seq / COLS)
30 .saturating_add(SOURCE_ROW_OFFSET)
31 .min(0x000F_FFFF);
32 let col = seq % COLS;
33 AbsCoord::new(row, col)
34 }
35
36 fn allocate_source_vertex(&mut self) -> VertexId {
37 let coord = self.next_source_coord();
38 let sheet_id: SheetId = self.default_sheet_id;
39 let vertex = self.store.allocate(coord, sheet_id, 0x01);
40 self.edges.add_vertex(coord, vertex.0);
41 self.store.set_kind(vertex, VertexKind::External);
42 vertex
43 }
44
45 pub fn resolve_source_scalar_entry(&self, name: &str) -> Option<&SourceScalarEntry> {
46 self.source_scalars.get(name)
47 }
48
49 pub fn resolve_source_table_entry(&self, name: &str) -> Option<&SourceTableEntry> {
50 self.source_tables.get(name)
51 }
52
53 pub fn define_source_scalar(
54 &mut self,
55 name: &str,
56 version: Option<u64>,
57 ) -> Result<(), ExcelError> {
58 if name.is_empty() {
59 return Err(ExcelError::new(ExcelErrorKind::Name)
60 .with_message("Source name cannot be empty".to_string()));
61 }
62 if self.source_scalars.contains_key(name) || self.source_tables.contains_key(name) {
63 return Err(ExcelError::new(ExcelErrorKind::Name)
64 .with_message(format!("Source already defined: {name}")));
65 }
66
67 let vertex = self.allocate_source_vertex();
68 self.source_vertex_lookup.insert(vertex, name.to_string());
69 self.mark_volatile(vertex, version.is_none());
70
71 let entry = SourceScalarEntry {
72 name: name.to_string(),
73 vertex,
74 version,
75 };
76 self.source_scalars.insert(name.to_string(), entry);
77 self.resolve_pending_name_references(NameScope::Workbook, name);
78 Ok(())
79 }
80
81 pub fn define_source_table(
82 &mut self,
83 name: &str,
84 version: Option<u64>,
85 ) -> Result<(), ExcelError> {
86 if name.is_empty() {
87 return Err(ExcelError::new(ExcelErrorKind::Name)
88 .with_message("Source name cannot be empty".to_string()));
89 }
90 if self.source_tables.contains_key(name) || self.source_scalars.contains_key(name) {
91 return Err(ExcelError::new(ExcelErrorKind::Name)
92 .with_message(format!("Source already defined: {name}")));
93 }
94
95 let vertex = self.allocate_source_vertex();
96 self.source_vertex_lookup.insert(vertex, name.to_string());
97 self.mark_volatile(vertex, version.is_none());
98
99 let entry = SourceTableEntry {
100 name: name.to_string(),
101 vertex,
102 version,
103 };
104 self.source_tables.insert(name.to_string(), entry);
105 Ok(())
106 }
107
108 pub fn set_source_scalar_version(
109 &mut self,
110 name: &str,
111 version: Option<u64>,
112 ) -> Result<(), ExcelError> {
113 let vertex = {
114 let entry = self.source_scalars.get_mut(name).ok_or_else(|| {
115 ExcelError::new(ExcelErrorKind::Name)
116 .with_message(format!("Unknown source: {name}"))
117 })?;
118
119 if entry.version == version {
120 return Ok(());
121 }
122
123 entry.version = version;
124 entry.vertex
125 };
126
127 self.mark_volatile(vertex, version.is_none());
128 self.mark_dirty(vertex);
129 Ok(())
130 }
131
132 pub fn set_source_table_version(
133 &mut self,
134 name: &str,
135 version: Option<u64>,
136 ) -> Result<(), ExcelError> {
137 let vertex = {
138 let entry = self.source_tables.get_mut(name).ok_or_else(|| {
139 ExcelError::new(ExcelErrorKind::Name)
140 .with_message(format!("Unknown source: {name}"))
141 })?;
142
143 if entry.version == version {
144 return Ok(());
145 }
146
147 entry.version = version;
148 entry.vertex
149 };
150
151 self.mark_volatile(vertex, version.is_none());
152 self.mark_dirty(vertex);
153 Ok(())
154 }
155
156 pub fn invalidate_source(&mut self, name: &str) -> Result<(), ExcelError> {
157 if let Some(s) = self.source_scalars.get(name) {
158 self.mark_dirty(s.vertex);
159 return Ok(());
160 }
161 if let Some(t) = self.source_tables.get(name) {
162 self.mark_dirty(t.vertex);
163 return Ok(());
164 }
165 Err(ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown source: {name}")))
166 }
167}