use std::fmt;
#[derive(Debug)]
#[non_exhaustive]
pub enum PlotlarsError {
Io {
path: String,
source: std::io::Error,
},
CsvParse {
path: String,
source: Box<dyn std::error::Error + Send + Sync>,
},
ParquetParse {
path: String,
source: Box<dyn std::error::Error + Send + Sync>,
},
#[cfg(feature = "format-json")]
JsonParse {
path: String,
source: Box<dyn std::error::Error + Send + Sync>,
},
#[cfg(feature = "format-excel")]
ExcelParse {
path: String,
source: Box<dyn std::error::Error + Send + Sync>,
},
ColumnNotFound {
column: String,
available: Vec<String>,
},
TypeMismatch {
column: String,
expected: String,
actual: String,
},
PlotBuild { message: String },
}
impl fmt::Display for PlotlarsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io { path, source } => {
write!(f, "I/O error reading '{}': {}", path, source)
}
Self::CsvParse { path, source } => {
write!(f, "failed to parse CSV '{}': {}", path, source)
}
Self::ParquetParse { path, source } => {
write!(f, "failed to parse Parquet '{}': {}", path, source)
}
#[cfg(feature = "format-json")]
Self::JsonParse { path, source } => {
write!(f, "failed to parse JSON '{}': {}", path, source)
}
#[cfg(feature = "format-excel")]
Self::ExcelParse { path, source } => {
write!(f, "failed to parse Excel '{}': {}", path, source)
}
Self::ColumnNotFound { column, available } => {
write!(
f,
"column '{}' not found; available columns: [{}]",
column,
available.join(", ")
)
}
Self::TypeMismatch {
column,
expected,
actual,
} => {
write!(
f,
"column '{}': expected type {}, found {}",
column, expected, actual
)
}
Self::PlotBuild { message } => {
write!(f, "plot construction error: {}", message)
}
}
}
}
impl std::error::Error for PlotlarsError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io { source, .. } => Some(source),
Self::CsvParse { source, .. } => Some(source.as_ref()),
Self::ParquetParse { source, .. } => Some(source.as_ref()),
#[cfg(feature = "format-json")]
Self::JsonParse { source, .. } => Some(source.as_ref()),
#[cfg(feature = "format-excel")]
Self::ExcelParse { source, .. } => Some(source.as_ref()),
Self::ColumnNotFound { .. } | Self::TypeMismatch { .. } | Self::PlotBuild { .. } => {
None
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_io_error() {
let err = PlotlarsError::Io {
path: "test.csv".to_string(),
source: std::io::Error::new(std::io::ErrorKind::NotFound, "not found"),
};
assert!(err.to_string().contains("test.csv"));
assert!(err.to_string().contains("not found"));
}
#[test]
fn display_csv_parse_error() {
let err = PlotlarsError::CsvParse {
path: "bad.csv".to_string(),
source: "invalid data".into(),
};
assert!(err.to_string().contains("bad.csv"));
}
#[test]
fn display_column_not_found() {
let err = PlotlarsError::ColumnNotFound {
column: "missing".to_string(),
available: vec!["a".to_string(), "b".to_string()],
};
let msg = err.to_string();
assert!(msg.contains("missing"));
assert!(msg.contains("a, b"));
}
#[test]
fn display_type_mismatch() {
let err = PlotlarsError::TypeMismatch {
column: "x".to_string(),
expected: "Float64".to_string(),
actual: "String".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("Float64"));
assert!(msg.contains("String"));
}
#[test]
fn error_source_chain() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
let err = PlotlarsError::Io {
path: "x".to_string(),
source: io_err,
};
assert!(std::error::Error::source(&err).is_some());
let err = PlotlarsError::ColumnNotFound {
column: "x".to_string(),
available: vec![],
};
assert!(std::error::Error::source(&err).is_none());
}
}