1use std::fmt;
2
3#[derive(Debug)]
4pub enum SourceError {
5 FileOpenFailed(String, std::io::Error),
6 EmptyFile(String),
7 DuplicateColumn(String),
8 FieldCountMismatch {
9 row: usize,
10 fields: usize,
11 expected: usize,
12 },
13 InvalidJson(usize, String),
14 UnknownFormat(String),
15 SourceNotFound(String),
16 ColumnNotFound(String, String),
17}
18
19impl fmt::Display for SourceError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 SourceError::FileOpenFailed(path, err) => {
23 write!(f, "failed to open source file '{path}': {err}")
24 }
25 SourceError::EmptyFile(fmt) => {
26 write!(f, "header required but file is empty: {fmt}")
27 }
28 SourceError::DuplicateColumn(col) => {
29 write!(f, "duplicate column name '{col}' in header")
30 }
31 SourceError::FieldCountMismatch {
32 row,
33 fields,
34 expected,
35 } => {
36 write!(f, "row {row} has {fields} fields, expected {expected}")
37 }
38 SourceError::InvalidJson(line, msg) => {
39 write!(f, "invalid JSON on line {line}: {msg}")
40 }
41 SourceError::UnknownFormat(file) => {
42 write!(f, "unknown format for file: {file}")
43 }
44 SourceError::SourceNotFound(name) => {
45 write!(f, "source '{name}' not found")
46 }
47 SourceError::ColumnNotFound(col, source) => {
48 write!(f, "column '{col}' not found in source '{source}'")
49 }
50 }
51 }
52}
53
54impl std::error::Error for SourceError {}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn test_source_error_display() {
62 let err = SourceError::FileOpenFailed(
63 "test.csv".into(),
64 std::io::Error::new(std::io::ErrorKind::NotFound, "file not found"),
65 );
66 assert!(err.to_string().contains("test.csv"));
67
68 let err = SourceError::EmptyFile("empty.csv".into());
69 assert!(err.to_string().contains("empty.csv"));
70
71 let err = SourceError::DuplicateColumn("id".into());
72 assert!(err.to_string().contains("id"));
73
74 let err = SourceError::FieldCountMismatch {
75 row: 5,
76 fields: 2,
77 expected: 3,
78 };
79 assert!(err.to_string().contains("5"));
80
81 let err = SourceError::InvalidJson(10, "bad json".into());
82 assert!(err.to_string().contains("bad json"));
83
84 let err = SourceError::UnknownFormat("file.bin".into());
85 assert!(err.to_string().contains("file.bin"));
86
87 let err = SourceError::SourceNotFound("users".into());
88 assert!(err.to_string().contains("users"));
89
90 let err = SourceError::ColumnNotFound("name".into(), "users".into());
91 assert!(err.to_string().contains("name"));
92 }
93
94 #[test]
95 fn test_source_error_debug() {
96 let err = SourceError::EmptyFile("test.csv".into());
97 let s = format!("{:?}", err);
98 assert!(!s.is_empty());
99 }
100}