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}