Skip to main content

truecalc_workbook/
worksheet.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::address::Address;
6use crate::cell::Cell;
7
8/// A named sheet holding a sparse cell grid (schema spec §3).
9///
10/// Cell keys are plain uppercase A1 addresses (`A1`, `BC42`) — no `$`, no
11/// sheet qualifier, no lowercase. Absent addresses are empty cells and are
12/// never serialized. The map is ordered (`BTreeMap`), which matches the
13/// canonical (JCS) key order for ASCII A1 addresses: `A10` sorts before `A2`.
14///
15/// The typed grid API ([`get`](Self::get) / [`set`](Self::set) /
16/// [`clear`](Self::clear), keyed by a bounds-validated [`Address`]) is the
17/// in-memory accessor used by the recalc runtime (plan item 3.1); the raw
18/// [`cells`](Self::cells) map is retained for the serde layer. Address-syntax
19/// and sheet-name validation against the schema's normative rules also happens
20/// at the serialization boundary ([`Workbook::from_json`](crate::Workbook::from_json)),
21/// independently of the typed API, since `from_json` parses untrusted keys.
22#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
23#[serde(deny_unknown_fields)]
24pub struct Worksheet {
25    cells: BTreeMap<String, Cell>,
26    name: String,
27}
28
29impl Worksheet {
30    /// Creates an empty worksheet named `name`.
31    pub fn new(name: impl Into<String>) -> Self {
32        Self {
33            cells: BTreeMap::new(),
34            name: name.into(),
35        }
36    }
37
38    /// The sheet name, unique within a workbook (case-insensitive).
39    pub fn name(&self) -> &str {
40        &self.name
41    }
42
43    /// Sets the sheet name. Crate-internal: uniqueness and length validation is
44    /// the workbook's job ([`Workbook::rename_sheet`](crate::Workbook::rename_sheet)),
45    /// the only public path to a rename.
46    pub(crate) fn set_name(&mut self, name: impl Into<String>) {
47        self.name = name.into();
48    }
49
50    /// The sparse cell grid, keyed by plain uppercase A1 address.
51    pub fn cells(&self) -> &BTreeMap<String, Cell> {
52        &self.cells
53    }
54
55    /// Mutable access to the sparse cell grid.
56    pub fn cells_mut(&mut self) -> &mut BTreeMap<String, Cell> {
57        &mut self.cells
58    }
59
60    /// The cell at `addr`, or `None` if that address is empty.
61    pub fn get(&self, addr: Address) -> Option<&Cell> {
62        self.cells.get(&addr.to_a1())
63    }
64
65    /// Mutable access to the cell at `addr`, or `None` if that address is empty.
66    pub fn get_mut(&mut self, addr: Address) -> Option<&mut Cell> {
67        self.cells.get_mut(&addr.to_a1())
68    }
69
70    /// Writes `cell` at `addr`, returning the cell previously there (if any).
71    ///
72    /// `addr` is already bounds-validated by construction, so a set never
73    /// escapes the address bounds. To clear a cell use [`clear`](Self::clear),
74    /// never a set of an empty value (schema spec §4).
75    pub fn set(&mut self, addr: Address, cell: Cell) -> Option<Cell> {
76        self.cells.insert(addr.to_a1(), cell)
77    }
78
79    /// Removes the cell at `addr`, returning it if present.
80    ///
81    /// Clearing a cell is *removing its entry*, never writing an empty value:
82    /// an empty entry would be byte-distinguishable from the absent cell it
83    /// denotes (schema spec §4).
84    pub fn clear(&mut self, addr: Address) -> Option<Cell> {
85        self.cells.remove(&addr.to_a1())
86    }
87
88    /// Whether a cell is present at `addr`.
89    pub fn contains(&self, addr: Address) -> bool {
90        self.cells.contains_key(&addr.to_a1())
91    }
92
93    /// The number of populated cells.
94    pub fn len(&self) -> usize {
95        self.cells.len()
96    }
97
98    /// Whether the grid has no populated cells.
99    pub fn is_empty(&self) -> bool {
100        self.cells.is_empty()
101    }
102
103    /// Iterates the populated cells in canonical (JCS) key order, yielding the
104    /// parsed [`Address`] for each. Keys held by a worksheet are always valid
105    /// A1, so parsing cannot fail.
106    pub fn iter(&self) -> impl Iterator<Item = (Address, &Cell)> {
107        self.cells.iter().map(|(key, cell)| {
108            let addr = Address::from_a1(key).expect("worksheet keys are always valid A1");
109            (addr, cell)
110        })
111    }
112}