Skip to main content

formualizer_eval/engine/graph/
sources.rs

1use 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        self.bump_symbol_revision();
79        Ok(())
80    }
81
82    pub fn define_source_table(
83        &mut self,
84        name: &str,
85        version: Option<u64>,
86    ) -> Result<(), ExcelError> {
87        if name.is_empty() {
88            return Err(ExcelError::new(ExcelErrorKind::Name)
89                .with_message("Source name cannot be empty".to_string()));
90        }
91        if self.source_tables.contains_key(name) || self.source_scalars.contains_key(name) {
92            return Err(ExcelError::new(ExcelErrorKind::Name)
93                .with_message(format!("Source already defined: {name}")));
94        }
95
96        let vertex = self.allocate_source_vertex();
97        self.source_vertex_lookup.insert(vertex, name.to_string());
98        self.mark_volatile(vertex, version.is_none());
99
100        let entry = SourceTableEntry {
101            name: name.to_string(),
102            vertex,
103            version,
104        };
105        self.source_tables.insert(name.to_string(), entry);
106        self.bump_symbol_revision();
107        Ok(())
108    }
109
110    pub fn set_source_scalar_version(
111        &mut self,
112        name: &str,
113        version: Option<u64>,
114    ) -> Result<(), ExcelError> {
115        let vertex = {
116            let entry = self.source_scalars.get_mut(name).ok_or_else(|| {
117                ExcelError::new(ExcelErrorKind::Name)
118                    .with_message(format!("Unknown source: {name}"))
119            })?;
120
121            if entry.version == version {
122                return Ok(());
123            }
124
125            entry.version = version;
126            entry.vertex
127        };
128
129        self.mark_volatile(vertex, version.is_none());
130        self.mark_dirty(vertex);
131        Ok(())
132    }
133
134    pub fn set_source_table_version(
135        &mut self,
136        name: &str,
137        version: Option<u64>,
138    ) -> Result<(), ExcelError> {
139        let vertex = {
140            let entry = self.source_tables.get_mut(name).ok_or_else(|| {
141                ExcelError::new(ExcelErrorKind::Name)
142                    .with_message(format!("Unknown source: {name}"))
143            })?;
144
145            if entry.version == version {
146                return Ok(());
147            }
148
149            entry.version = version;
150            entry.vertex
151        };
152
153        self.mark_volatile(vertex, version.is_none());
154        self.mark_dirty(vertex);
155        Ok(())
156    }
157
158    pub fn invalidate_source(&mut self, name: &str) -> Result<(), ExcelError> {
159        if let Some(s) = self.source_scalars.get(name) {
160            self.mark_dirty(s.vertex);
161            return Ok(());
162        }
163        if let Some(t) = self.source_tables.get(name) {
164            self.mark_dirty(t.vertex);
165            return Ok(());
166        }
167        Err(ExcelError::new(ExcelErrorKind::Name).with_message(format!("Unknown source: {name}")))
168    }
169}