sheets-diff 2.4.0

Structured diff engine for Microsoft Excel .xlsx workbooks
Documentation
# RFC-022 — Styles and Formatting Diff Policy

**Status.** Accepted — design settled; implementation incomplete as of 2.2.3. See ../README.md.
**Target:** v2.1 candidate  
**Related:** RFC-020, RFC-023, RFC-029

## 1. Summary

Define an optional formatting diff layer for cell styles, number formats,
fill/border/font attributes, alignment, and related visual metadata. This RFC
keeps formatting out of the default v2.0 value-diff path while reserving a clean
extension model.

## 2. Motivation

Some spreadsheet diffs care only about data. Others care about review/audit
changes where formatting is meaningful: red cells, locked cells, hidden rows,
number format changes, or conditional formatting. If formatting is mixed into
ordinary `CellDiff` by default, the output becomes noisy. If formatting is not
modeled at all, later additions may break the API.

## 3. Goals

- Provide an optional style diff model.
- Keep value/formula comparison independent from formatting by default.
- Make formatting support partial and diagnostic-aware.
- Avoid promising Excel-perfect visual rendering.

## 4. Non-goals

- Pixel rendering.
- Conditional formatting formula evaluation.
- Theme resolution identical to Excel.
- Merge/write support.

## 5. Public model

```rust
pub struct CellDiff {
    pub address: CellAddress,
    pub value: Option<ValueChange>,
    pub formula: Option<FormulaChange>,
    pub format: Option<FormatChange>,
    pub notes: Vec<CellDiagnostic>,
}

pub struct FormatChange {
    pub old: Option<CellFormatSnapshot>,
    pub new: Option<CellFormatSnapshot>,
    pub changed_fields: Vec<FormatField>,
}

pub struct CellFormatSnapshot {
    pub number_format: Option<CellNumberFormat>,
    pub font: Option<FontFormat>,
    pub fill: Option<FillFormat>,
    pub border: Option<BorderFormat>,
    pub alignment: Option<AlignmentFormat>,
    pub protection: Option<ProtectionFormat>,
}
```

All fields are optional because reader support may be incomplete.

## 6. Options

```rust
pub enum FormatCompareMode {
    Ignore,
    NumberFormatOnly,
    BasicStyle,
    AllAvailable,
}
```

Default: `Ignore`.

`NumberFormatOnly` may be implemented earlier because it has direct interaction
with display and typed value interpretation. `AllAvailable` is best-effort and
must attach diagnostics for unsupported style categories.

## 7. Internal design

Style snapshots should be interned to reduce memory use:

```rust
struct StyleTable {
    formats: Vec<CellFormatSnapshot>,
    ids: HashMap<CellFormatSnapshot, StyleId>,
}

struct NormalizedCell {
    coord: Coord,
    value: CellValue,
    formula: Option<String>,
    style: Option<StyleId>,
}
```

Comparing style IDs is cheap. Detailed field differences are computed only when
style IDs differ and formatting comparison is enabled.

## 8. Conditional formatting

Conditional formatting should not be evaluated. A future object-diff layer may
report that conditional formatting rules changed, but cell-level format changes
that depend on evaluated conditions are out of scope.

## 9. UX guidance

Formatting diffs should be displayed separately or behind a toggle in GUI tools.
A default table that mixes value and style changes can become too noisy.

## 10. Acceptance criteria

- Default v2 output is unchanged by style-only changes.
- With `NumberFormatOnly`, a number-format change is reported without pretending
  the cell value changed.
- Style support is clearly documented as best-effort.
- Unsupported style features produce diagnostics when style comparison is
  requested.