1use std::fmt;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub struct XlsRsError {
9 pub kind: ErrorKind,
10 pub context: ErrorContext,
11}
12
13impl fmt::Display for XlsRsError {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 write!(f, "{}", self.kind)?;
16 if let Some(file) = &self.context.file {
17 write!(f, " in file '{}'", file)?;
18 }
19 if let Some(row) = self.context.row {
20 write!(f, " at row {}", row + 1)?; }
22 if let Some(col) = self.context.column {
23 write!(f, ", column {}", col + 1)?;
24 }
25 if let Some(cell) = &self.context.cell_ref {
26 write!(f, " (cell {})", cell)?;
27 }
28 Ok(())
29 }
30}
31
32
33#[derive(Debug, Default, Clone)]
35pub struct ErrorContext {
36 pub file: Option<String>,
38 pub row: Option<usize>,
40 pub column: Option<usize>,
42 pub cell_ref: Option<String>,
44 pub column_name: Option<String>,
46}
47
48impl ErrorContext {
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn with_file(mut self, file: &str) -> Self {
54 self.file = Some(file.to_string());
55 self
56 }
57
58 pub fn with_row(mut self, row: usize) -> Self {
59 self.row = Some(row);
60 self
61 }
62
63 pub fn with_column(mut self, col: usize) -> Self {
64 self.column = Some(col);
65 self
66 }
67
68 pub fn with_cell_ref(mut self, cell: &str) -> Self {
69 self.cell_ref = Some(cell.to_string());
70 self
71 }
72
73 pub fn with_column_name(mut self, name: &str) -> Self {
74 self.column_name = Some(name.to_string());
75 self
76 }
77}
78
79#[derive(Error, Debug)]
81pub enum ErrorKind {
82 #[error("Column '{0}' not found")]
83 ColumnNotFound(String),
84
85 #[error("Invalid cell reference '{0}'")]
86 InvalidCellRef(String),
87
88 #[error("Invalid value '{0}' - expected {1}")]
89 InvalidValue(String, String),
90
91 #[error("Type conversion failed: cannot convert '{0}' to {1}")]
92 TypeConversion(String, String),
93
94 #[error("Division by zero")]
95 DivisionByZero,
96
97 #[error("Invalid formula: {0}")]
98 InvalidFormula(String),
99
100 #[error("File not found: {0}")]
101 FileNotFound(String),
102
103 #[error("Unsupported file format: {0}")]
104 UnsupportedFormat(String),
105
106 #[error("Parse error: {0}")]
107 ParseError(String),
108
109 #[error("Invalid date format: {0}")]
110 InvalidDateFormat(String),
111
112 #[error("Invalid regex pattern: {0}")]
113 InvalidRegex(String),
114
115 #[error("IO error: {0}")]
116 IoError(String),
117
118 #[error("{0}")]
119 Other(String),
120}
121
122impl XlsRsError {
123 pub fn column_not_found(name: &str) -> Self {
124 Self {
125 kind: ErrorKind::ColumnNotFound(name.to_string()),
126 context: ErrorContext::new(),
127 }
128 }
129
130 pub fn invalid_value(value: &str, expected: &str) -> Self {
131 Self {
132 kind: ErrorKind::InvalidValue(value.to_string(), expected.to_string()),
133 context: ErrorContext::new(),
134 }
135 }
136
137 pub fn type_conversion(value: &str, target_type: &str) -> Self {
138 Self {
139 kind: ErrorKind::TypeConversion(value.to_string(), target_type.to_string()),
140 context: ErrorContext::new(),
141 }
142 }
143
144 pub fn with_context(mut self, context: ErrorContext) -> Self {
145 self.context = context;
146 self
147 }
148}
149
150pub type XlsRsResult<T> = Result<T, XlsRsError>;
152
153pub trait ResultExt<T> {
155 fn with_file_context(self, file: &str) -> anyhow::Result<T>;
156 fn with_row_context(self, file: &str, row: usize) -> anyhow::Result<T>;
157 fn with_cell_context(self, file: &str, row: usize, col: usize) -> anyhow::Result<T>;
158}
159
160impl<T, E: std::error::Error + Send + Sync + 'static> ResultExt<T> for Result<T, E> {
161 fn with_file_context(self, file: &str) -> anyhow::Result<T> {
162 self.map_err(|e| anyhow::anyhow!("{} in file '{}'", e, file))
163 }
164
165 fn with_row_context(self, file: &str, row: usize) -> anyhow::Result<T> {
166 self.map_err(|e| anyhow::anyhow!("{} in file '{}' at row {}", e, file, row + 1))
167 }
168
169 fn with_cell_context(self, file: &str, row: usize, col: usize) -> anyhow::Result<T> {
170 self.map_err(|e| {
171 anyhow::anyhow!(
172 "{} in file '{}' at row {}, column {}",
173 e,
174 file,
175 row + 1,
176 col + 1
177 )
178 })
179 }
180}