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        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}