1use std::fmt;
4
5use crate::model::{SheetRef, Side, SourceDescription};
6
7#[non_exhaustive]
13#[derive(Debug)]
14pub enum OpenErrorKind {
15 NotFound,
16 PermissionDenied,
17 NotXlsx,
19 Corrupt,
21 Locked,
23 Other,
24}
25
26impl fmt::Display for OpenErrorKind {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 OpenErrorKind::NotFound => f.write_str("file not found"),
30 OpenErrorKind::PermissionDenied => f.write_str("permission denied"),
31 OpenErrorKind::NotXlsx => f.write_str("not an xlsx file"),
32 OpenErrorKind::Corrupt => f.write_str("file is corrupt"),
33 OpenErrorKind::Locked => f.write_str("file is locked"),
34 OpenErrorKind::Other => f.write_str("open failed"),
35 }
36 }
37}
38
39#[non_exhaustive]
41#[derive(Debug)]
42pub enum ReadErrorKind {
43 SheetNotFound,
53 MalformedSheet,
55 Other,
64}
65
66impl fmt::Display for ReadErrorKind {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 ReadErrorKind::SheetNotFound => f.write_str("sheet not found"),
70 ReadErrorKind::MalformedSheet => f.write_str("sheet is malformed"),
71 ReadErrorKind::Other => f.write_str("read failed"),
72 }
73 }
74}
75
76#[derive(Clone, Copy, PartialEq, Eq, Debug)]
82pub enum LimitKind {
83 Sheets,
84 CellsRead,
85 CellsCompared,
86 DiffsReturned,
87 InputBytes,
89}
90
91impl fmt::Display for LimitKind {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 LimitKind::Sheets => f.write_str("max_sheets"),
95 LimitKind::CellsRead => f.write_str("max_cells_read"),
96 LimitKind::CellsCompared => f.write_str("max_cells_compared"),
97 LimitKind::DiffsReturned => f.write_str("max_diffs_returned"),
98 LimitKind::InputBytes => f.write_str("max_input_bytes"),
99 }
100 }
101}
102
103pub struct CalamiLineError(pub(crate) calamine::XlsxError);
111
112impl fmt::Debug for CalamiLineError {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "calamine error: {}", self.0)
115 }
116}
117impl fmt::Display for CalamiLineError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(f, "{}", self.0)
120 }
121}
122impl std::error::Error for CalamiLineError {}
123
124#[non_exhaustive]
133#[derive(Debug)]
134pub enum SheetsDiffError {
135 OpenWorkbook {
137 side: Side,
138 source: SourceDescription,
139 kind: OpenErrorKind,
140 inner: Option<Box<CalamiLineError>>,
142 },
143 ReadSheet {
145 side: Side,
146 sheet: SheetRef,
147 kind: ReadErrorKind,
148 inner: Option<Box<CalamiLineError>>,
149 },
150 UnsupportedFormat { side: Side, detail: String },
152 EncryptedWorkbook { side: Side },
154 InvalidOptions { detail: String },
156 Cancelled,
158 LimitExceeded { limit: LimitKind, observed: u64 },
160 Internal { detail: String },
162}
163
164impl fmt::Display for SheetsDiffError {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 SheetsDiffError::OpenWorkbook {
168 side, source, kind, ..
169 } => {
170 let name = source.display_name.as_deref().unwrap_or("<unknown>");
171 write!(f, "cannot open {side} workbook '{name}': {kind}")
172 }
173 SheetsDiffError::ReadSheet {
174 side, sheet, kind, ..
175 } => {
176 write!(
177 f,
178 "cannot read sheet '{}' from {side} workbook: {kind}",
179 sheet.name
180 )
181 }
182 SheetsDiffError::UnsupportedFormat { side, detail } => {
183 write!(
184 f,
185 "{side} workbook is not a supported xlsx format: {detail}"
186 )
187 }
188 SheetsDiffError::EncryptedWorkbook { side } => {
189 write!(f, "{side} workbook is password-protected")
190 }
191 SheetsDiffError::InvalidOptions { detail } => {
192 write!(f, "invalid options: {detail}")
193 }
194 SheetsDiffError::Cancelled => f.write_str("comparison was cancelled"),
195 SheetsDiffError::LimitExceeded { limit, observed } => {
196 write!(f, "limit '{limit}' exceeded (observed {observed})")
197 }
198 SheetsDiffError::Internal { detail } => {
199 write!(f, "internal error: {detail}")
200 }
201 }
202 }
203}
204
205impl std::error::Error for SheetsDiffError {
206 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
207 match self {
208 SheetsDiffError::OpenWorkbook { inner, .. } => {
209 inner.as_deref().map(|e| e as &dyn std::error::Error)
210 }
211 SheetsDiffError::ReadSheet { inner, .. } => {
212 inner.as_deref().map(|e| e as &dyn std::error::Error)
213 }
214 _ => None,
215 }
216 }
217}
218
219impl SheetsDiffError {
224 pub(crate) fn open_workbook(
225 side: Side,
226 source: SourceDescription,
227 calamine_err: calamine::XlsxError,
228 ) -> Self {
229 let kind = classify_open_error(&calamine_err);
230 SheetsDiffError::OpenWorkbook {
231 side,
232 source,
233 kind,
234 inner: Some(Box::new(CalamiLineError(calamine_err))),
235 }
236 }
237
238 pub(crate) fn read_sheet(
239 side: Side,
240 sheet: SheetRef,
241 calamine_err: calamine::XlsxError,
242 ) -> Self {
243 let kind = classify_read_error(&calamine_err);
244 SheetsDiffError::ReadSheet {
245 side,
246 sheet,
247 kind,
248 inner: Some(Box::new(CalamiLineError(calamine_err))),
249 }
250 }
251}
252
253fn classify_open_error(e: &calamine::XlsxError) -> OpenErrorKind {
254 use calamine::XlsxError;
255 match e {
256 XlsxError::Password => OpenErrorKind::NotXlsx, XlsxError::FileNotFound(_) => OpenErrorKind::NotFound,
258 XlsxError::Io(io) => match io.kind() {
259 std::io::ErrorKind::NotFound => OpenErrorKind::NotFound,
260 std::io::ErrorKind::PermissionDenied => OpenErrorKind::PermissionDenied,
261 _ => OpenErrorKind::Other,
262 },
263 XlsxError::Zip(_) => OpenErrorKind::NotXlsx,
264 _ => OpenErrorKind::Corrupt,
265 }
266}
267
268fn classify_read_error(e: &calamine::XlsxError) -> ReadErrorKind {
269 use calamine::XlsxError;
270 match e {
271 XlsxError::WorksheetNotFound(_) => ReadErrorKind::SheetNotFound,
272 XlsxError::Io(_) => ReadErrorKind::Other,
276 _ => ReadErrorKind::MalformedSheet,
277 }
278}
279
280pub(crate) fn from_open_error(
283 side: Side,
284 source: SourceDescription,
285 e: calamine::XlsxError,
286) -> SheetsDiffError {
287 if matches!(e, calamine::XlsxError::Password) {
288 SheetsDiffError::EncryptedWorkbook { side }
289 } else {
290 SheetsDiffError::open_workbook(side, source, e)
291 }
292}