1use thiserror::Error;
7
8#[derive(Error, Debug)]
10pub enum Error {
11 #[error("invalid cell reference: {0}")]
13 InvalidCellReference(String),
14
15 #[error("invalid row number: {0}")]
17 InvalidRowNumber(u32),
18
19 #[error("invalid column number: {0}")]
21 InvalidColumnNumber(u32),
22
23 #[error("sheet '{name}' does not exist")]
25 SheetNotFound { name: String },
26
27 #[error("sheet '{name}' already exists")]
29 SheetAlreadyExists { name: String },
30
31 #[error("invalid sheet name: {0}")]
33 InvalidSheetName(String),
34
35 #[error("I/O error: {0}")]
37 Io(#[from] std::io::Error),
38
39 #[error("ZIP error: {0}")]
41 Zip(String),
42
43 #[error("XML parse error: {0}")]
45 XmlParse(String),
46
47 #[error("XML deserialization error: {0}")]
49 XmlDeserialize(String),
50
51 #[error("column width {width} exceeds maximum {max}")]
53 ColumnWidthExceeded { width: f64, max: f64 },
54
55 #[error("row height {height} exceeds maximum {max}")]
57 RowHeightExceeded { height: f64, max: f64 },
58
59 #[error("cell value too long: {length} characters (max {max})")]
61 CellValueTooLong { length: usize, max: usize },
62
63 #[error("style not found: {id}")]
65 StyleNotFound { id: u32 },
66
67 #[error("cell styles exceeded maximum ({max})")]
69 CellStylesExceeded { max: usize },
70
71 #[error("row {row} has already been written (must write rows in ascending order)")]
73 StreamRowAlreadyWritten { row: u32 },
74
75 #[error("stream writer already finished")]
77 StreamAlreadyFinished,
78
79 #[error("cannot set column width after rows have been written")]
81 StreamColumnsAfterRows,
82
83 #[error("merge cell range '{new}' overlaps with existing range '{existing}'")]
85 MergeCellOverlap { new: String, existing: String },
86
87 #[error("merge cell range '{0}' not found")]
89 MergeCellNotFound(String),
90
91 #[error("invalid defined name: {0}")]
93 InvalidDefinedName(String),
94
95 #[error("defined name '{name}' not found")]
97 DefinedNameNotFound { name: String },
98
99 #[error("circular reference detected at {cell}")]
101 CircularReference { cell: String },
102
103 #[error("unknown function: {name}")]
105 UnknownFunction { name: String },
106
107 #[error("function {name} expects {expected} arguments, got {got}")]
109 WrongArgCount {
110 name: String,
111 expected: String,
112 got: usize,
113 },
114
115 #[error("formula evaluation error: {0}")]
117 FormulaError(String),
118
119 #[error("pivot table '{name}' not found")]
121 PivotTableNotFound { name: String },
122
123 #[error("pivot table '{name}' already exists")]
125 PivotTableAlreadyExists { name: String },
126
127 #[error("invalid source range: {0}")]
129 InvalidSourceRange(String),
130
131 #[error("unsupported image format: {format}")]
133 UnsupportedImageFormat { format: String },
134
135 #[error("file is encrypted, password required")]
137 FileEncrypted,
138
139 #[error("incorrect password")]
141 IncorrectPassword,
142
143 #[error("unsupported encryption method: {0}")]
145 UnsupportedEncryption(String),
146
147 #[error("outline level {level} exceeds maximum {max}")]
149 OutlineLevelExceeded { level: u8, max: u8 },
150
151 #[error("invalid merge cell reference: {0}")]
153 InvalidMergeCellReference(String),
154
155 #[error("invalid reference: {reference}")]
157 InvalidReference { reference: String },
158
159 #[error("invalid argument: {0}")]
161 InvalidArgument(String),
162
163 #[error("internal error: {0}")]
165 Internal(String),
166}
167
168pub type Result<T> = std::result::Result<T, Error>;
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_error_display_invalid_cell_reference() {
177 let err = Error::InvalidCellReference("XYZ0".to_string());
178 assert_eq!(err.to_string(), "invalid cell reference: XYZ0");
179 }
180
181 #[test]
182 fn test_error_display_sheet_not_found() {
183 let err = Error::SheetNotFound {
184 name: "Missing".to_string(),
185 };
186 assert_eq!(err.to_string(), "sheet 'Missing' does not exist");
187 }
188
189 #[test]
190 fn test_error_display_sheet_already_exists() {
191 let err = Error::SheetAlreadyExists {
192 name: "Sheet1".to_string(),
193 };
194 assert_eq!(err.to_string(), "sheet 'Sheet1' already exists");
195 }
196
197 #[test]
198 fn test_error_display_invalid_sheet_name() {
199 let err = Error::InvalidSheetName("bad[name".to_string());
200 assert_eq!(err.to_string(), "invalid sheet name: bad[name");
201 }
202
203 #[test]
204 fn test_error_display_invalid_row_number() {
205 let err = Error::InvalidRowNumber(0);
206 assert_eq!(err.to_string(), "invalid row number: 0");
207 }
208
209 #[test]
210 fn test_error_display_invalid_column_number() {
211 let err = Error::InvalidColumnNumber(99999);
212 assert_eq!(err.to_string(), "invalid column number: 99999");
213 }
214
215 #[test]
216 fn test_error_display_io() {
217 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
218 let err = Error::Io(io_err);
219 assert_eq!(err.to_string(), "I/O error: gone");
220 }
221
222 #[test]
223 fn test_error_display_zip() {
224 let err = Error::Zip("corrupted archive".to_string());
225 assert_eq!(err.to_string(), "ZIP error: corrupted archive");
226 }
227
228 #[test]
229 fn test_error_display_xml_parse() {
230 let err = Error::XmlParse("unexpected EOF".to_string());
231 assert_eq!(err.to_string(), "XML parse error: unexpected EOF");
232 }
233
234 #[test]
235 fn test_error_display_xml_deserialize() {
236 let err = Error::XmlDeserialize("missing attribute".to_string());
237 assert_eq!(
238 err.to_string(),
239 "XML deserialization error: missing attribute"
240 );
241 }
242
243 #[test]
244 fn test_error_display_cell_value_too_long() {
245 let err = Error::CellValueTooLong {
246 length: 40000,
247 max: 32767,
248 };
249 assert_eq!(
250 err.to_string(),
251 "cell value too long: 40000 characters (max 32767)"
252 );
253 }
254
255 #[test]
256 fn test_error_display_outline_level_exceeded() {
257 let err = Error::OutlineLevelExceeded { level: 8, max: 7 };
258 assert_eq!(err.to_string(), "outline level 8 exceeds maximum 7");
259 }
260
261 #[test]
262 fn test_error_display_invalid_merge_cell_reference() {
263 let err = Error::InvalidMergeCellReference("bad ref".to_string());
264 assert_eq!(err.to_string(), "invalid merge cell reference: bad ref");
265 }
266
267 #[test]
268 fn test_error_display_internal() {
269 let err = Error::Internal("something went wrong".to_string());
270 assert_eq!(err.to_string(), "internal error: something went wrong");
271 }
272
273 #[test]
274 fn test_from_io_error() {
275 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
276 let err: Error = io_err.into();
277 assert!(matches!(err, Error::Io(_)));
278 }
279
280 #[test]
281 fn test_error_is_send_and_sync() {
282 fn assert_send<T: Send>() {}
283 fn assert_sync<T: Sync>() {}
284 assert_send::<Error>();
285 assert_sync::<Error>();
286 }
287}