Skip to main content

gridline_engine/engine/
cell.rs

1//! Cell data structures for the spreadsheet grid.
2//!
3//! This module provides the core data types for representing cells:
4//! - [`CellType`] - The type of content in a cell (empty, text, number, or formula)
5//! - [`Cell`] - A cell with content, dependencies, and cached evaluation state
6//! - [`Grid`] - Thread-safe sparse storage for cells (backed by `DashMap`)
7//! - [`SpillMap`] - Thread-safe storage for array formula spill values
8
9use dashmap::DashMap;
10use std::sync::Arc;
11use serde::{Deserialize, Serialize};
12
13use super::cell_ref::CellRef;
14use super::deps::extract_dependencies;
15
16/// The type of content stored in a cell.
17#[derive(Clone, Debug, Serialize, Deserialize)]
18pub enum CellType {
19    Empty,
20    Text(String),
21    Number(f64),
22    Script(String),
23}
24
25/// A cell in the spreadsheet grid.
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct Cell {
28    pub contents: CellType,
29    pub depends_on: Vec<CellRef>,
30    pub dirty: bool,
31    /// Cached display string for script cells (not serialized).
32    #[serde(skip)]
33    pub cached_value: Option<String>,
34}
35
36impl Cell {
37    pub fn new_empty() -> Cell {
38        Cell {
39            contents: CellType::Empty,
40            depends_on: vec![],
41            dirty: false,
42            cached_value: None,
43        }
44    }
45
46    pub fn new_text(text: &str) -> Cell {
47        Cell {
48            contents: CellType::Text(text.to_string()),
49            depends_on: vec![],
50            dirty: false,
51            cached_value: None,
52        }
53    }
54
55    pub fn new_number(n: f64) -> Cell {
56        Cell {
57            contents: CellType::Number(n),
58            depends_on: vec![],
59            dirty: false,
60            cached_value: None,
61        }
62    }
63
64    /// Create a new cell containing a script/formula.
65    /// Dependencies are automatically extracted from the script.
66    pub fn new_script(script: &str) -> Cell {
67        Cell {
68            depends_on: extract_dependencies(script),
69            contents: CellType::Script(script.to_string()),
70            dirty: true,
71            cached_value: None,
72        }
73    }
74
75    /// Parse user input and create appropriate cell type.
76    /// - Empty string or whitespace -> Empty
77    /// - Starts with '=' -> Script (without the '=')
78    /// - Quoted string -> Text (without quotes)
79    /// - Valid number -> Number
80    /// - Otherwise -> Text
81    pub fn from_input(input: &str) -> Cell {
82        let trimmed = input.trim();
83        if trimmed.is_empty() {
84            return Cell::new_empty();
85        }
86
87        if let Some(formula) = trimmed.strip_prefix('=') {
88            return Cell::new_script(formula);
89        }
90
91        if trimmed.starts_with('"') && trimmed.ends_with('"') && trimmed.len() >= 2 {
92            let text = &trimmed[1..trimmed.len() - 1];
93            return Cell::new_text(text);
94        }
95
96        if let Ok(n) = trimmed.parse::<f64>() {
97            return Cell::new_number(n);
98        }
99
100        Cell::new_text(trimmed)
101    }
102
103    /// Get a display string for the cell content (for editing).
104    pub fn to_input_string(&self) -> String {
105        match &self.contents {
106            CellType::Empty => String::new(),
107            CellType::Text(s) => s.clone(),
108            CellType::Number(n) => n.to_string(),
109            CellType::Script(s) => format!("={}", s),
110        }
111    }
112}
113
114/// Thread-safe sparse grid storage.
115/// Wrapped in Arc so clones share the same underlying data.
116pub type Grid = std::sync::Arc<DashMap<CellRef, Cell>>;
117
118/// Thread-safe cache for computed cell values (both scalars and arrays).
119/// Maps cell positions to their evaluated Dynamic values.
120/// This allows:
121/// - Cell references to use pre-computed values instead of re-evaluating
122/// - Array formulas to store spill values for chaining
123pub type ValueCache = Arc<DashMap<CellRef, rhai::Dynamic>>;
124
125// Legacy type aliases for backward compatibility during refactoring
126#[doc(hidden)]
127pub type SpillMap = ValueCache;
128#[doc(hidden)]
129pub type ComputedMap = ValueCache;