use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ErrorCode {
ValidationError,
InvalidUuid,
InvalidJson,
NotFound,
Unauthorized,
Forbidden,
Conflict,
UnprocessableEntity,
JsonExtraction,
BadRequest,
MethodNotAllowed,
InternalError,
ServiceUnavailable,
RateLimitExceeded,
DatabaseNotFound,
DatabaseConfig,
DatabaseError,
DatabaseIo,
DatabaseTls,
DatabaseProtocol,
DatabaseTypeNotFound,
DatabaseColumnIndex,
DatabaseColumnNotFound,
DatabaseDecode,
DatabaseEncode,
DatabaseDriver,
DatabasePoolTimeout,
DatabasePoolClosed,
DatabaseWorkerCrashed,
DatabaseMigration,
DatabaseUnhandled,
MigrationError,
IoError,
SerdeJsonError,
}
impl ErrorCode {
pub fn as_str(&self) -> &'static str {
match self {
Self::ValidationError => "VALIDATION_ERROR",
Self::InvalidUuid => "INVALID_UUID",
Self::InvalidJson => "INVALID_JSON",
Self::NotFound => "NOT_FOUND",
Self::Unauthorized => "UNAUTHORIZED",
Self::Forbidden => "FORBIDDEN",
Self::Conflict => "CONFLICT",
Self::UnprocessableEntity => "UNPROCESSABLE_ENTITY",
Self::JsonExtraction => "JSON_EXTRACTION",
Self::BadRequest => "BAD_REQUEST",
Self::MethodNotAllowed => "METHOD_NOT_ALLOWED",
Self::InternalError => "INTERNAL_ERROR",
Self::ServiceUnavailable => "SERVICE_UNAVAILABLE",
Self::RateLimitExceeded => "RATE_LIMIT_EXCEEDED",
Self::DatabaseNotFound => "DATABASE_NOT_FOUND",
Self::DatabaseConfig => "DATABASE_CONFIG",
Self::DatabaseError => "DATABASE_ERROR",
Self::DatabaseIo => "DATABASE_IO",
Self::DatabaseTls => "DATABASE_TLS",
Self::DatabaseProtocol => "DATABASE_PROTOCOL",
Self::DatabaseTypeNotFound => "DATABASE_TYPE_NOT_FOUND",
Self::DatabaseColumnIndex => "DATABASE_COLUMN_INDEX",
Self::DatabaseColumnNotFound => "DATABASE_COLUMN_NOT_FOUND",
Self::DatabaseDecode => "DATABASE_DECODE",
Self::DatabaseEncode => "DATABASE_ENCODE",
Self::DatabaseDriver => "DATABASE_DRIVER",
Self::DatabasePoolTimeout => "DATABASE_POOL_TIMEOUT",
Self::DatabasePoolClosed => "DATABASE_POOL_CLOSED",
Self::DatabaseWorkerCrashed => "DATABASE_WORKER_CRASHED",
Self::DatabaseMigration => "DATABASE_MIGRATION",
Self::DatabaseUnhandled => "DATABASE_UNHANDLED",
Self::MigrationError => "MIGRATION_ERROR",
Self::IoError => "IO_ERROR",
Self::SerdeJsonError => "SERDE_JSON_ERROR",
}
}
pub fn code(&self) -> i32 {
match self {
Self::ValidationError => 1001,
Self::InvalidUuid => 1002,
Self::JsonExtraction => 1003,
Self::NotFound => 1004,
Self::InternalError => 1005,
Self::Unauthorized => 1006,
Self::Forbidden => 1007,
Self::Conflict => 1008,
Self::UnprocessableEntity => 1009,
Self::InvalidJson => 1010,
Self::ServiceUnavailable => 1011,
Self::RateLimitExceeded => 1012,
Self::BadRequest => 1013,
Self::MethodNotAllowed => 1014,
Self::DatabaseNotFound => 2001,
Self::DatabaseConfig => 2002,
Self::DatabaseError => 2003,
Self::DatabaseIo => 2004,
Self::DatabaseTls => 2005,
Self::DatabaseProtocol => 2006,
Self::DatabaseTypeNotFound => 2007,
Self::DatabaseColumnIndex => 2008,
Self::DatabaseColumnNotFound => 2009,
Self::DatabaseDecode => 2010,
Self::DatabaseEncode => 2011,
Self::DatabaseDriver => 2012,
Self::DatabasePoolTimeout => 2013,
Self::DatabasePoolClosed => 2014,
Self::DatabaseWorkerCrashed => 2015,
Self::DatabaseMigration => 2016,
Self::DatabaseUnhandled => 2099,
Self::MigrationError => 3001,
Self::IoError => 4001,
Self::SerdeJsonError => 5001,
}
}
pub fn default_message(&self) -> &'static str {
match self {
Self::ValidationError => "Request validation failed",
Self::InvalidUuid => "Invalid UUID format",
Self::InvalidJson => "Invalid JSON format",
Self::NotFound => "Resource not found",
Self::Unauthorized => "Authentication required",
Self::Forbidden => "Access forbidden",
Self::Conflict => "Resource already exists",
Self::UnprocessableEntity => "Request cannot be processed",
Self::JsonExtraction => "Failed to parse request body",
Self::BadRequest => "Bad request",
Self::MethodNotAllowed => "Method not allowed",
Self::InternalError => "An internal server error occurred",
Self::ServiceUnavailable => "Service is temporarily unavailable",
Self::RateLimitExceeded => "Rate limit exceeded",
Self::DatabaseNotFound => "Database record not found",
Self::DatabaseConfig => "Database configuration error",
Self::DatabaseError => "Database error occurred",
Self::DatabaseIo => "Database I/O error",
Self::DatabaseTls => "Database TLS error",
Self::DatabaseProtocol => "Database protocol error",
Self::DatabaseTypeNotFound => "Database type not found",
Self::DatabaseColumnIndex => "Database column index out of bounds",
Self::DatabaseColumnNotFound => "Database column not found",
Self::DatabaseDecode => "Failed to decode database response",
Self::DatabaseEncode => "Failed to encode database request",
Self::DatabaseDriver => "Database driver error",
Self::DatabasePoolTimeout => "Database connection pool timed out",
Self::DatabasePoolClosed => "Database connection pool closed",
Self::DatabaseWorkerCrashed => "Database worker crashed",
Self::DatabaseMigration => "Database migration failed",
Self::DatabaseUnhandled => "Unhandled database error",
Self::MigrationError => "Migration error",
Self::IoError => "I/O error occurred",
Self::SerdeJsonError => "JSON serialization error",
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn test_error_code_string_representation() {
assert_eq!(ErrorCode::ValidationError.as_str(), "VALIDATION_ERROR");
assert_eq!(ErrorCode::NotFound.as_str(), "NOT_FOUND");
assert_eq!(ErrorCode::DatabaseError.as_str(), "DATABASE_ERROR");
}
#[test]
fn test_error_code_integer_codes() {
assert_eq!(ErrorCode::ValidationError.code(), 1001);
assert_eq!(ErrorCode::DatabaseError.code(), 2003);
assert_eq!(ErrorCode::MigrationError.code(), 3001);
}
#[test]
fn test_error_code_messages() {
assert_eq!(
ErrorCode::ValidationError.default_message(),
"Request validation failed"
);
assert_eq!(ErrorCode::NotFound.default_message(), "Resource not found");
}
#[test]
fn test_error_code_display() {
assert_eq!(ErrorCode::ValidationError.to_string(), "VALIDATION_ERROR");
}
#[test]
fn test_error_code_serialization() {
let code = ErrorCode::ValidationError;
let json = serde_json::to_string(&code).unwrap();
assert_eq!(json, "\"VALIDATION_ERROR\"");
}
#[test]
fn test_error_code_deserialization() {
let json = "\"VALIDATION_ERROR\"";
let code: ErrorCode = serde_json::from_str(json).unwrap();
assert_eq!(code, ErrorCode::ValidationError);
}
}