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}