1use std::{error::Error, fmt};
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use crate::LiteralValue;
18
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
25pub enum ExcelErrorKind {
26 Null,
27 Ref,
28 Name,
29 Value,
30 Div,
31 Na,
32 Num,
33 Error,
34 NImpl,
35 Spill,
36 Calc,
37 Circ,
38 Cancelled,
39}
40
41impl fmt::Display for ExcelErrorKind {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_str(match self {
44 Self::Null => "#NULL!",
45 Self::Ref => "#REF!",
46 Self::Name => "#NAME?",
47 Self::Value => "#VALUE!",
48 Self::Div => "#DIV/0!",
49 Self::Na => "#N/A",
50 Self::Num => "#NUM!",
51 Self::Error => "#ERROR!",
52 Self::NImpl => "#N/IMPL!",
53 Self::Spill => "#SPILL!",
54 Self::Calc => "#CALC!",
55 Self::Circ => "#CIRC!",
56 Self::Cancelled => "#CANCELLED!",
57 })
58 }
59}
60
61impl ExcelErrorKind {
62 pub fn try_parse(s: &str) -> Option<Self> {
63 match s.trim().to_ascii_lowercase().as_str() {
64 "#null!" => Some(Self::Null),
65 "#ref!" => Some(Self::Ref),
66 "#name?" => Some(Self::Name),
67 "#value!" => Some(Self::Value),
68 "#div/0!" => Some(Self::Div),
69 "#n/a" => Some(Self::Na),
70 "#num!" => Some(Self::Num),
71 "#error!" => Some(Self::Error),
72 "#n/impl!" => Some(Self::NImpl),
73 "#spill!" => Some(Self::Spill),
74 "#calc!" => Some(Self::Calc),
75 "#circ!" => Some(Self::Circ),
76 "#cancelled!" => Some(Self::Cancelled),
77 _ => None,
78 }
79 }
80
81 pub fn parse(s: &str) -> Self {
82 Self::try_parse(s).unwrap_or(Self::Error)
83 }
84}
85
86#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
92pub struct ErrorContext {
93 pub row: Option<u32>,
94 pub col: Option<u32>,
95 pub origin_row: Option<u32>,
97 pub origin_col: Option<u32>,
98 pub origin_sheet: Option<String>,
99}
100
101#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
106pub enum ExcelErrorExtra {
107 #[default]
109 None,
110
111 Spill {
113 expected_rows: u32,
114 expected_cols: u32,
115 },
116 }
119
120#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131#[derive(Debug, Clone, PartialEq, Eq, Hash)]
132pub struct ExcelError {
133 pub kind: ExcelErrorKind,
134 pub message: Option<String>,
135 pub context: Option<ErrorContext>,
136 pub extra: ExcelErrorExtra,
137}
138
139impl From<ExcelErrorKind> for ExcelError {
142 fn from(kind: ExcelErrorKind) -> Self {
143 Self {
144 kind,
145 message: None,
146 context: None,
147 extra: ExcelErrorExtra::None,
148 }
149 }
150}
151
152impl ExcelError {
153 pub fn new(kind: ExcelErrorKind) -> Self {
155 kind.into()
156 }
157
158 pub fn with_message<S: Into<String>>(mut self, msg: S) -> Self {
160 self.message = Some(msg.into());
161 self
162 }
163
164 pub fn with_location(mut self, row: u32, col: u32) -> Self {
166 self.context = Some(ErrorContext {
167 row: Some(row),
168 col: Some(col),
169 origin_row: None,
170 origin_col: None,
171 origin_sheet: None,
172 });
173 self
174 }
175
176 pub fn with_origin(mut self, sheet: Option<String>, row: u32, col: u32) -> Self {
178 if let Some(ref mut ctx) = self.context {
179 ctx.origin_sheet = sheet;
180 ctx.origin_row = Some(row);
181 ctx.origin_col = Some(col);
182 } else {
183 self.context = Some(ErrorContext {
184 row: None,
185 col: None,
186 origin_row: Some(row),
187 origin_col: Some(col),
188 origin_sheet: sheet,
189 });
190 }
191 self
192 }
193
194 pub fn with_extra(mut self, extra: ExcelErrorExtra) -> Self {
196 self.extra = extra;
197 self
198 }
199
200 pub fn from_error_string(s: &str) -> Self {
201 match ExcelErrorKind::try_parse(s) {
202 Some(kind) => Self::new(kind),
203 None => {
204 Self::new(ExcelErrorKind::Error).with_message(format!("Unknown error code: {s}"))
205 }
206 }
207 }
208
209 pub fn new_value() -> Self {
210 Self::new(ExcelErrorKind::Value)
211 }
212
213 pub fn new_name() -> Self {
214 Self::new(ExcelErrorKind::Name)
215 }
216
217 pub fn new_div() -> Self {
218 Self::new(ExcelErrorKind::Div)
219 }
220
221 pub fn new_ref() -> Self {
222 Self::new(ExcelErrorKind::Ref)
223 }
224
225 pub fn new_circ() -> Self {
226 Self::new(ExcelErrorKind::Circ)
227 }
228
229 pub fn new_num() -> Self {
230 Self::new(ExcelErrorKind::Num)
231 }
232
233 pub fn new_na() -> Self {
234 Self::new(ExcelErrorKind::Na)
235 }
236}
237
238impl fmt::Display for ExcelError {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(f, "{}", self.kind)?;
244
245 if let Some(ref msg) = self.message {
247 write!(f, ": {msg}")?;
248 }
249
250 if let Some(ref ctx) = self.context {
252 if let (Some(r), Some(c)) = (ctx.row, ctx.col) {
253 write!(f, " (row {r}, col {c})")?;
254 }
255
256 if let (Some(or), Some(oc)) = (ctx.origin_row, ctx.origin_col)
258 && (ctx.row != Some(or) || ctx.col != Some(oc))
259 {
260 if let Some(ref sheet) = ctx.origin_sheet {
261 write!(f, " [origin: {sheet}!R{or}C{oc}]")?;
262 } else {
263 write!(f, " [origin: R{or}C{oc}]")?;
264 }
265 }
266 }
267
268 match &self.extra {
270 ExcelErrorExtra::None => {}
271 ExcelErrorExtra::Spill {
272 expected_rows,
273 expected_cols,
274 } => {
275 write!(f, " [spill {expected_rows}×{expected_cols}]")?;
276 }
277 }
278
279 Ok(())
280 }
281}
282
283impl Error for ExcelError {}
284impl From<ExcelError> for String {
285 fn from(error: ExcelError) -> Self {
286 format!("{error}")
287 }
288}
289impl From<ExcelError> for LiteralValue {
290 fn from(error: ExcelError) -> Self {
291 LiteralValue::Error(error)
292 }
293}
294
295impl PartialEq<str> for ExcelErrorKind {
296 fn eq(&self, other: &str) -> bool {
297 format!("{self}") == other
298 }
299}
300
301impl PartialEq<&str> for ExcelError {
302 fn eq(&self, other: &&str) -> bool {
303 self.kind.to_string() == *other
304 }
305}
306
307impl PartialEq<str> for ExcelError {
308 fn eq(&self, other: &str) -> bool {
309 self.kind.to_string() == other
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn parse_known_error_kind() {
319 assert_eq!(ExcelErrorKind::parse("#DIV/0!"), ExcelErrorKind::Div);
320 assert_eq!(ExcelErrorKind::parse("#n/a"), ExcelErrorKind::Na);
321 }
322
323 #[test]
324 fn parse_unknown_error_kind_falls_back() {
325 assert_eq!(ExcelErrorKind::parse("#BOGUS!"), ExcelErrorKind::Error);
326 let err = ExcelError::from_error_string("#BOGUS!");
327 assert_eq!(err.kind, ExcelErrorKind::Error);
328 assert!(err.message.unwrap_or_default().contains("#BOGUS!"));
329 }
330}