avl_console/
error.rs

1//! Error types for AVL Console
2
3use axum::{
4    http::StatusCode,
5    response::{IntoResponse, Response},
6    Json,
7};
8use serde::Serialize;
9
10pub type Result<T> = std::result::Result<T, ConsoleError>;
11
12/// Main error type for AVL Console operations
13#[derive(Debug, thiserror::Error)]
14pub enum ConsoleError {
15    #[error("Authentication failed: {0}")]
16    Authentication(String),
17
18    #[error("Authorization failed: {0}")]
19    Authorization(String),
20
21    #[error("Database error: {0}")]
22    Database(String),
23
24    #[error("Storage error: {0}")]
25    Storage(String),
26
27    #[error("Server error: {0}")]
28    Server(String),
29
30    #[error("Configuration error: {0}")]
31    Config(String),
32
33    #[error("Invalid input: {0}")]
34    InvalidInput(String),
35
36    #[error("Resource not found: {0}")]
37    NotFound(String),
38
39    #[error("Rate limit exceeded")]
40    RateLimitExceeded,
41
42    #[error("WebSocket error: {0}")]
43    WebSocket(String),
44
45    #[error("Template error: {0}")]
46    Template(String),
47
48    #[error("Internal error: {0}")]
49    Internal(String),
50}
51
52#[derive(Serialize)]
53struct ErrorResponse {
54    error: String,
55    message: String,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    details: Option<String>,
58}
59
60impl IntoResponse for ConsoleError {
61    fn into_response(self) -> Response {
62        let (status, error_type, message) = match &self {
63            ConsoleError::Authentication(_) => (
64                StatusCode::UNAUTHORIZED,
65                "authentication_error",
66                self.to_string(),
67            ),
68            ConsoleError::Authorization(_) => (
69                StatusCode::FORBIDDEN,
70                "authorization_error",
71                self.to_string(),
72            ),
73            ConsoleError::Database(_) | ConsoleError::Storage(_) => (
74                StatusCode::BAD_GATEWAY,
75                "service_error",
76                self.to_string(),
77            ),
78            ConsoleError::InvalidInput(_) => (
79                StatusCode::BAD_REQUEST,
80                "invalid_input",
81                self.to_string(),
82            ),
83            ConsoleError::NotFound(_) => (
84                StatusCode::NOT_FOUND,
85                "not_found",
86                self.to_string(),
87            ),
88            ConsoleError::RateLimitExceeded => (
89                StatusCode::TOO_MANY_REQUESTS,
90                "rate_limit_exceeded",
91                "Too many requests. Please try again later.".to_string(),
92            ),
93            ConsoleError::WebSocket(_) => (
94                StatusCode::BAD_REQUEST,
95                "websocket_error",
96                self.to_string(),
97            ),
98            ConsoleError::Template(_) => (
99                StatusCode::INTERNAL_SERVER_ERROR,
100                "template_error",
101                "Failed to render page".to_string(),
102            ),
103            _ => (
104                StatusCode::INTERNAL_SERVER_ERROR,
105                "internal_error",
106                "An internal error occurred".to_string(),
107            ),
108        };
109
110        let error_response = ErrorResponse {
111            error: error_type.to_string(),
112            message,
113            details: None,
114        };
115
116        (status, Json(error_response)).into_response()
117    }
118}
119
120impl From<anyhow::Error> for ConsoleError {
121    fn from(err: anyhow::Error) -> Self {
122        ConsoleError::Internal(err.to_string())
123    }
124}
125
126impl From<serde_json::Error> for ConsoleError {
127    fn from(err: serde_json::Error) -> Self {
128        ConsoleError::InvalidInput(err.to_string())
129    }
130}