//! Error types for Attune services //! //! This module provides a unified error handling approach across all services. use thiserror::Error; use crate::mq::MqError; /// Result type alias using Attune's Error type pub type Result = std::result::Result; /// Main error type for Attune services #[derive(Debug, Error)] pub enum Error { /// Database errors #[error("Database error: {0}")] Database(#[from] sqlx::Error), /// Serialization/deserialization errors #[error("Serialization error: {0}")] Serialization(#[from] serde_json::Error), /// I/O errors #[error("I/O error: {0}")] Io(String), /// Validation errors #[error("Validation error: {0}")] Validation(String), /// Not found errors #[error("Not found: {entity} with {field}={value}")] NotFound { entity: String, field: String, value: String, }, /// Already exists errors #[error("Already exists: {entity} with {field}={value}")] AlreadyExists { entity: String, field: String, value: String, }, /// Invalid state errors #[error("Invalid state: {0}")] InvalidState(String), /// Permission denied errors #[error("Permission denied: {0}")] PermissionDenied(String), /// Authentication errors #[error("Authentication failed: {0}")] AuthenticationFailed(String), /// Configuration errors #[error("Configuration error: {0}")] Configuration(String), /// Encryption/decryption errors #[error("Encryption error: {0}")] Encryption(String), /// Timeout errors #[error("Operation timed out: {0}")] Timeout(String), /// External service errors #[error("External service error: {0}")] ExternalService(String), /// Worker errors #[error("Worker error: {0}")] Worker(String), /// Execution errors #[error("Execution error: {0}")] Execution(String), /// Schema validation errors #[error("Schema validation error: {0}")] SchemaValidation(String), /// Generic internal errors #[error("Internal error: {0}")] Internal(String), /// Wrapped anyhow errors for compatibility #[error(transparent)] Other(#[from] anyhow::Error), } impl Error { /// Create a NotFound error pub fn not_found( entity: impl Into, field: impl Into, value: impl Into, ) -> Self { Self::NotFound { entity: entity.into(), field: field.into(), value: value.into(), } } /// Create an AlreadyExists error pub fn already_exists( entity: impl Into, field: impl Into, value: impl Into, ) -> Self { Self::AlreadyExists { entity: entity.into(), field: field.into(), value: value.into(), } } /// Create a Validation error pub fn validation(msg: impl Into) -> Self { Self::Validation(msg.into()) } /// Create an InvalidState error pub fn invalid_state(msg: impl Into) -> Self { Self::InvalidState(msg.into()) } /// Create a PermissionDenied error pub fn permission_denied(msg: impl Into) -> Self { Self::PermissionDenied(msg.into()) } /// Create an AuthenticationFailed error pub fn authentication_failed(msg: impl Into) -> Self { Self::AuthenticationFailed(msg.into()) } /// Create a Configuration error pub fn configuration(msg: impl Into) -> Self { Self::Configuration(msg.into()) } /// Create an Encryption error pub fn encryption(msg: impl Into) -> Self { Self::Encryption(msg.into()) } /// Create a Timeout error pub fn timeout(msg: impl Into) -> Self { Self::Timeout(msg.into()) } /// Create an ExternalService error pub fn external_service(msg: impl Into) -> Self { Self::ExternalService(msg.into()) } /// Create a Worker error pub fn worker(msg: impl Into) -> Self { Self::Worker(msg.into()) } /// Create an Execution error pub fn execution(msg: impl Into) -> Self { Self::Execution(msg.into()) } /// Create a SchemaValidation error pub fn schema_validation(msg: impl Into) -> Self { Self::SchemaValidation(msg.into()) } /// Create an Internal error pub fn internal(msg: impl Into) -> Self { Self::Internal(msg.into()) } /// Create an I/O error pub fn io(msg: impl Into) -> Self { Self::Io(msg.into()) } /// Check if this is a database error pub fn is_database(&self) -> bool { matches!(self, Self::Database(_)) } /// Check if this is a not found error pub fn is_not_found(&self) -> bool { matches!(self, Self::NotFound { .. }) } /// Check if this is an authentication error pub fn is_auth_error(&self) -> bool { matches!( self, Self::AuthenticationFailed(_) | Self::PermissionDenied(_) ) } } /// Convert MqError to Error impl From for Error { fn from(err: MqError) -> Self { Self::Internal(format!("Message queue error: {}", err)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_not_found_error() { let err = Error::not_found("Pack", "ref", "mypack"); assert!(err.is_not_found()); assert_eq!(err.to_string(), "Not found: Pack with ref=mypack"); } #[test] fn test_already_exists_error() { let err = Error::already_exists("Action", "ref", "myaction"); assert_eq!(err.to_string(), "Already exists: Action with ref=myaction"); } #[test] fn test_validation_error() { let err = Error::validation("Invalid input"); assert_eq!(err.to_string(), "Validation error: Invalid input"); } #[test] fn test_is_auth_error() { let err1 = Error::authentication_failed("Invalid token"); assert!(err1.is_auth_error()); let err2 = Error::permission_denied("No access"); assert!(err2.is_auth_error()); let err3 = Error::validation("Bad input"); assert!(!err3.is_auth_error()); } }