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