1use crate::error::SheetsDiffError;
4
5#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
11pub enum FormulaCompareMode {
12 #[default]
14 RawText,
15 NormalizedText,
18 RawAndNormalized,
20 Ignore,
22}
23
24#[derive(Clone, Copy, PartialEq, Debug, Default)]
30pub enum NumberComparePolicy {
31 #[default]
33 Exact,
34 AbsoluteTolerance(f64),
35 RelativeTolerance(f64),
36 AbsoluteOrRelative { abs: f64, rel: f64 },
37}
38
39#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
41pub enum NumericTypePolicy {
42 #[default]
44 PreserveType,
45 CompareMathematicalValue,
47}
48
49#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
51pub enum DateComparePolicy {
52 #[default]
54 ExactRepresentation,
55 NormalizeEquivalentDateTimes,
57}
58
59#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
61pub enum TypeMismatchPolicy {
62 #[default]
64 Different,
65 CompareDisplayString,
67}
68
69#[derive(Clone, Debug, Default)]
71pub struct ValueCompareOptions {
72 pub number: NumberComparePolicy,
73 pub numeric_type: NumericTypePolicy,
74 pub date: DateComparePolicy,
75 pub type_mismatch: TypeMismatchPolicy,
76}
77
78#[derive(Clone, Debug)]
84pub struct ComparisonOptions {
85 pub value: ValueCompareOptions,
86 pub formula: FormulaCompareMode,
87 pub include_formula_cached_values: bool,
89}
90
91impl Default for ComparisonOptions {
92 fn default() -> Self {
93 Self {
94 value: ValueCompareOptions::default(),
95 formula: FormulaCompareMode::default(),
96 include_formula_cached_values: true,
97 }
98 }
99}
100
101#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
107pub enum SheetMatchingMode {
108 ExactNameOnly,
110 #[default]
113 ExactNameThenConservativeRename,
114 ExactNameThenIndex,
116}
117
118#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
120pub enum AlignmentMode {
121 #[default]
123 Positional,
124 }
126
127#[derive(Clone, Debug, Default)]
129pub struct MatchingOptions {
130 pub sheet_matching: SheetMatchingMode,
131 pub alignment: AlignmentMode,
132}
133
134#[derive(Clone, Debug, Default)]
142pub struct Limits {
143 pub max_sheets: Option<u32>,
144 pub max_cells_read: Option<u64>,
145 pub max_cells_compared: Option<u64>,
146 pub max_diffs_returned: Option<u64>,
147}
148
149#[derive(Clone, Debug)]
155pub enum DiffEvent {
156 Started,
157 OpeningWorkbook { side: crate::model::Side },
158 WorkbookOpened { side: crate::model::Side, sheet_count: usize },
159 MatchingSheets,
160 SheetStarted { index: usize, total: usize, name: String },
161 SheetFinished { index: usize, changed_cells: usize },
162 Finished,
163}
164
165pub trait ProgressSink: Send {
170 fn on_event(&mut self, event: DiffEvent);
171}
172
173impl<F: FnMut(DiffEvent) + Send> ProgressSink for F {
174 fn on_event(&mut self, event: DiffEvent) {
175 self(event);
176 }
177}
178
179pub trait Cancellation: Send + Sync {
181 fn is_cancelled(&self) -> bool;
182}
183
184impl<F: Fn() -> bool + Send + Sync> Cancellation for F {
185 fn is_cancelled(&self) -> bool {
186 self()
187 }
188}
189
190#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
192pub enum ExecutionMode {
193 #[default]
195 Sequential,
196 }
198
199pub struct ExecutionOptions {
201 pub progress: Option<Box<dyn ProgressSink>>,
202 pub cancellation: Option<Box<dyn Cancellation>>,
203 pub mode: ExecutionMode,
204}
205
206impl Default for ExecutionOptions {
207 fn default() -> Self {
208 Self {
209 progress: None,
210 cancellation: None,
211 mode: ExecutionMode::default(),
212 }
213 }
214}
215
216#[derive(Clone, Debug, Default)]
221pub struct DiagnosticOptions {
222 pub min_severity: Option<crate::model::Severity>,
224}
225
226#[derive(Clone, Debug, Default)]
231pub struct OutputOptions {
232 }
234
235pub struct DiffOptions {
243 pub comparison: ComparisonOptions,
244 pub matching: MatchingOptions,
245 pub limits: Limits,
246 pub execution: ExecutionOptions,
247 pub diagnostics: DiagnosticOptions,
248 pub output: OutputOptions,
249}
250
251impl Default for DiffOptions {
252 fn default() -> Self {
253 Self {
254 comparison: ComparisonOptions::default(),
255 matching: MatchingOptions::default(),
256 limits: Limits::default(),
257 execution: ExecutionOptions::default(),
258 diagnostics: DiagnosticOptions::default(),
259 output: OutputOptions::default(),
260 }
261 }
262}
263
264impl DiffOptions {
265 pub fn builder() -> DiffOptionsBuilder {
266 DiffOptionsBuilder::new()
267 }
268
269 pub(crate) fn validate(&self) -> Result<(), SheetsDiffError> {
271 if self.comparison.formula == FormulaCompareMode::NormalizedText
273 || self.comparison.formula == FormulaCompareMode::RawAndNormalized
274 {
275 return Err(SheetsDiffError::InvalidOptions {
276 detail: "FormulaCompareMode::NormalizedText / RawAndNormalized is not \
277 available in v2.0; no formula normaliser is implemented yet"
278 .into(),
279 });
280 }
281 Ok(())
282 }
283}
284
285#[derive(Default)]
293pub struct DiffOptionsBuilder {
294 opts: DiffOptions,
295}
296
297impl DiffOptionsBuilder {
298 pub fn new() -> Self {
299 Self { opts: DiffOptions::default() }
300 }
301
302 pub fn formula_compare(mut self, mode: FormulaCompareMode) -> Self {
305 self.opts.comparison.formula = mode;
306 self
307 }
308
309 pub fn include_formula_cached_values(mut self, yes: bool) -> Self {
310 self.opts.comparison.include_formula_cached_values = yes;
311 self
312 }
313
314 pub fn number_compare(mut self, policy: NumberComparePolicy) -> Self {
315 self.opts.comparison.value.number = policy;
316 self
317 }
318
319 pub fn numeric_type_policy(mut self, policy: NumericTypePolicy) -> Self {
320 self.opts.comparison.value.numeric_type = policy;
321 self
322 }
323
324 pub fn type_mismatch_policy(mut self, policy: TypeMismatchPolicy) -> Self {
325 self.opts.comparison.value.type_mismatch = policy;
326 self
327 }
328
329 pub fn sheet_matching(mut self, mode: SheetMatchingMode) -> Self {
332 self.opts.matching.sheet_matching = mode;
333 self
334 }
335
336 pub fn max_sheets(mut self, n: u32) -> Self {
339 self.opts.limits.max_sheets = Some(n);
340 self
341 }
342
343 pub fn max_cells_compared(mut self, n: u64) -> Self {
344 self.opts.limits.max_cells_compared = Some(n);
345 self
346 }
347
348 pub fn max_diffs_returned(mut self, n: u64) -> Self {
349 self.opts.limits.max_diffs_returned = Some(n);
350 self
351 }
352
353 pub fn progress<S: ProgressSink + 'static>(mut self, sink: S) -> Self {
356 self.opts.execution.progress = Some(Box::new(sink));
357 self
358 }
359
360 pub fn cancellation<C: Cancellation + 'static>(mut self, token: C) -> Self {
361 self.opts.execution.cancellation = Some(Box::new(token));
362 self
363 }
364
365 pub fn build(self) -> Result<DiffOptions, SheetsDiffError> {
367 self.opts.validate()?;
368 Ok(self.opts)
369 }
370}