Skip to main content

visi_core/core/engine/
cell.rs

1use serde::{Deserialize, Serialize};
2
3/// A random 53-bit identifier for a sheet or column.
4///
5/// Capped to `2^53 - 1` for JSON compatibility
6pub fn generate_unique_id() -> u64 {
7    let mut buf = [0u8; 8];
8    let val = if getrandom::getrandom(&mut buf).is_err() {
9        let now = web_time::SystemTime::now()
10            .duration_since(web_time::SystemTime::UNIX_EPOCH)
11            .map(|d| d.as_nanos())
12            .unwrap_or(0);
13        now as u64
14    } else {
15        u64::from_le_bytes(buf)
16    };
17    val & 0x001F_FFFF_FFFF_FFFF
18}
19
20/// The intrinsic data type of a cell, mirroring calamine worksheet value variants.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
22pub enum CellType {
23    /// Empty cell (`calamine::Data::Empty`).
24    #[default]
25    Empty,
26    /// Signed integer (`calamine::Data::Int`).
27    Int,
28    /// Floating point number (`calamine::Data::Float`).
29    Float,
30    /// String (`calamine::Data::String`).
31    String,
32    /// Boolean (`calamine::Data::Bool`).
33    Bool,
34    /// Date/time serial identified by calamine from workbook formatting.
35    DateTime,
36    /// ISO 8601 date/time (`calamine::Data::DateTimeIso`, OpenXML `t="d"`).
37    DateTimeIso,
38    /// ISO 8601 duration (`calamine::Data::DurationIso`).
39    DurationIso,
40    /// Error cell (`calamine::Data::Error`, OpenXML `t="e"`).
41    Error,
42}
43
44impl CellType {
45    /// Whether this cell type is explicitly a string/text cell.
46    pub fn is_string(&self) -> bool {
47        matches!(self, CellType::String)
48    }
49
50    /// Stable lowercase spelling used by CLI and JSON output.
51    pub fn as_str(&self) -> &'static str {
52        match self {
53            CellType::Empty => "empty",
54            CellType::Int => "int",
55            CellType::Float => "float",
56            CellType::String => "string",
57            CellType::Bool => "bool",
58            CellType::DateTime => "date-time",
59            CellType::DateTimeIso => "date-time-iso",
60            CellType::DurationIso => "duration-iso",
61            CellType::Error => "error",
62        }
63    }
64}
65
66/// For either a column or row
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
68pub enum RefType {
69    /// Written without a `$`, so it shifts
70    /// when the formula is filled or copied
71    Relative,
72    /// Written with a `$`, so it stays put
73    Absolute,
74}
75
76impl std::fmt::Display for RefType {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            RefType::Relative => write!(f, ""),
80            RefType::Absolute => write!(f, "$"),
81        }
82    }
83}
84
85/// A cell's position, plus whether it was written as absolute
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
87pub struct CellRef {
88    /// Row index, 0-based
89    pub row: usize,
90    /// Column index, 0-based
91    pub col: usize,
92    /// Whether the row was written with a `$`.
93    pub row_ref_type: RefType,
94    /// Whether the column was written with a `$`.
95    pub col_ref_type: RefType,
96}
97
98impl std::fmt::Display for CellRef {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(
101            f,
102            "CELL({}{}, {}{})",
103            self.row_ref_type, self.row, self.col_ref_type, self.col
104        )
105    }
106}
107
108impl CellRef {
109    /// A relative reference to `(row, col)`, 0-based.
110    pub fn new(row: usize, col: usize) -> CellRef {
111        Self {
112            row,
113            col,
114            row_ref_type: RefType::Relative,
115            col_ref_type: RefType::Relative,
116        }
117    }
118}
119
120/// Something a formula reads, and therefore
121/// an edge in the recalculation graph.
122#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
123pub enum Dependency {
124    /// A cell on the same sheet.
125    Local(CellRef),
126    /// A whole column on the same sheet, by 0-based position.
127    LocalColumn(usize),
128    /// A cell on another sheet.
129    Remote {
130        /// Name of the sheet the cell is on.
131        sheet: String,
132        /// The cell, on that sheet.
133        cell: CellRef,
134    },
135    /// A whole column on another sheet.
136    RemoteColumn {
137        /// Name of the sheet the column is on.
138        sheet: String,
139        /// Column index, 0-based.
140        col: usize,
141    },
142}
143
144/// A caret position: a cell plus an offset within its source text
145#[derive(Debug, Clone, Default)]
146pub struct TextCellRef {
147    /// Row index, 0-based
148    pub row: usize,
149    /// Column index, 0-based
150    pub col: usize,
151    /// Offset into the cell's source text, in characters rather than bytes
152    pub char_offset: usize,
153}
154
155/// A formula that could not be evaluated at all
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub enum EvalError {
158    /// The formula could not be parsed, or named something unrecognized
159    UnknownFunction(String),
160}
161
162impl std::error::Error for EvalError {}
163
164impl std::fmt::Display for EvalError {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        match self {
167            EvalError::UnknownFunction(func) => write!(f, "{}", func),
168        }
169    }
170}
171
172/// What the engine's evaluation entry points return on failure.
173#[derive(Debug, Clone, PartialEq, Eq)]
174pub enum EngineError {
175    /// A formula could not be evaluated.
176    EvalError(EvalError),
177}
178
179impl std::error::Error for EngineError {
180    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
181        match self {
182            EngineError::EvalError(err) => Some(err),
183        }
184    }
185}
186
187impl std::fmt::Display for EngineError {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        match self {
190            EngineError::EvalError(err) => write!(f, "{}", err),
191        }
192    }
193}
194
195impl From<EvalError> for EngineError {
196    fn from(err: EvalError) -> Self {
197        EngineError::EvalError(err)
198    }
199}