visi_core/core/formula.rs
1use crate::core::RefType;
2use serde::{Deserialize, Serialize};
3
4/// Which part of an Excel Table a structured reference selects, as in
5/// `Sales[#Headers]`.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum SheetSection {
8 /// The body rows, excluding header and totals. The default.
9 Data,
10 /// The header row.
11 Headers,
12 /// The totals row.
13 Totals,
14 /// Header, data and totals together.
15 All,
16}
17
18/// One piece of a compiled formula: either literal text or a reference held by
19/// id.
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub enum FormulaPart {
22 /// A literal stretch of the formula -- operators, function names,
23 /// constants -- copied through unchanged.
24 Text(String),
25 /// A single cell, as in `Sheet2!$A1`.
26 SheetReference {
27 /// Sheet the cell is on.
28 sheet_id: u64,
29 /// Row, 0-based.
30 row: usize,
31 /// Column, 0-based.
32 col: usize,
33 /// Whether the row was written with a `$`.
34 row_ref_type: RefType,
35 /// Whether the column was written with a `$`.
36 col_ref_type: RefType,
37 },
38 /// A whole column, held by column id so a column rename survives.
39 ColumnReference {
40 /// Sheet the column is on.
41 sheet_id: u64,
42 /// The column's identifier, not its position.
43 col_id: u64,
44 },
45 /// An Excel Table structured reference, as in `Sales[Amount]` or
46 /// `[@Amount]`.
47 StructuredReference {
48 /// Sheet the reference resolves against.
49 sheet_id: u64,
50 /// The referenced column, or `None` for a whole-table reference.
51 col_id: Option<u64>,
52 /// `true` for the `[@Amount]` form, which means the current row.
53 is_this_row: bool,
54 /// Which part of the table is selected.
55 section: SheetSection,
56 },
57 /// A rectangular range, as in `Sheet2!A1:$B$10`.
58 RangeReference {
59 /// Sheet the range is on.
60 sheet_id: u64,
61 /// First row, 0-based.
62 start_row: usize,
63 /// First column, 0-based.
64 start_col: usize,
65 /// Last row, 0-based and inclusive.
66 end_row: usize,
67 /// Last column, 0-based and inclusive.
68 end_col: usize,
69 /// Whether the start row was written with a `$`.
70 start_row_ref_type: RefType,
71 /// Whether the start column was written with a `$`.
72 start_col_ref_type: RefType,
73 /// Whether the end row was written with a `$`.
74 end_row_ref_type: RefType,
75 /// Whether the end column was written with a `$`.
76 end_col_ref_type: RefType,
77 },
78}
79
80/// A formula split into literal text and id-held references.
81///
82/// Cached per cell in `DataColumn::compiled_src`, and rendered back to A1 text
83/// on demand by `parser::serialize_formula`.
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct CompiledFormula {
86 /// The pieces, in the order they appear in the formula text.
87 pub parts: Vec<FormulaPart>,
88}
89
90impl CompiledFormula {
91 /// Creates a plain formula from a raw string, without any parsed references.
92 /// Useful as a default constructor or fallback.
93 pub fn plain(text: String) -> Self {
94 Self {
95 parts: vec![FormulaPart::Text(text)],
96 }
97 }
98
99 /// Checks if the formula is empty
100 #[allow(dead_code)]
101 pub fn is_empty(&self) -> bool {
102 self.parts.is_empty()
103 || (self.parts.len() == 1
104 && match &self.parts[0] {
105 FormulaPart::Text(s) => s.is_empty(),
106 _ => false,
107 })
108 }
109}
110
111impl Default for CompiledFormula {
112 fn default() -> Self {
113 Self::plain(String::new())
114 }
115}