1use crate::SheetId;
2use crate::engine::packed_coord::PackedCoord;
3use crate::engine::sheet_registry::SheetRegistry;
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: PackedCoord,
14 end: PackedCoord, },
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<PackedCoord>,
28 end: Option<PackedCoord>,
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_LIKELY_ARRAY: FormulaFlags = 0b0000_1000;
38
39#[derive(Debug, Default, Clone)]
40pub struct DependencyPlan {
41 pub formula_targets: Vec<(SheetId, PackedCoord)>,
42 pub global_cells: Vec<(SheetId, PackedCoord)>,
43 pub per_formula_cells: Vec<Vec<u32>>, pub per_formula_ranges: Vec<Vec<RangeKey>>,
45 pub per_formula_flags: Vec<FormulaFlags>,
46 pub edges_flat: Option<Vec<u32>>, pub offsets: Option<Vec<u32>>, }
49
50pub fn build_dependency_plan<'a, I>(
53 sheet_reg: &mut SheetRegistry,
54 formulas: I,
55 policy: &CollectPolicy,
56 volatile_flags: Option<&[bool]>,
57) -> Result<DependencyPlan, ExcelError>
58where
59 I: Iterator<Item = (&'a str, u32, u32, &'a formualizer_parse::parser::ASTNode)>,
60{
61 let mut plan = DependencyPlan::default();
62
63 let mut cell_index: FxHashMap<(SheetId, PackedCoord), u32> = FxHashMap::default();
65
66 for (i, (sheet_name, row, col, ast)) in formulas.enumerate() {
67 let sheet_id = sheet_reg.id_for(sheet_name);
68 let target = (sheet_id, PackedCoord::new(row, col));
69 plan.formula_targets.push(target);
70
71 let mut flags: FormulaFlags = 0;
72 if let Some(v) = volatile_flags.and_then(|v| v.get(i)).copied() {
73 if v {
74 flags |= F_VOLATILE;
75 }
76 }
77
78 let mut per_cells: Vec<u32> = Vec::new();
79 let mut per_ranges: Vec<RangeKey> = Vec::new();
80
81 let refs = ast.collect_references(policy);
83 for r in refs {
84 match r {
85 ReferenceType::Cell { sheet, row, col } => {
86 let dep_sheet = sheet
87 .as_deref()
88 .map(|name| sheet_reg.id_for(name))
89 .unwrap_or(sheet_id);
90 let key = (dep_sheet, PackedCoord::new(row, col));
91 let idx = match cell_index.get(&key) {
92 Some(&idx) => idx,
93 None => {
94 let new_idx = plan.global_cells.len() as u32;
95 plan.global_cells.push(key);
96 cell_index.insert(key, new_idx);
97 new_idx
98 }
99 };
100 per_cells.push(idx);
101 }
102 ReferenceType::Range {
103 sheet,
104 start_row,
105 start_col,
106 end_row,
107 end_col,
108 } => {
109 let dep_sheet = sheet
110 .as_deref()
111 .map(|name| sheet_reg.id_for(name))
112 .unwrap_or(sheet_id);
113 match (start_row, start_col, end_row, end_col) {
114 (Some(sr), Some(sc), Some(er), Some(ec)) => {
115 per_ranges.push(RangeKey::Rect {
116 sheet: dep_sheet,
117 start: PackedCoord::new(sr, sc),
118 end: PackedCoord::new(er, ec),
119 })
120 }
121 (None, Some(c), None, Some(ec)) if c == ec => {
122 per_ranges.push(RangeKey::WholeCol {
123 sheet: dep_sheet,
124 col: c,
125 })
126 }
127 (Some(r), None, Some(er), None) if r == er => {
128 per_ranges.push(RangeKey::WholeRow {
129 sheet: dep_sheet,
130 row: r,
131 })
132 }
133 _ => per_ranges.push(RangeKey::OpenRect {
134 sheet: dep_sheet,
135 start: start_row
136 .zip(start_col)
137 .map(|(r, c)| PackedCoord::new(r, c)),
138 end: end_row.zip(end_col).map(|(r, c)| PackedCoord::new(r, c)),
139 }),
140 }
141 }
142 ReferenceType::NamedRange(_name) => {
143 }
145 ReferenceType::Table(_tbl) => {
146 }
148 }
149 }
150
151 plan.per_formula_cells.push(per_cells);
152 plan.per_formula_ranges.push(per_ranges);
153 plan.per_formula_flags.push(flags);
154 }
155
156 Ok(plan)
157}