1use std::fmt;
2
3#[derive(Debug)]
19#[non_exhaustive]
20pub enum PlotlarsError {
21 Io {
23 path: String,
24 source: std::io::Error,
25 },
26
27 CsvParse {
29 path: String,
30 source: Box<dyn std::error::Error + Send + Sync>,
31 },
32
33 ParquetParse {
35 path: String,
36 source: Box<dyn std::error::Error + Send + Sync>,
37 },
38
39 #[cfg(feature = "format-json")]
41 JsonParse {
42 path: String,
43 source: Box<dyn std::error::Error + Send + Sync>,
44 },
45
46 #[cfg(feature = "format-excel")]
48 ExcelParse {
49 path: String,
50 source: Box<dyn std::error::Error + Send + Sync>,
51 },
52
53 ColumnNotFound {
55 column: String,
56 available: Vec<String>,
57 },
58
59 TypeMismatch {
61 column: String,
62 expected: String,
63 actual: String,
64 },
65
66 PlotBuild { message: String },
68}
69
70impl fmt::Display for PlotlarsError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Self::Io { path, source } => {
74 write!(f, "I/O error reading '{}': {}", path, source)
75 }
76 Self::CsvParse { path, source } => {
77 write!(f, "failed to parse CSV '{}': {}", path, source)
78 }
79 Self::ParquetParse { path, source } => {
80 write!(f, "failed to parse Parquet '{}': {}", path, source)
81 }
82 #[cfg(feature = "format-json")]
83 Self::JsonParse { path, source } => {
84 write!(f, "failed to parse JSON '{}': {}", path, source)
85 }
86 #[cfg(feature = "format-excel")]
87 Self::ExcelParse { path, source } => {
88 write!(f, "failed to parse Excel '{}': {}", path, source)
89 }
90 Self::ColumnNotFound { column, available } => {
91 write!(
92 f,
93 "column '{}' not found; available columns: [{}]",
94 column,
95 available.join(", ")
96 )
97 }
98 Self::TypeMismatch {
99 column,
100 expected,
101 actual,
102 } => {
103 write!(
104 f,
105 "column '{}': expected type {}, found {}",
106 column, expected, actual
107 )
108 }
109 Self::PlotBuild { message } => {
110 write!(f, "plot construction error: {}", message)
111 }
112 }
113 }
114}
115
116impl std::error::Error for PlotlarsError {
117 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
118 match self {
119 Self::Io { source, .. } => Some(source),
120 Self::CsvParse { source, .. } => Some(source.as_ref()),
121 Self::ParquetParse { source, .. } => Some(source.as_ref()),
122 #[cfg(feature = "format-json")]
123 Self::JsonParse { source, .. } => Some(source.as_ref()),
124 #[cfg(feature = "format-excel")]
125 Self::ExcelParse { source, .. } => Some(source.as_ref()),
126 Self::ColumnNotFound { .. } | Self::TypeMismatch { .. } | Self::PlotBuild { .. } => {
127 None
128 }
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn display_io_error() {
139 let err = PlotlarsError::Io {
140 path: "test.csv".to_string(),
141 source: std::io::Error::new(std::io::ErrorKind::NotFound, "not found"),
142 };
143 assert!(err.to_string().contains("test.csv"));
144 assert!(err.to_string().contains("not found"));
145 }
146
147 #[test]
148 fn display_csv_parse_error() {
149 let err = PlotlarsError::CsvParse {
150 path: "bad.csv".to_string(),
151 source: "invalid data".into(),
152 };
153 assert!(err.to_string().contains("bad.csv"));
154 }
155
156 #[test]
157 fn display_column_not_found() {
158 let err = PlotlarsError::ColumnNotFound {
159 column: "missing".to_string(),
160 available: vec!["a".to_string(), "b".to_string()],
161 };
162 let msg = err.to_string();
163 assert!(msg.contains("missing"));
164 assert!(msg.contains("a, b"));
165 }
166
167 #[test]
168 fn display_type_mismatch() {
169 let err = PlotlarsError::TypeMismatch {
170 column: "x".to_string(),
171 expected: "Float64".to_string(),
172 actual: "String".to_string(),
173 };
174 let msg = err.to_string();
175 assert!(msg.contains("Float64"));
176 assert!(msg.contains("String"));
177 }
178
179 #[test]
180 fn error_source_chain() {
181 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
182 let err = PlotlarsError::Io {
183 path: "x".to_string(),
184 source: io_err,
185 };
186 assert!(std::error::Error::source(&err).is_some());
187
188 let err = PlotlarsError::ColumnNotFound {
189 column: "x".to_string(),
190 available: vec![],
191 };
192 assert!(std::error::Error::source(&err).is_none());
193 }
194}