# truecalc-workbook
[](https://crates.io/crates/truecalc-workbook)
[](https://docs.rs/truecalc-workbook)
[](../../LICENSE)
Workbook layer for the [truecalc](https://github.com/truecalc/core) spreadsheet engine: engine-locked workbook, worksheet, and cell value types with a canonical JSON serialization contract.
## Install
```toml
[dependencies]
truecalc-workbook = "0.9"
```
Or via cargo:
```sh
cargo add truecalc-workbook
```
## Quick start
```rust
use truecalc_workbook::{Address, CellInput, EngineFlavor, RecalcContext, Value, Workbook, Worksheet};
// Create a workbook locked to Google Sheets semantics.
let mut wb = Workbook::new(EngineFlavor::Sheets);
// Add a sheet and write some cells.
wb.add_sheet(Worksheet::new("Budget")).unwrap();
let a1 = Address::from_a1("A1").unwrap();
let a2 = Address::from_a1("A2").unwrap();
let a3 = Address::from_a1("A3").unwrap();
wb.set("Budget", a1, CellInput::Literal(Value::Number(1000.0))).unwrap();
wb.set("Budget", a2, CellInput::Literal(Value::Number(500.0))).unwrap();
wb.set("Budget", a3, CellInput::Formula("=SUM(A1:A2)".to_string())).unwrap();
// Recalculate to evaluate all formulas.
// RecalcContext::new(unix_ms, iana_tz, rng_seed)
let ctx = RecalcContext::new(1_780_000_000_000, "Etc/GMT", 0).unwrap();
let _changes = wb.recalc(&ctx);
// Read back the computed result.
assert_eq!(wb.get("Budget", a3).unwrap().value(), &Value::Number(1500.0));
```
## Design
- A `Workbook` is a **value object** — `Clone + PartialEq + Hash`, no hidden state, no
callbacks. Mutate via [`Workbook::set`] / [`Workbook::clear`], then drive recalc.
- **[`RecalcContext`]** pins volatile functions (`NOW`, `TODAY`) to a fixed UTC instant +
IANA timezone via the vendored `chrono-tz` database (not the host OS tz tables).
Same workbook + same context ⇒ byte-identical recomputed grid.
- **[`CellInput`]** distinguishes `Literal(value)` from `Formula("=...".to_string())`.
Formula syntax is validated against the locked engine at `set` time.
## Recalc modes
- [`Workbook::recalc`] — full recalc, evaluates every formula cell in topological order.
- [`Workbook::recalc_incremental`] — incremental recalc, recomputes only the transitive
dependents of the edited cells (plus all volatile cells). Produces the same result as
full recalc.
Both return an ordered list of [`Change`] values describing every cell that changed.
## JSON serialization
[`Workbook::to_json`] / [`Workbook::from_json`] implement the canonical RFC 8785 / JCS
serialization boundary — byte-identical output across Rust, WASM, MCP, and REST surfaces.
The JSON schema is the cross-surface contract; see `schema/` for the JSON Schema spec.
### Schema summary
```json
{
"version": "1",
"engine": "sheets",
"names": [],
"sheets": [
{
"name": "Budget",
"cells": {
"A1": { "value": 1000.0 },
"A3": { "formula": "=SUM(A1:A2)", "value": 1500.0 }
}
}
]
}
```
Key schema invariants:
- `engine` is `"sheets"` or `"excel"` — required, immutable.
- `version` is the string `"1"` — compared by exact match.
- Cells without a formula have only `value`; formula cells store `formula` + last `value`.
- Named ranges are validated against existing sheets at deserialize time.
## Cookbook example
See [`examples/workbook-budget/`](../../examples/workbook-budget/) for a worked example
that creates a budget workbook, sets income and expense formulas, recalcs, and prints the
results.
## Related crates
- [`truecalc-core`](https://crates.io/crates/truecalc-core): the formula parser and evaluator.
## License
MIT, same as the rest of this workspace.