use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq)]
pub enum ProviderError {
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Network error: {0}")]
Network(String),
#[error("External service '{service}' error: {error}")]
ExternalService { service: String, error: String },
#[error("Data parsing error: {0}")]
DataParsing(String),
#[error("Authentication failed: {0}")]
Authentication(String),
#[error("Authorization failed: {0}")]
Authorization(String),
#[error("Rate limit exceeded: {0}")]
RateLimit(String),
#[error("Operation timed out: {0}")]
Timeout(String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Dependency injection failed: {0}")]
DependencyInjection(String),
#[error("Cache error: {0}")]
Cache(String),
#[error("Provider error: {0}")]
Generic(String),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum UserError {
#[error("User not found: {id}")]
NotFound { id: u32 },
#[error("User suspended: {reason}")]
Suspended { reason: String },
#[error("User deleted: {id}")]
Deleted { id: u32 },
#[error("Permission denied for user {user_id}: {action}")]
PermissionDenied { user_id: u32, action: String },
#[error("User validation failed: {field}: {reason}")]
ValidationFailed { field: String, reason: String },
#[error("Provider error: {0}")]
Provider(#[from] ProviderError),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum ApiError {
#[error("HTTP {status}: {message}")]
HttpStatus { status: u16, message: String },
#[error("JSON parsing failed: {0}")]
JsonParsing(String),
#[error("Request building failed: {0}")]
RequestBuilding(String),
#[error("Response processing failed: {0}")]
ResponseProcessing(String),
#[error("API endpoint not found: {endpoint}")]
EndpointNotFound { endpoint: String },
#[error("API version mismatch: expected {expected}, got {actual}")]
VersionMismatch { expected: String, actual: String },
#[error("Provider error: {0}")]
Provider(#[from] ProviderError),
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum DatabaseError {
#[error("Database connection failed: {0}")]
Connection(String),
#[error("Query execution failed: {query}: {error}")]
QueryExecution { query: String, error: String },
#[error("Transaction failed: {0}")]
Transaction(String),
#[error("Database migration failed: {0}")]
Migration(String),
#[error("Database constraint violation: {constraint}: {details}")]
ConstraintViolation { constraint: String, details: String },
#[error("Record not found: {table}: {id}")]
RecordNotFound { table: String, id: String },
#[error("Provider error: {0}")]
Provider(#[from] ProviderError),
}
pub type ProviderResult<T> = Result<T, ProviderError>;
pub type UserResult<T> = Result<T, UserError>;
pub type ApiResult<T> = Result<T, ApiError>;
pub type DatabaseResult<T> = Result<T, DatabaseError>;
impl From<String> for ProviderError {
fn from(error: String) -> Self {
ProviderError::Generic(error)
}
}
impl From<&str> for ProviderError {
fn from(error: &str) -> Self {
ProviderError::Generic(error.to_string())
}
}
impl From<ProviderError> for String {
fn from(error: ProviderError) -> Self {
error.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_provider_error_display() {
let error = ProviderError::InvalidInput("test input".to_string());
assert_eq!(error.to_string(), "Invalid input: test input");
}
#[test]
fn test_user_error_with_provider_error() {
let provider_error = ProviderError::Network("connection failed".to_string());
let user_error = UserError::Provider(provider_error);
assert_eq!(
user_error.to_string(),
"Provider error: Network error: connection failed"
);
}
#[test]
fn test_api_error_http_status() {
let error = ApiError::HttpStatus {
status: 404,
message: "Not Found".to_string(),
};
assert_eq!(error.to_string(), "HTTP 404: Not Found");
}
#[test]
fn test_database_error_constraint_violation() {
let error = DatabaseError::ConstraintViolation {
constraint: "unique_email".to_string(),
details: "Email already exists".to_string(),
};
assert_eq!(
error.to_string(),
"Database constraint violation: unique_email: Email already exists"
);
}
}