1use std::borrow::Cow;
2use std::error::Error as StdError;
3use std::fmt;
4use std::io;
5
6pub type Result<T> = std::result::Result<T, Error>;
8
9#[derive(Debug)]
11pub enum Error {
12 Io(io::Error),
14
15 Polars(polars::error::PolarsError),
17
18 Json(serde_json::Error),
20
21 Format(FormatError),
23
24 Operation(Cow<'static, str>),
26
27 Config(String),
29
30 Multiple(Vec<Error>),
32}
33
34#[derive(Debug, Clone)]
36pub enum FormatError {
37 Unknown(String),
39
40 DetectionFailed(String),
42
43 UnsupportedFeature(String),
45
46 SchemaMismatch {
48 expected: String,
50 actual: String,
52 },
53
54 InvalidOption(String),
56
57 SerializationError(String),
59}
60
61impl fmt::Display for Error {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 Error::Io(e) => write!(f, "I/O error: {e}"),
65 Error::Polars(e) => write!(f, "DataFrame error: {}", e),
66 Error::Json(e) => write!(f, "JSON error: {}", e),
67 Error::Format(e) => write!(f, "Format error: {}", e),
68 Error::Operation(msg) => write!(f, "Operation error: {}", msg),
69 Error::Config(msg) => write!(f, "Configuration error: {}", msg),
70 Error::Multiple(errors) => {
71 write!(f, "Multiple errors occurred:")?;
72 for (i, e) in errors.iter().enumerate() {
73 write!(f, "\n {}. {}", i + 1, e)?;
74 }
75 Ok(())
76 }
77 }
78 }
79}
80
81impl fmt::Display for FormatError {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 match self {
84 FormatError::Unknown(format) => {
85 write!(f, "Unknown format: {}", format)
86 }
87 FormatError::DetectionFailed(path) => {
88 write!(f, "Failed to detect format for: {}", path)
89 }
90 FormatError::UnsupportedFeature(feature) => {
91 write!(f, "Unsupported feature: {}", feature)
92 }
93 FormatError::SchemaMismatch { expected, actual } => {
94 write!(f, "Schema mismatch: expected {}, got {}", expected, actual)
95 }
96 FormatError::InvalidOption(option) => {
97 write!(f, "Invalid format option: {}", option)
98 }
99 FormatError::SerializationError(msg) => {
100 write!(f, "Serialization error: {}", msg)
101 }
102 }
103 }
104}
105
106impl StdError for Error {
107 fn source(&self) -> Option<&(dyn StdError + 'static)> {
108 match self {
109 Error::Io(e) => Some(e),
110 Error::Polars(e) => Some(e),
111 Error::Json(e) => Some(e),
112 _ => None,
113 }
114 }
115}
116
117impl StdError for FormatError {}
118
119impl From<io::Error> for Error {
121 fn from(e: io::Error) -> Self {
122 Error::Io(e)
123 }
124}
125
126impl From<polars::error::PolarsError> for Error {
127 fn from(e: polars::error::PolarsError) -> Self {
128 Error::Polars(e)
129 }
130}
131
132impl From<serde_json::Error> for Error {
133 fn from(e: serde_json::Error) -> Self {
134 Error::Json(e)
135 }
136}
137
138impl From<apache_avro::Error> for Error {
139 fn from(e: apache_avro::Error) -> Self {
140 Error::Format(FormatError::SerializationError(e.to_string()))
141 }
142}
143
144impl From<FormatError> for Error {
145 fn from(e: FormatError) -> Self {
146 Error::Format(e)
147 }
148}
149
150impl From<anyhow::Error> for Error {
151 fn from(e: anyhow::Error) -> Self {
152 Error::Operation(Cow::Owned(e.to_string()))
153 }
154}
155
156impl Error {
158 pub fn operation(msg: impl Into<Cow<'static, str>>) -> Self {
160 Error::Operation(msg.into())
161 }
162
163 pub fn config(msg: impl Into<String>) -> Self {
165 Error::Config(msg.into())
166 }
167
168 pub fn combine(errors: Vec<Error>) -> Self {
170 match errors.len() {
171 0 => Error::operation(Cow::Borrowed("No errors")),
172 1 => errors.into_iter().next().unwrap(),
173 _ => Error::Multiple(errors),
174 }
175 }
176}
177
178impl FormatError {
179 pub fn unknown(format: impl Into<String>) -> Self {
181 FormatError::Unknown(format.into())
182 }
183
184 pub fn detection_failed(path: impl Into<String>) -> Self {
186 FormatError::DetectionFailed(path.into())
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_error_display() {
196 let err = Error::operation(Cow::Borrowed("test operation failed"));
198 assert_eq!(err.to_string(), "Operation error: test operation failed");
199
200 let err = Error::config("test config failed".to_string());
201 assert_eq!(err.to_string(), "Configuration error: test config failed");
202
203 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
204 let err = Error::Io(io_err);
205 assert_eq!(err.to_string(), "I/O error: file not found");
206
207 let polars_err = polars::error::PolarsError::from(io::Error::other("test polars error"));
210 let err = Error::Polars(polars_err);
211 assert!(err.to_string().starts_with("DataFrame error:"));
212
213 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
214 let err = Error::Json(json_err);
215 assert!(err.to_string().starts_with("JSON error:"));
216
217 let format_err = FormatError::unknown("xyz");
218 let err = Error::Format(format_err);
219 assert_eq!(err.to_string(), "Format error: Unknown format: xyz");
220
221 let errors = vec![
223 Error::operation(Cow::Borrowed("error 1")),
224 Error::operation(Cow::Borrowed("error 2")),
225 ];
226 let combined = Error::combine(errors);
227 let display = combined.to_string();
228 assert!(display.starts_with("Multiple errors occurred:"));
229 assert!(display.contains("1. Operation error: error 1"));
230 assert!(display.contains("2. Operation error: error 2"));
231 }
232
233 #[test]
234 fn test_format_error_display() {
235 let err = FormatError::Unknown("xyz".to_string());
236 assert_eq!(err.to_string(), "Unknown format: xyz");
237
238 let err = FormatError::DetectionFailed("path/to/file".to_string());
239 assert_eq!(err.to_string(), "Failed to detect format for: path/to/file");
240
241 let err = FormatError::UnsupportedFeature("streaming".to_string());
242 assert_eq!(err.to_string(), "Unsupported feature: streaming");
243
244 let err = FormatError::SchemaMismatch {
245 expected: "schema1".to_string(),
246 actual: "schema2".to_string(),
247 };
248 assert_eq!(
249 err.to_string(),
250 "Schema mismatch: expected schema1, got schema2"
251 );
252
253 let err = FormatError::InvalidOption("option=value".to_string());
254 assert_eq!(err.to_string(), "Invalid format option: option=value");
255
256 let err = FormatError::SerializationError("failed to serialize".to_string());
257 assert_eq!(err.to_string(), "Serialization error: failed to serialize");
258 }
259
260 #[test]
261 fn test_error_conversion() {
262 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
264 let err: Error = io_err.into();
265 assert!(matches!(err, Error::Io(_)));
266
267 let polars_err = polars::error::PolarsError::from(io::Error::other("test"));
269 let err: Error = polars_err.into();
270 assert!(matches!(err, Error::Polars(_)));
271
272 let json_err = serde_json::from_str::<serde_json::Value>("{").unwrap_err();
274 let err: Error = json_err.into();
275 assert!(matches!(err, Error::Json(_)));
276
277 let format_err = FormatError::unknown("test");
279 let err: Error = format_err.into();
280 assert!(matches!(err, Error::Format(_)));
281
282 }
285
286 #[test]
287 fn test_error_source() {
288 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
289 let err = Error::Io(io_err);
290 assert!(err.source().is_some());
291
292 let polars_err = polars::error::PolarsError::from(io::Error::other("test"));
293 let err = Error::Polars(polars_err);
294 assert!(err.source().is_some());
295
296 let json_err = serde_json::from_str::<serde_json::Value>("{").unwrap_err();
297 let err = Error::Json(json_err);
298 assert!(err.source().is_some());
299
300 let err = Error::operation("test");
301 assert!(err.source().is_none());
302
303 let err = Error::config("test");
304 assert!(err.source().is_none());
305
306 let format_err = FormatError::unknown("test");
307 let err = Error::Format(format_err);
308 assert!(err.source().is_none());
309
310 let errors = vec![Error::operation("1"), Error::operation("2")];
311 let err = Error::combine(errors);
312 assert!(err.source().is_none());
313 }
314
315 #[test]
316 fn test_helper_functions() {
317 let err = Error::operation("test msg");
319 assert!(matches!(err, Error::Operation(_)));
320 assert_eq!(err.to_string(), "Operation error: test msg");
321
322 let err = Error::operation(Cow::Owned("owned".to_string()));
323 assert!(matches!(err, Error::Operation(_)));
324
325 let err = Error::config("config msg");
327 assert!(matches!(err, Error::Config(_)));
328 assert_eq!(err.to_string(), "Configuration error: config msg");
329
330 let err = FormatError::unknown("fmt");
332 assert!(matches!(err, FormatError::Unknown(_)));
333 assert_eq!(err.to_string(), "Unknown format: fmt");
334
335 let err = FormatError::detection_failed("path");
337 assert!(matches!(err, FormatError::DetectionFailed(_)));
338 assert_eq!(err.to_string(), "Failed to detect format for: path");
339 }
340
341 #[test]
342 fn test_error_combine() {
343 let combined = Error::combine(vec![]);
345 assert!(matches!(combined, Error::Operation(_)));
346 assert_eq!(combined.to_string(), "Operation error: No errors");
347
348 let single = Error::operation("single");
350 let combined = Error::combine(vec![single]);
351 assert!(matches!(combined, Error::Operation(_)));
352 assert_eq!(combined.to_string(), "Operation error: single".to_string());
353
354 let errors = vec![
356 Error::operation("error 1"),
357 Error::operation("error 2"),
358 Error::config("config error"),
359 ];
360 let combined = Error::combine(errors);
361 assert!(matches!(combined, Error::Multiple(_)));
362 let display = combined.to_string();
363 assert!(display.contains("Multiple errors occurred:"));
364 assert!(display.contains("1. Operation error: error 1"));
365 assert!(display.contains("2. Operation error: error 2"));
366 assert!(display.contains("3. Configuration error: config error"));
367 }
368
369 #[test]
370 fn test_format_error_clone() {
371 let err = FormatError::Unknown("test".to_string());
372 let cloned = err.clone();
373 assert_eq!(err.to_string(), cloned.to_string());
374 }
375}