Skip to main content

visi_core/core/engine/
column.rs

1use crate::core::CompiledFormula;
2use crate::core::SharedVec;
3use serde::{Deserialize, Serialize};
4
5use super::bitmask::Bitmask;
6use super::cell::{CellType, generate_unique_id};
7use super::result_data::ResultData;
8
9/// A column of computed values
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub enum ColumnData {
12    /// All-integer, or integer-and-blank.
13    Integer {
14        /// Which positions hold a value rather than a blank.
15        validity: Bitmask,
16        /// The values. Positions marked invalid hold a placeholder.
17        values: SharedVec<i64>,
18    },
19    /// Numeric with at least one non-integer, or integer-and-blank promoted.
20    Float {
21        /// Which positions hold a value rather than a blank.
22        validity: Bitmask,
23        /// The values. Positions marked invalid hold a placeholder.
24        values: SharedVec<f64>,
25    },
26    /// Mixed: anything the numeric representations cannot hold -- text,
27    /// booleans, errors.
28    Any(SharedVec<ResultData>),
29}
30
31impl ColumnData {
32    pub(crate) fn new(size: usize) -> Self {
33        Self::Integer {
34            validity: Bitmask::with_size(size),
35            values: vec![0; size].into(),
36        }
37    }
38
39    /// How many rows the column holds.
40    pub fn len(&self) -> usize {
41        match self {
42            Self::Integer { validity, .. } => validity.len,
43            Self::Float { validity, .. } => validity.len,
44            Self::Any(v) => v.len(),
45        }
46    }
47
48    /// Whether the column holds no rows at all.
49    pub fn is_empty(&self) -> bool {
50        self.len() == 0
51    }
52
53    pub(crate) fn push(&mut self, value: ResultData) {
54        let index = self.len();
55        self.insert(index, value);
56    }
57
58    /// The value at `index`, or `None` if that is past the end.
59    ///
60    /// A blank within the column's range reads as
61    /// `Some(ResultData::None)`, which is what distinguishes it from an
62    /// out-of-range index.
63    pub fn get(&self, index: usize) -> Option<ResultData> {
64        if index >= self.len() {
65            return None;
66        }
67        match self {
68            Self::Integer { validity, values } => {
69                if validity.get(index) {
70                    Some(ResultData::Integer(values[index]))
71                } else {
72                    Some(ResultData::None)
73                }
74            }
75            Self::Float { validity, values } => {
76                if validity.get(index) {
77                    Some(ResultData::Float(values[index]))
78                } else {
79                    Some(ResultData::None)
80                }
81            }
82            Self::Any(v) => Some(v[index].clone()),
83        }
84    }
85
86    pub(crate) fn demote_to_any(&mut self) {
87        let len = self.len();
88        let mut any = Vec::with_capacity(len);
89        for i in 0..len {
90            any.push(self.get(i).unwrap());
91        }
92        *self = Self::Any(any.into());
93    }
94
95    pub(crate) fn promote_to_float(&mut self) {
96        if let Self::Integer { validity, values } = self {
97            let float_values = values.iter().map(|&i| i as f64).collect();
98            *self = Self::Float {
99                validity: validity.clone(),
100                values: float_values,
101            };
102        }
103    }
104
105    pub(crate) fn resize(&mut self, size: usize) {
106        match self {
107            Self::Integer { validity, values } => {
108                values.resize(size, 0);
109                *validity = Bitmask::with_size(size);
110            }
111            Self::Float { validity, values } => {
112                values.resize(size, 0.0);
113                *validity = Bitmask::with_size(size);
114            }
115            Self::Any(v) => {
116                v.resize(size, ResultData::None);
117            }
118        }
119    }
120
121    pub(crate) fn set(&mut self, index: usize, value: ResultData) {
122        if index >= self.len() {
123            return;
124        }
125        match self {
126            Self::Integer { validity, values } => match value {
127                ResultData::Integer(i) => {
128                    validity.set(index, true);
129                    values[index] = i;
130                }
131                ResultData::Float(f) => {
132                    self.promote_to_float();
133                    self.set(index, ResultData::Float(f));
134                }
135                ResultData::None => {
136                    validity.set(index, false);
137                    values[index] = 0;
138                }
139                _ => {
140                    self.demote_to_any();
141                    if let Self::Any(v) = self {
142                        v[index] = value;
143                    }
144                }
145            },
146            Self::Float { validity, values } => match value {
147                ResultData::Float(f) => {
148                    validity.set(index, true);
149                    values[index] = f;
150                }
151                ResultData::Integer(i) => {
152                    validity.set(index, true);
153                    values[index] = i as f64;
154                }
155                ResultData::None => {
156                    validity.set(index, false);
157                    values[index] = 0.0;
158                }
159                _ => {
160                    self.demote_to_any();
161                    if let Self::Any(v) = self {
162                        v[index] = value;
163                    }
164                }
165            },
166            Self::Any(v) => {
167                v[index] = value;
168            }
169        }
170    }
171
172    pub(crate) fn insert(&mut self, index: usize, value: ResultData) {
173        match self {
174            Self::Integer { validity, values } => match value {
175                ResultData::Integer(i) => {
176                    validity.insert(index, true);
177                    values.insert(index, i);
178                }
179                ResultData::Float(f) => {
180                    self.promote_to_float();
181                    self.insert(index, ResultData::Float(f));
182                }
183                ResultData::None => {
184                    validity.insert(index, false);
185                    values.insert(index, 0);
186                }
187                _ => {
188                    self.demote_to_any();
189                    if let Self::Any(v) = self {
190                        v.insert(index, value);
191                    }
192                }
193            },
194            Self::Float { validity, values } => match value {
195                ResultData::Float(f) => {
196                    validity.insert(index, true);
197                    values.insert(index, f);
198                }
199                ResultData::Integer(i) => {
200                    validity.insert(index, true);
201                    values.insert(index, i as f64);
202                }
203                ResultData::None => {
204                    validity.insert(index, false);
205                    values.insert(index, 0.0);
206                }
207                _ => {
208                    self.demote_to_any();
209                    if let Self::Any(v) = self {
210                        v.insert(index, value);
211                    }
212                }
213            },
214            Self::Any(v) => {
215                v.insert(index, value);
216            }
217        }
218    }
219
220    pub(crate) fn remove(&mut self, index: usize) {
221        match self {
222            Self::Integer { validity, values } => {
223                validity.remove(index);
224                values.remove(index);
225            }
226            Self::Float { validity, values } => {
227                validity.remove(index);
228                values.remove(index);
229            }
230            Self::Any(v) => {
231                v.remove(index);
232            }
233        }
234    }
235
236    pub(crate) fn drain<R: std::ops::RangeBounds<usize> + Clone>(&mut self, range: R) {
237        match self {
238            Self::Integer { validity, values } => {
239                validity.drain(range.clone());
240                values.drain(range);
241            }
242            Self::Float { validity, values } => {
243                validity.drain(range.clone());
244                values.drain(range);
245            }
246            Self::Any(v) => {
247                v.drain(range);
248            }
249        }
250    }
251}
252
253impl Default for ColumnData {
254    fn default() -> Self {
255        Self::Integer {
256            validity: Bitmask::with_size(0),
257            values: SharedVec::new(),
258        }
259    }
260}
261
262/// One column of a sheet, including raw source, values, styles, etc.
263///
264/// IMPORTANT: `src`, `data`, `cell_types`, `compiled_src` and `styles`
265/// must all stay the same length.
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct DataColumn {
268    /// Identifier, stable across renames and repositioning. Compiled formulas
269    /// refer to a column by this rather than by name or position.
270    #[serde(default = "generate_unique_id")]
271    pub id: u64,
272    /// Display name, empty unless one was set.
273    #[serde(default)]
274    pub name: String,
275    /// Excel/OpenXML column width in character units, when one was explicitly stored.
276    #[serde(default)]
277    pub width: Option<f64>,
278    /// The computed values
279    #[serde(skip, default)]
280    pub(crate) data: ColumnData,
281    /// The raw text of each cell, exactly as typed
282    pub(crate) src: SharedVec<String>,
283    /// Intrinsic cell data types, matching Excel / OpenXML representations.
284    #[serde(default)]
285    pub(crate) cell_types: SharedVec<CellType>,
286    /// Cached compile output for each cell. Rebuilt on load.
287    #[serde(skip, default)]
288    pub(crate) compiled_src: SharedVec<CompiledFormula>,
289    /// Rows awaiting recomputation. Drained by `Sheet::commit`.
290    #[serde(skip, default)]
291    pub(crate) dirty_indices: SharedVec<usize>,
292    /// Per-cell styling, `None` where a cell has none. Carries a date cell's
293    /// number format.
294    #[serde(default)]
295    pub(crate) styles: SharedVec<Option<crate::core::CellStyle>>,
296}
297
298pub(crate) struct ColumnPosition {
299    pub row: usize,
300    pub char_offset: usize,
301}
302
303impl DataColumn {
304    /// A column of `size` empty rows
305    pub fn new(size: usize) -> Self {
306        Self {
307            id: generate_unique_id(),
308            name: String::new(),
309            width: None,
310            data: ColumnData::new(size),
311            src: vec![String::new(); size].into(),
312            cell_types: vec![CellType::Empty; size].into(),
313            compiled_src: vec![CompiledFormula::default(); size].into(),
314            dirty_indices: SharedVec::new(),
315            styles: vec![None; size].into(),
316        }
317    }
318
319    /// Rows in the column
320    pub fn len(&self) -> usize {
321        self.src.len()
322    }
323
324    /// Whether the column has no rows.
325    pub fn is_empty(&self) -> bool {
326        self.src.is_empty()
327    }
328
329    /// The raw text of a cell, exactly as typed, or `None` past the end.
330    pub fn src(&self, row: usize) -> Option<&str> {
331        self.src.get(row).map(String::as_str)
332    }
333
334    /// The computed value of a cell, or `None` past the end
335    pub fn value(&self, row: usize) -> Option<ResultData> {
336        self.data.get(row)
337    }
338
339    /// The whole value column, for callers that want to work with the typed
340    /// representation rather than row by row.
341    pub fn values(&self) -> &ColumnData {
342        &self.data
343    }
344
345    /// The intrinsic data type of a cell, or `None` past the end.
346    pub fn cell_type(&self, row: usize) -> Option<CellType> {
347        self.cell_types.get(row).copied()
348    }
349
350    /// Sets the intrinsic data type of a cell at `row`.
351    pub fn set_cell_type(&mut self, row: usize, cell_type: CellType) {
352        if row < self.cell_types.len() {
353            self.cell_types[row] = cell_type;
354        }
355    }
356
357    /// A cell's compiled formula, or `None` past the end. A cell holding a
358    /// literal has an empty one rather than no entry.
359    pub fn compiled(&self, row: usize) -> Option<&CompiledFormula> {
360        self.compiled_src.get(row)
361    }
362
363    /// A cell's style, or `None` if it has none or is past the end.
364    pub fn style(&self, row: usize) -> Option<&crate::core::CellStyle> {
365        self.styles.get(row).and_then(Option::as_ref)
366    }
367
368    pub(crate) fn mark_dirty(&mut self, row: usize) {
369        if !self.dirty_indices.contains(&row) {
370            self.dirty_indices.push(row);
371        }
372    }
373
374    #[cfg(test)]
375    pub(crate) fn from_src(name: impl Into<String>, src: Vec<String>) -> Self {
376        let mut col = Self::new(src.len());
377        col.name = name.into();
378        col.src = src.into();
379        col
380    }
381
382    pub(crate) fn rebuild_after_load(&mut self) {
383        let size = self.src.len();
384        self.data.resize(size);
385        self.cell_types.resize(size, CellType::Empty);
386        self.compiled_src = vec![CompiledFormula::default(); size].into();
387        self.styles.resize(size, None);
388    }
389
390    pub(crate) fn push_row(&mut self) {
391        self.src.push(String::new());
392        self.cell_types.push(CellType::Empty);
393        self.compiled_src.push(CompiledFormula::default());
394        self.data.push(ResultData::None);
395        self.styles.push(None);
396    }
397
398    pub(crate) fn insert_row(&mut self, index: usize) {
399        if index >= self.len() {
400            self.push_row();
401            return;
402        }
403        self.src.insert(index, String::new());
404        self.cell_types.insert(index, CellType::Empty);
405        self.compiled_src.insert(index, CompiledFormula::default());
406        self.data.insert(index, ResultData::None);
407        self.styles.insert(index, None);
408        self.shift_dirty_after_insert(index, 1);
409    }
410
411    pub(crate) fn remove_row(&mut self, index: usize) {
412        if index >= self.len() {
413            return;
414        }
415        self.src.remove(index);
416        self.cell_types.remove(index);
417        self.compiled_src.remove(index);
418        self.data.remove(index);
419        self.styles.remove(index);
420        self.drop_dirty_range(index, index + 1);
421    }
422
423    pub(crate) fn drain_rows<R: std::ops::RangeBounds<usize>>(&mut self, range: R) {
424        let start = match range.start_bound() {
425            std::ops::Bound::Included(&n) => n,
426            std::ops::Bound::Excluded(&n) => n + 1,
427            std::ops::Bound::Unbounded => 0,
428        };
429        let end = match range.end_bound() {
430            std::ops::Bound::Included(&n) => n + 1,
431            std::ops::Bound::Excluded(&n) => n,
432            std::ops::Bound::Unbounded => self.len(),
433        };
434        let start = start.min(self.len());
435        let end = end.min(self.len());
436        if start >= end {
437            return;
438        }
439        self.src.drain(start..end);
440        self.cell_types.drain(start..end);
441        self.compiled_src.drain(start..end);
442        self.data.drain(start..end);
443        self.styles.drain(start..end);
444        self.drop_dirty_range(start, end);
445    }
446
447    pub(crate) fn resize_rows(&mut self, len: usize) {
448        while self.len() < len {
449            self.push_row();
450        }
451        if self.len() > len {
452            self.drain_rows(len..);
453        }
454    }
455
456    fn drop_dirty_range(&mut self, start: usize, end: usize) {
457        let removed = end - start;
458        self.dirty_indices.retain(|&i| i < start || i >= end);
459        for i in self.dirty_indices.iter_mut() {
460            if *i >= end {
461                *i -= removed;
462            }
463        }
464    }
465
466    fn shift_dirty_after_insert(&mut self, index: usize, count: usize) {
467        for i in self.dirty_indices.iter_mut() {
468            if *i >= index {
469                *i += count;
470            }
471        }
472    }
473
474    pub(crate) fn insert(&mut self, position: ColumnPosition, input: &str) {
475        let ColumnPosition { row, char_offset } = position;
476        let index = row;
477        if index < self.src.len() {
478            if self.src[index].is_empty() {
479                self.src[index].push_str(input);
480            } else {
481                self.src[index].insert_str(char_offset, input);
482            }
483        } else {
484            self.resize_rows(index + 1);
485            self.src[index] = input.to_string();
486        }
487    }
488}