1use std::{error::Error, fmt};
13
14use crate::LiteralValue;
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
21pub enum ExcelErrorKind {
22 Null,
23 Ref,
24 Name,
25 Value,
26 Div,
27 Na,
28 Num,
29 Error,
30 NImpl,
31 Spill,
32 Calc,
33 Circ,
34 Cancelled,
35}
36
37impl fmt::Display for ExcelErrorKind {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 f.write_str(match self {
40 Self::Null => "#NULL!",
41 Self::Ref => "#REF!",
42 Self::Name => "#NAME?",
43 Self::Value => "#VALUE!",
44 Self::Div => "#DIV/0!",
45 Self::Na => "#N/A",
46 Self::Num => "#NUM!",
47 Self::Error => "#ERROR!",
48 Self::NImpl => "#N/IMPL!",
49 Self::Spill => "#SPILL!",
50 Self::Calc => "#CALC!",
51 Self::Circ => "#CIRC!",
52 Self::Cancelled => "#CANCELLED!",
53 })
54 }
55}
56
57impl ExcelErrorKind {
58 pub fn parse(s: &str) -> Self {
59 match s.trim().to_ascii_lowercase().as_str() {
60 "#null!" => Self::Null,
61 "#ref!" => Self::Ref,
62 "#name?" => Self::Name,
63 "#value!" => Self::Value,
64 "#div/0!" => Self::Div,
65 "#n/a" => Self::Na,
66 "#num!" => Self::Num,
67 "#error!" => Self::Error,
68 "#n/impl!" => Self::NImpl,
69 "#spill!" => Self::Spill,
70 "#calc!" => Self::Calc,
71 "#circ!" => Self::Circ,
72 "#cancelled!" => Self::Cancelled,
73 _ => panic!("Unknown error kind '{s}'"),
74 }
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
83pub struct ErrorContext {
84 pub row: Option<u32>,
85 pub col: Option<u32>,
86 pub origin_row: Option<u32>,
88 pub origin_col: Option<u32>,
89 pub origin_sheet: Option<String>,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
96pub enum ExcelErrorExtra {
97 #[default]
99 None,
100
101 Spill {
103 expected_rows: u32,
104 expected_cols: u32,
105 },
106 }
109
110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
121pub struct ExcelError {
122 pub kind: ExcelErrorKind,
123 pub message: Option<String>,
124 pub context: Option<ErrorContext>,
125 pub extra: ExcelErrorExtra,
126}
127
128impl From<ExcelErrorKind> for ExcelError {
131 fn from(kind: ExcelErrorKind) -> Self {
132 Self {
133 kind,
134 message: None,
135 context: None,
136 extra: ExcelErrorExtra::None,
137 }
138 }
139}
140
141impl ExcelError {
142 pub fn new(kind: ExcelErrorKind) -> Self {
144 kind.into()
145 }
146
147 pub fn with_message<S: Into<String>>(mut self, msg: S) -> Self {
149 self.message = Some(msg.into());
150 self
151 }
152
153 pub fn with_location(mut self, row: u32, col: u32) -> Self {
155 self.context = Some(ErrorContext {
156 row: Some(row),
157 col: Some(col),
158 origin_row: None,
159 origin_col: None,
160 origin_sheet: None,
161 });
162 self
163 }
164
165 pub fn with_origin(mut self, sheet: Option<String>, row: u32, col: u32) -> Self {
167 if let Some(ref mut ctx) = self.context {
168 ctx.origin_sheet = sheet;
169 ctx.origin_row = Some(row);
170 ctx.origin_col = Some(col);
171 } else {
172 self.context = Some(ErrorContext {
173 row: None,
174 col: None,
175 origin_row: Some(row),
176 origin_col: Some(col),
177 origin_sheet: sheet,
178 });
179 }
180 self
181 }
182
183 pub fn with_extra(mut self, extra: ExcelErrorExtra) -> Self {
185 self.extra = extra;
186 self
187 }
188
189 pub fn from_error_string(s: &str) -> Self {
190 let kind = ExcelErrorKind::parse(s);
191 Self::new(kind)
192 }
193
194 pub fn new_value() -> Self {
195 Self::new(ExcelErrorKind::Value)
196 }
197
198 pub fn new_name() -> Self {
199 Self::new(ExcelErrorKind::Name)
200 }
201
202 pub fn new_div() -> Self {
203 Self::new(ExcelErrorKind::Div)
204 }
205
206 pub fn new_ref() -> Self {
207 Self::new(ExcelErrorKind::Ref)
208 }
209
210 pub fn new_circ() -> Self {
211 Self::new(ExcelErrorKind::Circ)
212 }
213
214 pub fn new_num() -> Self {
215 Self::new(ExcelErrorKind::Num)
216 }
217
218 pub fn new_na() -> Self {
219 Self::new(ExcelErrorKind::Na)
220 }
221}
222
223impl fmt::Display for ExcelError {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "{}", self.kind)?;
229
230 if let Some(ref msg) = self.message {
232 write!(f, ": {msg}")?;
233 }
234
235 if let Some(ref ctx) = self.context {
237 if let (Some(r), Some(c)) = (ctx.row, ctx.col) {
238 write!(f, " (row {r}, col {c})")?;
239 }
240
241 if let (Some(or), Some(oc)) = (ctx.origin_row, ctx.origin_col) {
243 if ctx.origin_row != ctx.row || ctx.origin_col != ctx.col {
244 if let Some(ref sheet) = ctx.origin_sheet {
245 write!(f, " [origin: {sheet}!R{or}C{oc}]")?;
246 } else {
247 write!(f, " [origin: R{or}C{oc}]")?;
248 }
249 }
250 }
251 }
252
253 match &self.extra {
255 ExcelErrorExtra::None => {}
256 ExcelErrorExtra::Spill {
257 expected_rows,
258 expected_cols,
259 } => {
260 write!(f, " [spill {expected_rows}×{expected_cols}]")?;
261 }
262 }
263
264 Ok(())
265 }
266}
267
268impl Error for ExcelError {}
269impl From<ExcelError> for String {
270 fn from(error: ExcelError) -> Self {
271 format!("{error}")
272 }
273}
274impl From<ExcelError> for LiteralValue {
275 fn from(error: ExcelError) -> Self {
276 LiteralValue::Error(error)
277 }
278}
279
280impl PartialEq<str> for ExcelErrorKind {
281 fn eq(&self, other: &str) -> bool {
282 format!("{self}") == other
283 }
284}
285
286impl PartialEq<&str> for ExcelError {
287 fn eq(&self, other: &&str) -> bool {
288 self.kind.to_string() == *other
289 }
290}
291
292impl PartialEq<str> for ExcelError {
293 fn eq(&self, other: &str) -> bool {
294 self.kind.to_string() == other
295 }
296}