1use axum::Json;
7use axum::http::StatusCode;
8use axum::response::{IntoResponse, Response};
9
10#[derive(Debug)]
11pub enum AppError {
12 BadRequest(String),
13 NotFound(String),
14 Conflict(String),
15 Unauthorized,
16 NotConnected {
18 node: Option<String>,
19 },
20 Unavailable(String),
21 GatewayTimeout(String),
22 Upstream(ocpp_core::RpcError),
24 Internal(String),
25}
26
27impl std::fmt::Display for AppError {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{self:?}")
30 }
31}
32
33impl From<sqlx::Error> for AppError {
34 fn from(e: sqlx::Error) -> Self {
35 if let sqlx::Error::Database(db) = &e
36 && db.is_unique_violation()
37 {
38 return AppError::Conflict(db.message().to_owned());
39 }
40 if let sqlx::Error::Database(db) = &e
41 && db.is_foreign_key_violation()
42 {
43 return AppError::Conflict(format!("still referenced: {}", db.message()));
44 }
45 if let sqlx::Error::Database(db) = &e
46 && db.is_check_violation()
47 {
48 return AppError::BadRequest(db.message().to_owned());
49 }
50 AppError::Internal(e.to_string())
51 }
52}
53
54impl IntoResponse for AppError {
55 fn into_response(self) -> Response {
56 let (status, body) = match self {
57 AppError::BadRequest(m) => (
58 StatusCode::BAD_REQUEST,
59 serde_json::json!({ "error": "bad_request", "message": m }),
60 ),
61 AppError::NotFound(m) => (
62 StatusCode::NOT_FOUND,
63 serde_json::json!({ "error": "not_found", "message": m }),
64 ),
65 AppError::Conflict(m) => (
66 StatusCode::CONFLICT,
67 serde_json::json!({ "error": "conflict", "message": m }),
68 ),
69 AppError::Unauthorized => (
70 StatusCode::UNAUTHORIZED,
71 serde_json::json!({ "error": "unauthorized" }),
72 ),
73 AppError::NotConnected { node } => (
74 StatusCode::CONFLICT,
75 serde_json::json!({ "error": "station_not_connected", "message": "station is not connected to this node", "connected_node": node }),
76 ),
77 AppError::Unavailable(m) => (
78 StatusCode::SERVICE_UNAVAILABLE,
79 serde_json::json!({ "error": "unavailable", "message": m }),
80 ),
81 AppError::GatewayTimeout(m) => (
82 StatusCode::GATEWAY_TIMEOUT,
83 serde_json::json!({ "error": "timeout", "message": m }),
84 ),
85 AppError::Upstream(e) => (
86 StatusCode::BAD_GATEWAY,
87 serde_json::json!({ "error": "call_error", "error_code": e.code.to_string(), "description": e.description, "details": e.details }),
88 ),
89 AppError::Internal(m) => {
90 tracing::error!(error = %m, "internal error");
91 (
92 StatusCode::INTERNAL_SERVER_ERROR,
93 serde_json::json!({ "error": "internal" }),
94 )
95 }
96 };
97 (status, Json(body)).into_response()
98 }
99}