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,
44 MalformedSheet,
45 Other,
46}
47
48impl fmt::Display for ReadErrorKind {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 ReadErrorKind::SheetNotFound => f.write_str("sheet not found"),
52 ReadErrorKind::MalformedSheet => f.write_str("sheet is malformed"),
53 ReadErrorKind::Other => f.write_str("read failed"),
54 }
55 }
56}
57
58#[derive(Clone, Copy, PartialEq, Eq, Debug)]
64pub enum LimitKind {
65 Sheets,
66 CellsRead,
67 CellsCompared,
68 DiffsReturned,
69 InputBytes,
71}
72
73impl fmt::Display for LimitKind {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 LimitKind::Sheets => f.write_str("max_sheets"),
77 LimitKind::CellsRead => f.write_str("max_cells_read"),
78 LimitKind::CellsCompared => f.write_str("max_cells_compared"),
79 LimitKind::DiffsReturned => f.write_str("max_diffs_returned"),
80 LimitKind::InputBytes => f.write_str("max_input_bytes"),
81 }
82 }
83}
84
85pub struct CalamiLineError(pub(crate) calamine::XlsxError);
93
94impl fmt::Debug for CalamiLineError {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 write!(f, "calamine error: {}", self.0)
97 }
98}
99impl fmt::Display for CalamiLineError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "{}", self.0)
102 }
103}
104impl std::error::Error for CalamiLineError {}
105
106#[non_exhaustive]
115#[derive(Debug)]
116pub enum SheetsDiffError {
117 OpenWorkbook {
119 side: Side,
120 source: SourceDescription,
121 kind: OpenErrorKind,
122 inner: Option<Box<CalamiLineError>>,
124 },
125 ReadSheet {
127 side: Side,
128 sheet: SheetRef,
129 kind: ReadErrorKind,
130 inner: Option<Box<CalamiLineError>>,
131 },
132 UnsupportedFormat { side: Side, detail: String },
134 EncryptedWorkbook { side: Side },
136 InvalidOptions { detail: String },
138 Cancelled,
140 LimitExceeded { limit: LimitKind, observed: u64 },
142 Internal { detail: String },
144}
145
146impl fmt::Display for SheetsDiffError {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 match self {
149 SheetsDiffError::OpenWorkbook {
150 side, source, kind, ..
151 } => {
152 let name = source.display_name.as_deref().unwrap_or("<unknown>");
153 write!(f, "cannot open {side} workbook '{name}': {kind}")
154 }
155 SheetsDiffError::ReadSheet {
156 side, sheet, kind, ..
157 } => {
158 write!(
159 f,
160 "cannot read sheet '{}' from {side} workbook: {kind}",
161 sheet.name
162 )
163 }
164 SheetsDiffError::UnsupportedFormat { side, detail } => {
165 write!(
166 f,
167 "{side} workbook is not a supported xlsx format: {detail}"
168 )
169 }
170 SheetsDiffError::EncryptedWorkbook { side } => {
171 write!(f, "{side} workbook is password-protected")
172 }
173 SheetsDiffError::InvalidOptions { detail } => {
174 write!(f, "invalid options: {detail}")
175 }
176 SheetsDiffError::Cancelled => f.write_str("comparison was cancelled"),
177 SheetsDiffError::LimitExceeded { limit, observed } => {
178 write!(f, "limit '{limit}' exceeded (observed {observed})")
179 }
180 SheetsDiffError::Internal { detail } => {
181 write!(f, "internal error: {detail}")
182 }
183 }
184 }
185}
186
187impl std::error::Error for SheetsDiffError {
188 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
189 match self {
190 SheetsDiffError::OpenWorkbook { inner, .. } => {
191 inner.as_deref().map(|e| e as &dyn std::error::Error)
192 }
193 SheetsDiffError::ReadSheet { inner, .. } => {
194 inner.as_deref().map(|e| e as &dyn std::error::Error)
195 }
196 _ => None,
197 }
198 }
199}
200
201impl SheetsDiffError {
206 pub(crate) fn open_workbook(
207 side: Side,
208 source: SourceDescription,
209 calamine_err: calamine::XlsxError,
210 ) -> Self {
211 let kind = classify_open_error(&calamine_err);
212 SheetsDiffError::OpenWorkbook {
213 side,
214 source,
215 kind,
216 inner: Some(Box::new(CalamiLineError(calamine_err))),
217 }
218 }
219
220 pub(crate) fn read_sheet(
221 side: Side,
222 sheet: SheetRef,
223 calamine_err: calamine::XlsxError,
224 ) -> Self {
225 let kind = classify_read_error(&calamine_err);
226 SheetsDiffError::ReadSheet {
227 side,
228 sheet,
229 kind,
230 inner: Some(Box::new(CalamiLineError(calamine_err))),
231 }
232 }
233}
234
235fn classify_open_error(e: &calamine::XlsxError) -> OpenErrorKind {
236 use calamine::XlsxError;
237 match e {
238 XlsxError::Password => OpenErrorKind::NotXlsx, XlsxError::FileNotFound(_) => OpenErrorKind::NotFound,
240 XlsxError::Io(io) => match io.kind() {
241 std::io::ErrorKind::NotFound => OpenErrorKind::NotFound,
242 std::io::ErrorKind::PermissionDenied => OpenErrorKind::PermissionDenied,
243 _ => OpenErrorKind::Other,
244 },
245 XlsxError::Zip(_) => OpenErrorKind::NotXlsx,
246 _ => OpenErrorKind::Corrupt,
247 }
248}
249
250fn classify_read_error(e: &calamine::XlsxError) -> ReadErrorKind {
251 use calamine::XlsxError;
252 match e {
253 XlsxError::WorksheetNotFound(_) => ReadErrorKind::SheetNotFound,
254 XlsxError::Io(_) => ReadErrorKind::Other,
258 _ => ReadErrorKind::MalformedSheet,
259 }
260}
261
262pub(crate) fn from_open_error(
265 side: Side,
266 source: SourceDescription,
267 e: calamine::XlsxError,
268) -> SheetsDiffError {
269 if matches!(e, calamine::XlsxError::Password) {
270 SheetsDiffError::EncryptedWorkbook { side }
271 } else {
272 SheetsDiffError::open_workbook(side, source, e)
273 }
274}