use thiserror::Error;
#[derive(Error, Debug)]
pub enum DrasiError {
#[error("{component_type} '{component_id}' not found")]
ComponentNotFound {
component_type: String,
component_id: String,
},
#[error("{component_type} '{component_id}' already exists")]
AlreadyExists {
component_type: String,
component_id: String,
},
#[error("Invalid configuration: {message}")]
InvalidConfig {
message: String,
},
#[error("Invalid state: {message}")]
InvalidState {
message: String,
},
#[error("Validation failed: {message}")]
Validation {
message: String,
},
#[error("Failed to {operation} {component_type} '{component_id}': {reason}")]
OperationFailed {
component_type: String,
component_id: String,
operation: String,
reason: String,
},
#[error(transparent)]
Internal(#[from] anyhow::Error),
}
impl DrasiError {
pub fn component_not_found(
component_type: impl Into<String>,
component_id: impl Into<String>,
) -> Self {
DrasiError::ComponentNotFound {
component_type: component_type.into(),
component_id: component_id.into(),
}
}
pub fn already_exists(
component_type: impl Into<String>,
component_id: impl Into<String>,
) -> Self {
DrasiError::AlreadyExists {
component_type: component_type.into(),
component_id: component_id.into(),
}
}
pub fn invalid_config(message: impl Into<String>) -> Self {
DrasiError::InvalidConfig {
message: message.into(),
}
}
pub fn invalid_state(message: impl Into<String>) -> Self {
DrasiError::InvalidState {
message: message.into(),
}
}
pub fn validation(message: impl Into<String>) -> Self {
DrasiError::Validation {
message: message.into(),
}
}
pub fn operation_failed(
component_type: impl Into<String>,
component_id: impl Into<String>,
operation: impl Into<String>,
reason: impl Into<String>,
) -> Self {
DrasiError::OperationFailed {
component_type: component_type.into(),
component_id: component_id.into(),
operation: operation.into(),
reason: reason.into(),
}
}
}
pub type Result<T> = std::result::Result<T, DrasiError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_component_not_found_display() {
let err = DrasiError::component_not_found("source", "my-source");
assert_eq!(err.to_string(), "source 'my-source' not found");
}
#[test]
fn test_already_exists_display() {
let err = DrasiError::already_exists("query", "my-query");
assert_eq!(err.to_string(), "query 'my-query' already exists");
}
#[test]
fn test_invalid_config_display() {
let err = DrasiError::invalid_config("Missing field");
assert_eq!(err.to_string(), "Invalid configuration: Missing field");
}
#[test]
fn test_invalid_state_display() {
let err = DrasiError::invalid_state("Not initialized");
assert_eq!(err.to_string(), "Invalid state: Not initialized");
}
#[test]
fn test_validation_display() {
let err = DrasiError::validation("Empty query string");
assert_eq!(err.to_string(), "Validation failed: Empty query string");
}
#[test]
fn test_operation_failed_display() {
let err =
DrasiError::operation_failed("source", "my-source", "start", "Connection refused");
assert_eq!(
err.to_string(),
"Failed to start source 'my-source': Connection refused"
);
}
#[test]
fn test_internal_error_from_anyhow() {
let anyhow_err = anyhow::anyhow!("Something went wrong");
let drasi_err: DrasiError = anyhow_err.into();
assert!(matches!(drasi_err, DrasiError::Internal(_)));
assert!(drasi_err.to_string().contains("Something went wrong"));
}
#[test]
fn test_error_pattern_matching() {
let err = DrasiError::component_not_found("source", "test-source");
match err {
DrasiError::ComponentNotFound {
component_type,
component_id,
} => {
assert_eq!(component_type, "source");
assert_eq!(component_id, "test-source");
}
_ => panic!("Expected ComponentNotFound variant"),
}
}
#[test]
fn test_internal_error_transparent() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let anyhow_err = anyhow::Error::new(io_error).context("Failed to read config");
let drasi_err: DrasiError = anyhow_err.into();
assert!(matches!(drasi_err, DrasiError::Internal(_)));
let display = drasi_err.to_string();
assert!(display.contains("Failed to read config"));
if let DrasiError::Internal(ref anyhow_err) = drasi_err {
assert!(anyhow_err.to_string().contains("Failed to read config"));
}
}
}