1use crate::SheetId;
2use crate::engine::sheet_registry::SheetRegistry;
3use formualizer_common::Coord as AbsCoord;
4use formualizer_common::ExcelError;
5use formualizer_parse::parser::{CollectPolicy, ReferenceType};
6use rustc_hash::FxHashMap;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub enum RangeKey {
11 Rect {
12 sheet: SheetId,
13 start: AbsCoord,
14 end: AbsCoord, },
16 WholeRow {
17 sheet: SheetId,
18 row: u32,
19 },
20 WholeCol {
21 sheet: SheetId,
22 col: u32,
23 },
24 OpenRect {
26 sheet: SheetId,
27 start: Option<AbsCoord>,
28 end: Option<AbsCoord>,
29 },
30}
31
32pub type FormulaFlags = u8;
34pub const F_VOLATILE: FormulaFlags = 0b0000_0001;
35pub const F_HAS_RANGES: FormulaFlags = 0b0000_0010;
36pub const F_HAS_NAMES: FormulaFlags = 0b0000_0100;
37pub const F_HAS_TABLES: FormulaFlags = 0b0001_0000;
38pub const F_LIKELY_ARRAY: FormulaFlags = 0b0000_1000;
39
40#[derive(Debug, Default, Clone)]
41pub struct DependencyPlan {
42 pub formula_targets: Vec<(SheetId, AbsCoord)>,
43 pub global_cells: Vec<(SheetId, AbsCoord)>,
44 pub per_formula_cells: Vec<Vec<u32>>, pub per_formula_ranges: Vec<Vec<RangeKey>>,
46 pub per_formula_names: Vec<Vec<String>>,
47 pub per_formula_tables: Vec<Vec<String>>,
48 pub per_formula_flags: Vec<FormulaFlags>,
49 pub edges_flat: Option<Vec<u32>>, pub offsets: Option<Vec<u32>>, }
52
53pub fn build_dependency_plan<'a, I>(
56 sheet_reg: &mut SheetRegistry,
57 formulas: I,
58 policy: &CollectPolicy,
59 volatile_flags: Option<&[bool]>,
60) -> Result<DependencyPlan, ExcelError>
61where
62 I: Iterator<Item = (&'a str, u32, u32, &'a formualizer_parse::parser::ASTNode)>,
63{
64 let mut plan = DependencyPlan::default();
65
66 let mut cell_index: FxHashMap<(SheetId, AbsCoord), u32> = FxHashMap::default();
68
69 for (i, (sheet_name, row, col, ast)) in formulas.enumerate() {
70 let sheet_id = sheet_reg.id_for(sheet_name);
71 let target = (sheet_id, AbsCoord::from_excel(row, col));
72 plan.formula_targets.push(target);
73
74 let mut flags: FormulaFlags = 0;
75 if let Some(v) = volatile_flags.and_then(|v| v.get(i)).copied()
76 && v
77 {
78 flags |= F_VOLATILE;
79 }
80
81 let mut per_cells: Vec<u32> = Vec::new();
82 let mut per_ranges: Vec<RangeKey> = Vec::new();
83 let mut per_names: Vec<String> = Vec::new();
84 let mut per_tables: Vec<String> = Vec::new();
85
86 let refs = ast.collect_references(policy);
88 for r in refs {
89 match r {
90 ReferenceType::Cell {
91 sheet, row, col, ..
92 } => {
93 let dep_sheet = sheet
94 .as_deref()
95 .map(|name| sheet_reg.id_for(name))
96 .unwrap_or(sheet_id);
97 let key = (dep_sheet, AbsCoord::from_excel(row, col));
98 let idx = match cell_index.get(&key) {
99 Some(&idx) => idx,
100 None => {
101 let new_idx = plan.global_cells.len() as u32;
102 plan.global_cells.push(key);
103 cell_index.insert(key, new_idx);
104 new_idx
105 }
106 };
107 per_cells.push(idx);
108 }
109 ReferenceType::Range {
110 sheet,
111 start_row,
112 start_col,
113 end_row,
114 end_col,
115 ..
116 } => {
117 let dep_sheet = sheet
118 .as_deref()
119 .map(|name| sheet_reg.id_for(name))
120 .unwrap_or(sheet_id);
121 match (start_row, start_col, end_row, end_col) {
122 (Some(sr), Some(sc), Some(er), Some(ec)) => {
123 per_ranges.push(RangeKey::Rect {
124 sheet: dep_sheet,
125 start: AbsCoord::from_excel(sr, sc),
126 end: AbsCoord::from_excel(er, ec),
127 })
128 }
129 (None, Some(c), None, Some(ec)) if c == ec => {
130 per_ranges.push(RangeKey::WholeCol {
131 sheet: dep_sheet,
132 col: c,
133 })
134 }
135 (Some(r), None, Some(er), None) if r == er => {
136 per_ranges.push(RangeKey::WholeRow {
137 sheet: dep_sheet,
138 row: r,
139 })
140 }
141 _ => per_ranges.push(RangeKey::OpenRect {
142 sheet: dep_sheet,
143 start: start_row
144 .zip(start_col)
145 .map(|(r, c)| AbsCoord::from_excel(r, c)),
146 end: end_row
147 .zip(end_col)
148 .map(|(r, c)| AbsCoord::from_excel(r, c)),
149 }),
150 }
151 }
152 ReferenceType::External(ext) => match ext.kind {
153 formualizer_parse::parser::ExternalRefKind::Cell { .. } => {
154 flags |= F_HAS_NAMES;
155 per_names.push(ext.raw.clone());
156 }
157 formualizer_parse::parser::ExternalRefKind::Range { .. } => {
158 flags |= F_HAS_TABLES;
159 per_tables.push(ext.raw.clone());
160 }
161 },
162 ReferenceType::NamedRange(name) => {
163 flags |= F_HAS_NAMES;
165 per_names.push(name);
166 }
167 ReferenceType::Table(tref) => {
168 flags |= F_HAS_TABLES;
169 per_tables.push(tref.name);
170 }
171 }
172 }
173
174 plan.per_formula_cells.push(per_cells);
175 plan.per_formula_ranges.push(per_ranges);
176 plan.per_formula_names.push(per_names);
177 plan.per_formula_tables.push(per_tables);
178 plan.per_formula_flags.push(flags);
179 }
180
181 Ok(plan)
182}