Skip to main content

sheets_diff/
lib.rs

1//! # sheets-diff
2//!
3//! Structured diff engine for Microsoft Excel `.xlsx` workbooks.
4//!
5//! ## Quick start
6//!
7//! ```rust,no_run
8//! use sheets_diff::compare_paths;
9//!
10//! let diff = compare_paths("old.xlsx", "new.xlsx")?;
11//! println!("changed cells: {}", diff.summary.cells_changed);
12//! # Ok::<(), sheets_diff::SheetsDiffError>(())
13//! ```
14//!
15//! ## Input sources
16//!
17//! | Function | When to use |
18//! |---|---|
19//! | [`compare_paths`] | Simplest; caller provides file paths |
20//! | [`compare_bytes`] | You already have the bytes (e.g. from a cache or repo) |
21//! | [`compare_readers`] | You have open `Read + Seek` handles |
22//! | `compare_*_with_options` variants | Any of the above plus [`DiffOptions`] |
23//!
24//! See [`DiffOptions`] / [`DiffOptionsBuilder`] for all configuration knobs.
25
26// ---------------------------------------------------------------------------
27// Internal modules (not pub)
28// ---------------------------------------------------------------------------
29
30pub mod address;
31mod align;
32mod diff;
33mod error;
34mod matcher;
35mod meta;
36mod objects;
37mod normalize;
38mod open;
39
40pub(crate) mod compare;
41
42// ---------------------------------------------------------------------------
43// Public modules
44// ---------------------------------------------------------------------------
45
46/// Typed result model (`WorkbookDiff`, `SheetDiff`, `CellDiff`, `CellValue`, …).
47pub mod model;
48
49/// Comparison options and builder (`DiffOptions`, `DiffOptionsBuilder`, …).
50pub mod options;
51
52/// Output formatters (text summary, unified diff).
53pub mod output;
54
55// ---------------------------------------------------------------------------
56// Re-exports — the stable public API surface (RFC-002, RFC-031)
57// ---------------------------------------------------------------------------
58
59// Error types
60pub use error::{LimitKind, OpenErrorKind, ReadErrorKind, SheetsDiffError};
61
62// Model
63pub use model::{
64    AlignmentSummary,
65    DiffMetrics,
66    CellChangeKind,
67    CellDateTime,
68    CellDiff,
69    CellDisplay,
70    CellDuration,
71    CellError,
72    CellNumberFormat,
73    CellSnapshot,
74    CellValue,
75    DateTimeKind,
76    DiagnosticKind,
77    DiagnosticLocation,
78    DiagnosticSummary,
79    Diagnostic,
80    DiffStage,
81    DiffSummary,
82    DisplaySource,
83    FormatChange,
84    FormulaChange,
85    FormulaText,
86    MatchConfidence,
87    Severity,
88    SheetChange,
89    SheetDiff,
90    SheetMatchReason,
91    SheetRef,
92    SheetSummary,
93    Side,
94    SourceDescription,
95    SourceKind,
96    ValueChange,
97    ValueDifferenceKind,
98    WorkbookChange,
99    WorkbookDiff,
100    WorkbookObjectChange,
101    WorkbookSideInfo,
102};
103
104// Address
105pub use address::{CellAddress, ComparedRange, MAX_COL, MAX_COL_LABEL, MAX_ROW};
106
107// Options
108pub use objects::ObjectCompareMode;
109pub use options::{
110    AlignmentMode,
111    Cancellation,
112    ComparisonOptions,
113    DateComparePolicy,
114    DiagnosticOptions,
115    DiffEvent,
116    DiffOptions,
117    DiffOptionsBuilder,
118    ExecutionMode,
119    ExecutionOptions,
120    FormatCompareMode,
121    FormulaCompareMode,
122    Limits,
123    MatchingOptions,
124    NumberComparePolicy,
125    NumericTypePolicy,
126    OutputOptions,
127    ProgressSink,
128    SheetMatchingMode,
129    TypeMismatchPolicy,
130    ValueCompareOptions,
131};
132
133// ---------------------------------------------------------------------------
134// Public entry points (RFC-033 §12)
135// ---------------------------------------------------------------------------
136
137use std::io::{Read, Seek};
138use std::path::Path;
139
140/// Compare two workbooks given their filesystem paths.
141///
142/// Uses [`DiffOptions::default()`].
143/// Compare two workbooks given their filesystem paths.
144///
145/// # Path handling
146///
147/// `old` and `new` accept any `AsRef<Path>`, and the raw `Path` is passed to
148/// `std::fs::read` unchanged — there is **no internal `to_str()`/`unwrap()` on
149/// the path**, so non-UTF-8 paths (common on Linux) are fully supported and
150/// never cause a panic. The only UTF-8-dependent step is the cosmetic
151/// `SourceDescription.display_name`, which is set to `None` for a non-UTF-8
152/// file name rather than failing.
153pub fn compare_paths(
154    old: impl AsRef<Path>,
155    new: impl AsRef<Path>,
156) -> Result<WorkbookDiff, SheetsDiffError> {
157    diff::run_compare_paths(old, new, DiffOptions::default())
158}
159
160/// Compare two workbooks given their filesystem paths, with explicit options.
161pub fn compare_paths_with_options(
162    old: impl AsRef<Path>,
163    new: impl AsRef<Path>,
164    opts: DiffOptions,
165) -> Result<WorkbookDiff, SheetsDiffError> {
166    diff::run_compare_paths(old, new, opts)
167}
168
169/// Compare two workbooks given byte slices.
170pub fn compare_bytes(
171    old: impl AsRef<[u8]>,
172    new: impl AsRef<[u8]>,
173) -> Result<WorkbookDiff, SheetsDiffError> {
174    diff::run_compare_bytes(old, new, DiffOptions::default())
175}
176
177/// Compare two workbooks given byte slices, with explicit options.
178pub fn compare_bytes_with_options(
179    old: impl AsRef<[u8]>,
180    new: impl AsRef<[u8]>,
181    opts: DiffOptions,
182) -> Result<WorkbookDiff, SheetsDiffError> {
183    diff::run_compare_bytes(old, new, opts)
184}
185
186/// Compare two workbooks given `Read + Seek` readers.
187///
188/// `.xlsx` is ZIP-based and requires seeking.
189pub fn compare_readers<R1, R2>(
190    old: R1,
191    new: R2,
192) -> Result<WorkbookDiff, SheetsDiffError>
193where
194    R1: Read + Seek,
195    R2: Read + Seek,
196{
197    diff::run_compare_readers(old, new, DiffOptions::default())
198}
199
200/// Compare two workbooks given `Read + Seek` readers, with explicit options.
201pub fn compare_readers_with_options<R1, R2>(
202    old: R1,
203    new: R2,
204    opts: DiffOptions,
205) -> Result<WorkbookDiff, SheetsDiffError>
206where
207    R1: Read + Seek,
208    R2: Read + Seek,
209{
210    diff::run_compare_readers(old, new, opts)
211}