sheets_diff/objects.rs
1//! Non-cell workbook object detection and coverage diagnostics (RFC-023).
2//!
3//! calamine 0.35 does not expose object content (charts, images, comments,
4//! tables, pivot tables, hyperlinks, or data validation) through its public
5//! API. What it does expose is:
6//! - `Sheet.typ: SheetType` — distinguishes WorkSheet, ChartSheet, MacroSheet, Vba
7//! - `Sheet.visible: SheetVisible`
8//!
9//! The policy for v2.2 is `WarnIfPresent` for non-worksheet sheet types and
10//! a single coverage diagnostic explaining what is NOT compared. This prevents
11//! a misleading "no differences" result when meaningful objects are present.
12
13use calamine::{Reader, SheetType};
14
15use crate::model::{
16 Diagnostic, DiagnosticKind, DiagnosticLocation, DiffStage, Severity,
17};
18use crate::open::OpenedWorkbook;
19
20// ---------------------------------------------------------------------------
21// ObjectCompareMode (RFC-023 §6)
22// ---------------------------------------------------------------------------
23
24/// Controls how the presence of non-cell objects is handled.
25#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
26pub enum ObjectCompareMode {
27 /// Ignore objects entirely — no diagnostics.
28 Ignore,
29 /// Emit a coverage warning when non-worksheet sheets or any object
30 /// categories that cannot be compared are detected. Default.
31 #[default]
32 WarnIfPresent,
33 /// Compare what is available; emit diagnostics for the rest.
34 /// In v2.2 this behaves identically to `WarnIfPresent` because no
35 /// object content API is available in calamine 0.35.
36 CompareAvailable,
37}
38
39// ---------------------------------------------------------------------------
40// Public entry point
41// ---------------------------------------------------------------------------
42
43/// Detect non-cell objects on both workbook sides and emit coverage diagnostics.
44pub fn report_object_coverage(
45 old_wb: &mut OpenedWorkbook,
46 new_wb: &mut OpenedWorkbook,
47 mode: ObjectCompareMode,
48 diagnostics: &mut Vec<Diagnostic>,
49) {
50 if mode == ObjectCompareMode::Ignore {
51 return;
52 }
53
54 detect_non_worksheet_sheets(old_wb, diagnostics);
55 detect_non_worksheet_sheets(new_wb, diagnostics);
56
57 // Emit a single blanket coverage note so consumers know what was NOT compared.
58 emit_coverage_note(diagnostics);
59}
60
61// ---------------------------------------------------------------------------
62// Non-worksheet sheet detection
63// ---------------------------------------------------------------------------
64
65fn detect_non_worksheet_sheets(wb: &mut OpenedWorkbook, diagnostics: &mut Vec<Diagnostic>) {
66 for (index, sheet) in wb.reader.sheets_metadata().iter().enumerate() {
67 let kind = match sheet.typ {
68 SheetType::ChartSheet => Some("chart sheet"),
69 SheetType::MacroSheet => Some("macro sheet"),
70 SheetType::Vba => Some("VBA module"),
71 SheetType::DialogSheet => Some("dialog sheet"),
72 SheetType::WorkSheet => None, // ordinary — no warning needed
73 };
74 if let Some(kind_label) = kind {
75 diagnostics.push(Diagnostic {
76 severity: Severity::Warning,
77 kind: DiagnosticKind::UnsupportedWorkbookFeature {
78 feature: kind_label.to_owned(),
79 },
80 location: DiagnosticLocation {
81 stage: DiffStage::Metadata,
82 sheet_order: Some(index),
83 sheet_name: Some(sheet.name.clone()),
84 address: None,
85 },
86 message: format!(
87 "sheet '{}' is a {} — content not compared \
88 (calamine 0.35 does not expose {} data)",
89 sheet.name, kind_label, kind_label
90 ),
91 });
92 }
93 }
94}
95
96// ---------------------------------------------------------------------------
97// Blanket coverage note
98// ---------------------------------------------------------------------------
99
100fn emit_coverage_note(diagnostics: &mut Vec<Diagnostic>) {
101 diagnostics.push(Diagnostic {
102 severity: Severity::Info,
103 kind: DiagnosticKind::UnsupportedWorkbookFeature {
104 feature: "non-cell objects".to_owned(),
105 },
106 location: DiagnosticLocation {
107 stage: DiffStage::Metadata,
108 sheet_order: None,
109 sheet_name: None,
110 address: None,
111 },
112 message: "charts, images, comments, hyperlinks, tables, pivot tables, \
113 data validation, and conditional formatting are not compared \
114 in this version (calamine 0.35 does not expose object content)"
115 .into(),
116 });
117}