Skip to main content

truecalc_workbook/
cell.rs

1use serde::{Deserialize, Deserializer, Serialize};
2
3use crate::error::WorkbookError;
4use crate::value::Value;
5
6/// An authored cell: a literal (`value` only) or a formula
7/// (`formula` + `value`). Schema spec §4.
8///
9/// `value` is required on every cell, including formula cells — a
10/// never-evaluated formula cell carries [`Value::Empty`] until first recalc.
11/// A formula-less cell whose value is empty is invalid; construction and
12/// deserialization both reject it (see [`Cell::literal`]).
13#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
14#[serde(try_from = "CellDe")]
15pub struct Cell {
16    #[serde(skip_serializing_if = "Option::is_none")]
17    formula: Option<String>,
18    value: Value,
19}
20
21impl Cell {
22    /// Creates a literal cell.
23    ///
24    /// Rejects [`Value::Empty`]: an empty literal would be
25    /// byte-distinguishable from the absent cell it denotes (schema spec §4).
26    /// Clear a cell by removing its entry from the sheet's cell map instead.
27    pub fn literal(value: Value) -> Result<Self, WorkbookError> {
28        if matches!(value, Value::Empty) {
29            return Err(WorkbookError::EmptyLiteral);
30        }
31        Ok(Self {
32            formula: None,
33            value,
34        })
35    }
36
37    /// Creates a formula cell.
38    ///
39    /// `formula` is the verbatim authored text including the leading `=` —
40    /// it is never normalized and round-trips byte-exact. `value` is the
41    /// most recent evaluated result ([`Value::Empty`] until first recalc).
42    pub fn with_formula(formula: impl Into<String>, value: Value) -> Self {
43        Self {
44            formula: Some(formula.into()),
45            value,
46        }
47    }
48
49    /// The most recent evaluated result.
50    pub fn value(&self) -> &Value {
51        &self.value
52    }
53
54    /// The verbatim authored formula text, if this is a formula cell.
55    pub fn formula(&self) -> Option<&str> {
56        self.formula.as_deref()
57    }
58
59    /// Replaces the authored formula text, leaving `value` alone.
60    ///
61    /// Crate-internal, and deliberately not a public setter: the one caller is
62    /// [`Workbook::rename_sheet`](crate::Workbook::rename_sheet)'s reference
63    /// rewrite, which substitutes a new sheet qualifier into text the engine
64    /// re-rendered from the same parse. A public setter would let a caller
65    /// swap formula text without invalidating the dependency-graph cache.
66    pub(crate) fn set_formula(&mut self, formula: String) {
67        self.formula = Some(formula);
68    }
69}
70
71/// Shadow struct for deserialization: rejects unknown fields (including the
72/// reserved `format` and `comment`, schema spec §4 and §9) before the
73/// empty-literal invariant is checked in `TryFrom`.
74#[derive(Deserialize)]
75#[serde(deny_unknown_fields)]
76struct CellDe {
77    #[serde(default, deserialize_with = "de_formula")]
78    formula: Option<String>,
79    value: Value,
80}
81
82/// `formula`, when present, must be a JSON string (schema spec §4); an
83/// explicit `"formula": null` is not schema-valid and is rejected rather
84/// than treated as an absent field.
85fn de_formula<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<String>, D::Error> {
86    String::deserialize(deserializer).map(Some)
87}
88
89impl TryFrom<CellDe> for Cell {
90    type Error = WorkbookError;
91
92    fn try_from(de: CellDe) -> Result<Self, Self::Error> {
93        if de.formula.is_none() && matches!(de.value, Value::Empty) {
94            return Err(WorkbookError::EmptyLiteral);
95        }
96        Ok(Self {
97            formula: de.formula,
98            value: de.value,
99        })
100    }
101}