1use std::borrow::Cow;
2use std::error::Error as StdError;
3use std::fmt;
4use std::io;
5
6use dsq_formats;
7#[cfg(feature = "io")]
8use dsq_io;
9
10pub type Result<T> = std::result::Result<T, Error>;
12
13#[derive(Debug)]
15pub enum Error {
16 Io(io::Error),
18
19 Polars(polars::error::PolarsError),
21
22 Json(serde_json::Error),
24
25 Format(FormatError),
27
28 Filter(FilterError),
30
31 Type(TypeError),
33
34 Operation(Cow<'static, str>),
36
37 Config(String),
39
40 Multiple(Vec<Error>),
42}
43
44#[derive(Debug, Clone)]
46pub enum FormatError {
47 Unknown(String),
49
50 DetectionFailed(String),
52
53 UnsupportedFeature(String),
55
56 SchemaMismatch {
58 expected: String,
60 actual: String,
62 },
63
64 InvalidOption(String),
66}
67
68#[derive(Debug, Clone)]
70pub enum FilterError {
71 Parse(String),
73
74 Compile(String),
76
77 Runtime(String),
79
80 Undefined(String),
82
83 TypeMismatch {
85 expected: String,
87 actual: String,
89 },
90
91 ArgumentCount {
93 expected: usize,
95 actual: usize,
97 },
98}
99
100#[derive(Debug, Clone)]
102pub enum TypeError {
103 InvalidConversion {
105 from: String,
107 to: String,
109 },
110
111 UnsupportedOperation {
113 operation: String,
115 typ: String,
117 },
118
119 FieldNotFound {
121 field: String,
123 typ: String,
125 },
126
127 OutOfRange(String),
129
130 UnexpectedNull(String),
132}
133
134impl fmt::Display for Error {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 match self {
137 Error::Io(e) => write!(f, "I/O error: {e}"),
138 Error::Polars(e) => write!(f, "DataFrame error: {e}"),
139 Error::Json(e) => write!(f, "JSON error: {e}"),
140 Error::Format(e) => write!(f, "Format error: {e}"),
141 Error::Filter(e) => write!(f, "Filter error: {e}"),
142 Error::Type(e) => write!(f, "Type error: {e}"),
143 Error::Operation(msg) => write!(f, "Operation error: {msg}"),
144 Error::Config(msg) => write!(f, "Configuration error: {msg}"),
145 Error::Multiple(errors) => {
146 write!(f, "Multiple errors occurred:")?;
147 for (i, e) in errors.iter().enumerate() {
148 write!(f, "\n {}. {}", i + 1, e)?;
149 }
150 Ok(())
151 }
152 }
153 }
154}
155
156impl fmt::Display for FormatError {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 FormatError::Unknown(format) => {
160 write!(f, "Unknown format: {format}")
161 }
162 FormatError::DetectionFailed(path) => {
163 write!(f, "Failed to detect format for: {path}")
164 }
165 FormatError::UnsupportedFeature(feature) => {
166 write!(f, "Unsupported feature: {feature}")
167 }
168 FormatError::SchemaMismatch { expected, actual } => {
169 write!(f, "Schema mismatch: expected {expected}, got {actual}")
170 }
171 FormatError::InvalidOption(option) => {
172 write!(f, "Invalid format option: {option}")
173 }
174 }
175 }
176}
177
178impl fmt::Display for FilterError {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 match self {
181 FilterError::Parse(msg) => write!(f, "Parse error: {msg}"),
182 FilterError::Compile(msg) => write!(f, "Compilation error: {msg}"),
183 FilterError::Runtime(msg) => write!(f, "Runtime error: {msg}"),
184 FilterError::Undefined(name) => write!(f, "Undefined: {name}"),
185 FilterError::TypeMismatch { expected, actual } => {
186 write!(f, "Type mismatch: expected {expected}, got {actual}")
187 }
188 FilterError::ArgumentCount { expected, actual } => {
189 write!(f, "Wrong argument count: expected {expected}, got {actual}")
190 }
191 }
192 }
193}
194
195impl fmt::Display for TypeError {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 match self {
198 TypeError::InvalidConversion { from, to } => {
199 write!(f, "Cannot convert from {from} to {to}")
200 }
201 TypeError::UnsupportedOperation { operation, typ } => {
202 write!(f, "Operation '{operation}' not supported for type {typ}")
203 }
204 TypeError::FieldNotFound { field, typ } => {
205 write!(f, "Field '{field}' not found in {typ}")
206 }
207 TypeError::OutOfRange(msg) => write!(f, "Value out of range: {msg}"),
208 TypeError::UnexpectedNull(context) => {
209 write!(f, "Unexpected null value in: {context}")
210 }
211 }
212 }
213}
214
215impl StdError for Error {
216 fn source(&self) -> Option<&(dyn StdError + 'static)> {
217 match self {
218 Error::Io(e) => Some(e),
219 Error::Polars(e) => Some(e),
220 Error::Json(e) => Some(e),
221 _ => None,
222 }
223 }
224}
225
226impl StdError for FormatError {}
227impl StdError for FilterError {}
228impl StdError for TypeError {}
229
230impl From<io::Error> for Error {
232 fn from(e: io::Error) -> Self {
233 Error::Io(e)
234 }
235}
236
237impl From<polars::error::PolarsError> for Error {
238 fn from(e: polars::error::PolarsError) -> Self {
239 Error::Polars(e)
240 }
241}
242
243impl From<serde_json::Error> for Error {
244 fn from(e: serde_json::Error) -> Self {
245 Error::Json(e)
246 }
247}
248
249impl From<FormatError> for Error {
250 fn from(e: FormatError) -> Self {
251 Error::Format(e)
252 }
253}
254
255impl From<FilterError> for Error {
256 fn from(e: FilterError) -> Self {
257 Error::Filter(e)
258 }
259}
260
261impl From<TypeError> for Error {
262 fn from(e: TypeError) -> Self {
263 Error::Type(e)
264 }
265}
266
267impl From<anyhow::Error> for Error {
268 fn from(e: anyhow::Error) -> Self {
269 Error::Operation(Cow::Owned(e.to_string()))
270 }
271}
272
273#[cfg(feature = "io")]
274impl From<dsq_io::Error> for Error {
275 fn from(e: dsq_io::Error) -> Self {
276 Error::Operation(Cow::Owned(e.to_string()))
277 }
278}
279
280impl From<dsq_formats::Error> for Error {
281 fn from(e: dsq_formats::Error) -> Self {
282 Error::Operation(Cow::Owned(e.to_string()))
283 }
284}
285
286impl Error {
288 pub fn operation(msg: impl Into<Cow<'static, str>>) -> Self {
290 Error::Operation(msg.into())
291 }
292
293 pub fn config(msg: impl Into<String>) -> Self {
295 Error::Config(msg.into())
296 }
297
298 pub fn from_operation_error(msg: impl Into<Cow<'static, str>>) -> Self {
300 Error::Operation(msg.into())
301 }
302
303 pub fn from_config_error(msg: impl Into<String>) -> Self {
305 Error::Config(msg.into())
306 }
307
308 #[must_use]
310 pub fn combine(errors: Vec<Error>) -> Self {
311 match errors.len() {
312 0 => Error::operation(Cow::Borrowed("No errors")),
313 1 => errors.into_iter().next().unwrap(),
314 _ => Error::Multiple(errors),
315 }
316 }
317}
318
319impl FormatError {
320 pub fn unknown(format: impl Into<String>) -> Self {
322 FormatError::Unknown(format.into())
323 }
324
325 pub fn detection_failed(path: impl Into<String>) -> Self {
327 FormatError::DetectionFailed(path.into())
328 }
329}
330
331impl FilterError {
332 pub fn parse(msg: impl Into<String>) -> Self {
334 FilterError::Parse(msg.into())
335 }
336
337 pub fn compile(msg: impl Into<String>) -> Self {
339 FilterError::Compile(msg.into())
340 }
341
342 pub fn runtime(msg: impl Into<String>) -> Self {
344 FilterError::Runtime(msg.into())
345 }
346}
347
348impl TypeError {
349 pub fn invalid_conversion(from: impl Into<String>, to: impl Into<String>) -> Self {
351 TypeError::InvalidConversion {
352 from: from.into(),
353 to: to.into(),
354 }
355 }
356
357 pub fn unsupported_operation(operation: impl Into<String>, typ: impl Into<String>) -> Self {
359 TypeError::UnsupportedOperation {
360 operation: operation.into(),
361 typ: typ.into(),
362 }
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_error_display() {
372 let err = Error::operation(Cow::Borrowed("test operation failed"));
373 assert_eq!(err.to_string(), "Operation error: test operation failed");
374
375 let err = FormatError::unknown("xyz");
376 assert_eq!(err.to_string(), "Unknown format: xyz");
377
378 let err = FilterError::parse("unexpected token");
379 assert_eq!(err.to_string(), "Parse error: unexpected token");
380
381 let err = TypeError::invalid_conversion("string", "number");
382 assert_eq!(err.to_string(), "Cannot convert from string to number");
383 }
384
385 #[test]
386 fn test_error_conversion() {
387 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
388 let err: Error = io_err.into();
389 assert!(matches!(err, Error::Io(_)));
390
391 let format_err = FormatError::unknown("test");
392 let err: Error = format_err.into();
393 assert!(matches!(err, Error::Format(_)));
394 }
395
396 #[test]
397 fn test_multiple_errors() {
398 let errors = vec![
399 Error::operation(Cow::Borrowed("error 1")),
400 Error::operation(Cow::Borrowed("error 2")),
401 ];
402 let combined = Error::combine(errors);
403 assert!(matches!(combined, Error::Multiple(_)));
404 }
405}