Skip to main content

formualizer_eval/engine/graph/
sources.rs

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