slack status without the slack status.zzstoatzz.io/
quickslice
1use actix_web::{HttpResponse, error::ResponseError, http::StatusCode}; 2use std::fmt; 3 4#[derive(Debug)] 5pub enum AppError { 6 InternalError(String), 7 DatabaseError(String), 8 AuthenticationError(String), 9 #[allow(dead_code)] // Keep for potential future use 10 ValidationError(String), 11 #[allow(dead_code)] // Keep for potential future use 12 NotFound(String), 13 RateLimitExceeded, 14} 15 16impl fmt::Display for AppError { 17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 match self { 19 AppError::InternalError(msg) => write!(f, "Internal server error: {}", msg), 20 AppError::DatabaseError(msg) => write!(f, "Database error: {}", msg), 21 AppError::AuthenticationError(msg) => write!(f, "Authentication error: {}", msg), 22 AppError::ValidationError(msg) => write!(f, "Validation error: {}", msg), 23 AppError::NotFound(msg) => write!(f, "Not found: {}", msg), 24 AppError::RateLimitExceeded => write!(f, "Rate limit exceeded"), 25 } 26 } 27} 28 29impl ResponseError for AppError { 30 fn error_response(&self) -> HttpResponse { 31 let (status_code, error_message) = match self { 32 AppError::InternalError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()), 33 AppError::DatabaseError(_) => ( 34 StatusCode::INTERNAL_SERVER_ERROR, 35 "Database error occurred".to_string(), 36 ), 37 AppError::AuthenticationError(msg) => (StatusCode::UNAUTHORIZED, msg.clone()), 38 AppError::ValidationError(msg) => (StatusCode::BAD_REQUEST, msg.clone()), 39 AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()), 40 AppError::RateLimitExceeded => ( 41 StatusCode::TOO_MANY_REQUESTS, 42 "Rate limit exceeded. Please try again later.".to_string(), 43 ), 44 }; 45 46 HttpResponse::build(status_code).body(format!( 47 "Error {}: {}", 48 status_code.as_u16(), 49 error_message 50 )) 51 } 52 53 fn status_code(&self) -> StatusCode { 54 match self { 55 AppError::InternalError(_) | AppError::DatabaseError(_) => { 56 StatusCode::INTERNAL_SERVER_ERROR 57 } 58 AppError::AuthenticationError(_) => StatusCode::UNAUTHORIZED, 59 AppError::ValidationError(_) => StatusCode::BAD_REQUEST, 60 AppError::NotFound(_) => StatusCode::NOT_FOUND, 61 AppError::RateLimitExceeded => StatusCode::TOO_MANY_REQUESTS, 62 } 63 } 64} 65 66// Conversion helpers 67impl From<async_sqlite::Error> for AppError { 68 fn from(err: async_sqlite::Error) -> Self { 69 AppError::DatabaseError(err.to_string()) 70 } 71} 72 73impl From<serde_json::Error> for AppError { 74 fn from(err: serde_json::Error) -> Self { 75 AppError::InternalError(err.to_string()) 76 } 77} 78 79// Helper function to wrap results - removed as unused 80// If needed in the future, use: result.map_err(|e| ErrorInternalServerError(e)) 81 82#[cfg(test)] 83mod tests { 84 use super::*; 85 86 #[test] 87 fn test_error_display() { 88 let err = AppError::ValidationError("Invalid input".to_string()); 89 assert_eq!(err.to_string(), "Validation error: Invalid input"); 90 91 let err = AppError::RateLimitExceeded; 92 assert_eq!(err.to_string(), "Rate limit exceeded"); 93 } 94 95 #[test] 96 fn test_error_status_codes() { 97 assert_eq!( 98 AppError::InternalError("test".to_string()).status_code(), 99 StatusCode::INTERNAL_SERVER_ERROR 100 ); 101 assert_eq!( 102 AppError::ValidationError("test".to_string()).status_code(), 103 StatusCode::BAD_REQUEST 104 ); 105 assert_eq!( 106 AppError::AuthenticationError("test".to_string()).status_code(), 107 StatusCode::UNAUTHORIZED 108 ); 109 assert_eq!( 110 AppError::NotFound("test".to_string()).status_code(), 111 StatusCode::NOT_FOUND 112 ); 113 assert_eq!( 114 AppError::RateLimitExceeded.status_code(), 115 StatusCode::TOO_MANY_REQUESTS 116 ); 117 } 118}