1use axum::{
2 http::StatusCode,
3 response::{IntoResponse, Response},
4 Json,
5};
6use serde::Serialize;
7use thiserror::Error;
8
9#[derive(Error, Debug)]
10pub enum ProxyError {
11 #[error("HTTP error: {0}")]
12 Http(#[from] hyper::Error),
13
14 #[error("Configuration error: {0}")]
15 Config(String),
16
17 #[error("Authentication failed: {0}")]
18 Authentication(String),
19
20 #[error("Rate limit exceeded: {0}")]
21 RateLimit(String),
22
23 #[error("Validation error: {0}")]
24 Validation(String),
25
26 #[error("Request timeout")]
27 Timeout,
28
29 #[error("Bad request: {0}")]
30 BadRequest(String),
31
32 #[error("Invalid request: {0}")]
33 InvalidRequest(String),
34
35 #[error("Internal error: {0}")]
36 Internal(String),
37
38 #[error("Service unavailable: {0}")]
39 ServiceUnavailable(String),
40}
41
42#[derive(Serialize)]
44pub struct ErrorResponse {
45 pub error: ErrorDetail,
46}
47
48#[derive(Serialize)]
49pub struct ErrorDetail {
50 pub code: String,
51 pub message: String,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub details: Option<serde_json::Value>,
54}
55
56impl ProxyError {
57 fn error_code(&self) -> &str {
58 match self {
59 ProxyError::Http(_) => "HTTP_ERROR",
60 ProxyError::Config(_) => "CONFIG_ERROR",
61 ProxyError::Authentication(_) => "AUTH_ERROR",
62 ProxyError::RateLimit(_) => "RATE_LIMIT_EXCEEDED",
63 ProxyError::Validation(_) => "VALIDATION_ERROR",
64 ProxyError::Timeout => "TIMEOUT",
65 ProxyError::BadRequest(_) => "BAD_REQUEST",
66 ProxyError::InvalidRequest(_) => "INVALID_REQUEST",
67 ProxyError::Internal(_) => "INTERNAL_ERROR",
68 ProxyError::ServiceUnavailable(_) => "SERVICE_UNAVAILABLE",
69 }
70 }
71
72 fn status_code(&self) -> StatusCode {
73 match self {
74 ProxyError::Http(_) => StatusCode::BAD_GATEWAY,
75 ProxyError::Config(_) => StatusCode::INTERNAL_SERVER_ERROR,
76 ProxyError::Authentication(_) => StatusCode::UNAUTHORIZED,
77 ProxyError::RateLimit(_) => StatusCode::TOO_MANY_REQUESTS,
78 ProxyError::Validation(_) => StatusCode::BAD_REQUEST,
79 ProxyError::Timeout => StatusCode::GATEWAY_TIMEOUT,
80 ProxyError::BadRequest(_) => StatusCode::BAD_REQUEST,
81 ProxyError::InvalidRequest(_) => StatusCode::BAD_REQUEST,
82 ProxyError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
83 ProxyError::ServiceUnavailable(_) => StatusCode::SERVICE_UNAVAILABLE,
84 }
85 }
86}
87
88impl IntoResponse for ProxyError {
89 fn into_response(self) -> Response {
90 let status = self.status_code();
91 let error_response = ErrorResponse {
92 error: ErrorDetail {
93 code: self.error_code().to_string(),
94 message: self.to_string(),
95 details: None,
96 },
97 };
98
99 (status, Json(error_response)).into_response()
100 }
101}
102
103impl From<std::io::Error> for ProxyError {
105 fn from(err: std::io::Error) -> Self {
106 ProxyError::Internal(err.to_string())
107 }
108}
109
110impl From<serde_json::Error> for ProxyError {
111 fn from(err: serde_json::Error) -> Self {
112 ProxyError::BadRequest(err.to_string())
113 }
114}
115
116pub type ProxyResult<T> = Result<T, ProxyError>;