use thiserror::Error;
#[derive(Debug, Error)]
pub enum OracleError {
#[error("Failed to load patterns from {path}: {source}")]
LoadError {
path: String,
#[source]
source: std::io::Error,
},
#[error("Failed to save patterns to {path}: {source}")]
SaveError {
path: String,
#[source]
source: std::io::Error,
},
#[error("Invalid pattern format: {0}")]
InvalidPattern(String),
#[error("Pattern store error: {0}")]
PatternStoreError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Failed to apply diff: {0}")]
DiffError(String),
#[error("Export error: {0}")]
ExportError(String),
}
impl From<std::io::Error> for OracleError {
fn from(e: std::io::Error) -> Self {
Self::LoadError { path: "<unknown>".into(), source: e }
}
}
impl From<toml::de::Error> for OracleError {
fn from(e: toml::de::Error) -> Self {
Self::ConfigError(e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_error_construction() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let err = OracleError::LoadError { path: "/tmp/test.apr".into(), source: io_err };
assert!(matches!(err, OracleError::LoadError { .. }));
}
#[test]
fn test_save_error_construction() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
let err = OracleError::SaveError { path: "/etc/test.apr".into(), source: io_err };
assert!(matches!(err, OracleError::SaveError { .. }));
}
#[test]
fn test_invalid_pattern_error() {
let err = OracleError::InvalidPattern("malformed pattern syntax".into());
assert!(matches!(err, OracleError::InvalidPattern(_)));
}
#[test]
fn test_pattern_store_error() {
let err = OracleError::PatternStoreError("store corruption detected".into());
assert!(matches!(err, OracleError::PatternStoreError(_)));
}
#[test]
fn test_config_error() {
let err = OracleError::ConfigError("invalid threshold value".into());
assert!(matches!(err, OracleError::ConfigError(_)));
}
#[test]
fn test_diff_error() {
let err = OracleError::DiffError("hunk mismatch at line 42".into());
assert!(matches!(err, OracleError::DiffError(_)));
}
#[test]
fn test_export_error() {
let err = OracleError::ExportError("failed to write parquet".into());
assert!(matches!(err, OracleError::ExportError(_)));
}
#[test]
fn test_load_error_display() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let err = OracleError::LoadError { path: "/tmp/patterns.apr".into(), source: io_err };
let msg = format!("{}", err);
assert!(msg.contains("/tmp/patterns.apr"));
assert!(msg.contains("file not found"));
}
#[test]
fn test_save_error_display() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
let err = OracleError::SaveError { path: "/etc/patterns.apr".into(), source: io_err };
let msg = format!("{}", err);
assert!(msg.contains("/etc/patterns.apr"));
assert!(msg.contains("access denied"));
}
#[test]
fn test_invalid_pattern_display() {
let err = OracleError::InvalidPattern("missing error_code field".into());
let msg = format!("{}", err);
assert!(msg.contains("Invalid pattern format"));
assert!(msg.contains("missing error_code field"));
}
#[test]
fn test_pattern_store_error_display() {
let err = OracleError::PatternStoreError("index corruption".into());
let msg = format!("{}", err);
assert!(msg.contains("Pattern store error"));
assert!(msg.contains("index corruption"));
}
#[test]
fn test_config_error_display() {
let err = OracleError::ConfigError("invalid TOML syntax".into());
let msg = format!("{}", err);
assert!(msg.contains("Configuration error"));
assert!(msg.contains("invalid TOML syntax"));
}
#[test]
fn test_diff_error_display() {
let err = OracleError::DiffError("context mismatch".into());
let msg = format!("{}", err);
assert!(msg.contains("Failed to apply diff"));
assert!(msg.contains("context mismatch"));
}
#[test]
fn test_export_error_display() {
let err = OracleError::ExportError("arrow schema error".into());
let msg = format!("{}", err);
assert!(msg.contains("Export error"));
assert!(msg.contains("arrow schema error"));
}
#[test]
fn test_from_io_error() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "no such file");
let oracle_err: OracleError = io_err.into();
match oracle_err {
OracleError::LoadError { path, source } => {
assert_eq!(path, "<unknown>");
assert_eq!(source.kind(), std::io::ErrorKind::NotFound);
}
_ => panic!("Expected LoadError variant"),
}
}
#[test]
fn test_from_io_error_preserves_error_kind() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
let oracle_err: OracleError = io_err.into();
if let OracleError::LoadError { source, .. } = oracle_err {
assert_eq!(source.kind(), std::io::ErrorKind::PermissionDenied);
} else {
panic!("Expected LoadError variant");
}
}
#[test]
fn test_from_toml_error() {
let toml_result: Result<toml::Value, _> = toml::from_str("invalid = [toml");
let toml_err = toml_result.unwrap_err();
let oracle_err: OracleError = toml_err.into();
match oracle_err {
OracleError::ConfigError(msg) => {
assert!(!msg.is_empty());
}
_ => panic!("Expected ConfigError variant"),
}
}
#[test]
fn test_debug_formatting() {
let err = OracleError::InvalidPattern("test".into());
let debug_str = format!("{:?}", err);
assert!(debug_str.contains("InvalidPattern"));
}
#[test]
fn test_load_error_source_chain() {
use std::error::Error;
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "underlying error");
let err = OracleError::LoadError { path: "/test".into(), source: io_err };
assert!(err.source().is_some());
}
#[test]
fn test_save_error_source_chain() {
use std::error::Error;
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk full");
let err = OracleError::SaveError { path: "/test".into(), source: io_err };
assert!(err.source().is_some());
}
#[test]
fn test_simple_errors_no_source() {
use std::error::Error;
let err = OracleError::InvalidPattern("test".into());
assert!(err.source().is_none());
let err = OracleError::PatternStoreError("test".into());
assert!(err.source().is_none());
let err = OracleError::ConfigError("test".into());
assert!(err.source().is_none());
let err = OracleError::DiffError("test".into());
assert!(err.source().is_none());
let err = OracleError::ExportError("test".into());
assert!(err.source().is_none());
}
}