Skip to main content

truecalc_workbook/
error.rs

1use std::fmt;
2
3/// Errors from workbook value-type constructors and from
4/// [`Workbook::from_json`](crate::Workbook::from_json) /
5/// [`Workbook::to_json`](crate::Workbook::to_json).
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum WorkbookError {
9    /// A formula-less cell whose value is `empty` is invalid: it would be
10    /// byte-distinguishable from the absent cell it denotes, breaking
11    /// canonical uniqueness (schema spec §4). Clear a cell by removing its
12    /// entry from the sheet's cell map instead.
13    EmptyLiteral,
14    /// A document-level rule was violated while parsing or serializing — a
15    /// rule serde cannot express (schema spec §1, §2, §3, §5, §7, §8) or a
16    /// resource-limit breach (scope ADR Decision 5). Carries a human-readable
17    /// description of the first violation found.
18    Validation(String),
19    /// A sheet-management operation
20    /// ([`add_sheet`](crate::Workbook::add_sheet),
21    /// [`insert_sheet`](crate::Workbook::insert_sheet),
22    /// [`rename_sheet`](crate::Workbook::rename_sheet),
23    /// [`move_sheet`](crate::Workbook::move_sheet)) violated an invariant —
24    /// a duplicate sheet name under simple case folding (schema spec §2), a
25    /// name that is empty or exceeds the length limit (schema spec §3), the
26    /// per-workbook sheet cap (scope ADR Decision 5), an out-of-range tab
27    /// position, or an unknown sheet name. Carries a human-readable
28    /// description.
29    SheetManagement(String),
30    /// [`Workbook::set`](crate::Workbook::set) directly named a sheet, by
31    /// name, that does not exist. Distinct from [`DanglingSheetRef`] — this
32    /// is the direct-operation-target case (a caller-supplied sheet name
33    /// argument), not an indirect reference embedded inside a
34    /// named-range/table `ref` string. Carries a human-readable description.
35    ///
36    /// [`DanglingSheetRef`]: WorkbookError::DanglingSheetRef
37    UnknownSheet(String),
38    /// A named-range or table `ref` string parses fine but names a sheet the
39    /// workbook does not have (schema spec §7's dangling-ref rule, checked
40    /// eagerly at definition time rather than only at save). Distinct from
41    /// [`UnknownSheet`] — the caller here supplied a `ref` *string* to
42    /// validate, not a bare sheet-name argument. Carries a human-readable
43    /// description.
44    ///
45    /// [`UnknownSheet`]: WorkbookError::UnknownSheet
46    DanglingSheetRef(String),
47    /// A formula string failed to parse against the workbook's locked engine
48    /// ([`Workbook::set`](crate::Workbook::set)'s syntax-only validation).
49    /// Carries a human-readable description.
50    InvalidFormula(String),
51    /// A named-range/table *name* or *ref* is syntactically invalid or
52    /// non-canonical, independent of sheet existence: an invalid name shape
53    /// (must match `^[A-Za-z_][A-Za-z0-9_]*$` and not be an A1/R1C1/boolean
54    /// literal), a `ref` that fails to parse as a canonical reference, or a
55    /// `ref` that parses but is not a range where a range is required
56    /// (tables only). Carries a human-readable description.
57    InvalidReference(String),
58    /// A new named range or table's name collides, case-insensitively, with
59    /// an existing named range or table (cross-kind collisions included,
60    /// since names and tables share one case-insensitive namespace per
61    /// structured-references spec §4). Carries a human-readable description.
62    DuplicateName(String),
63    /// A table's range overlaps another table's range
64    /// (structured-references spec §4 — table ranges must be disjoint).
65    /// Distinct from [`DuplicateName`] — the name is fine, the geometry
66    /// conflicts. Carries a human-readable description.
67    ///
68    /// [`DuplicateName`]: WorkbookError::DuplicateName
69    RangeOverlap(String),
70    /// A redefine targeted a name/table that does not currently exist (the
71    /// update-only counterpart of [`DuplicateName`]'s create-time collision
72    /// check). Carries a human-readable description.
73    ///
74    /// [`DuplicateName`]: WorkbookError::DuplicateName
75    NotFound(String),
76    /// A per-mutation resource cap from scope ADR Decision 5 was exceeded:
77    /// workbook cell count, named-range count, table count, a text value's
78    /// scalar-value length, an array value's element count, or a formula's
79    /// byte length. Carries a human-readable description.
80    ResourceLimit(String),
81}
82
83impl fmt::Display for WorkbookError {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            WorkbookError::EmptyLiteral => write!(
87                f,
88                "a literal cell cannot hold an empty value; \
89                 clear a cell by removing its entry instead"
90            ),
91            WorkbookError::Validation(msg) => write!(f, "{msg}"),
92            WorkbookError::SheetManagement(msg) => write!(f, "{msg}"),
93            WorkbookError::UnknownSheet(msg) => write!(f, "{msg}"),
94            WorkbookError::DanglingSheetRef(msg) => write!(f, "{msg}"),
95            WorkbookError::InvalidFormula(msg) => write!(f, "{msg}"),
96            WorkbookError::InvalidReference(msg) => write!(f, "{msg}"),
97            WorkbookError::DuplicateName(msg) => write!(f, "{msg}"),
98            WorkbookError::RangeOverlap(msg) => write!(f, "{msg}"),
99            WorkbookError::NotFound(msg) => write!(f, "{msg}"),
100            WorkbookError::ResourceLimit(msg) => write!(f, "{msg}"),
101        }
102    }
103}
104
105impl std::error::Error for WorkbookError {}