zagens_runtime_api/
error.rs1use axum::Json;
4use axum::http::StatusCode;
5use axum::response::{IntoResponse, Response};
6
7#[derive(Debug, Clone)]
8pub struct ApiError {
9 pub status: StatusCode,
10 pub message: String,
11}
12
13impl ApiError {
14 pub fn bad_request(message: impl Into<String>) -> Self {
15 Self {
16 status: StatusCode::BAD_REQUEST,
17 message: message.into(),
18 }
19 }
20
21 pub fn not_found(message: impl Into<String>) -> Self {
22 Self {
23 status: StatusCode::NOT_FOUND,
24 message: message.into(),
25 }
26 }
27
28 pub fn internal(message: impl Into<String>) -> Self {
29 Self {
30 status: StatusCode::INTERNAL_SERVER_ERROR,
31 message: message.into(),
32 }
33 }
34
35 pub fn forbidden(message: impl Into<String>) -> Self {
36 Self {
37 status: StatusCode::FORBIDDEN,
38 message: message.into(),
39 }
40 }
41
42 pub fn conflict(message: impl Into<String>) -> Self {
43 Self {
44 status: StatusCode::CONFLICT,
45 message: message.into(),
46 }
47 }
48}
49
50impl From<std::io::Error> for ApiError {
51 fn from(e: std::io::Error) -> Self {
52 ApiError::internal(e.to_string())
53 }
54}
55
56impl IntoResponse for ApiError {
57 fn into_response(self) -> Response {
58 use zagens_core::error_taxonomy::ErrorEnvelope;
59
60 let status_recoverable = matches!(
61 self.status,
62 StatusCode::INTERNAL_SERVER_ERROR
63 | StatusCode::BAD_GATEWAY
64 | StatusCode::SERVICE_UNAVAILABLE
65 | StatusCode::GATEWAY_TIMEOUT
66 | StatusCode::REQUEST_TIMEOUT
67 | StatusCode::TOO_MANY_REQUESTS
68 );
69 let mut envelope = ErrorEnvelope::classify(&self.message, status_recoverable);
70 envelope.recoverable = envelope.recoverable || status_recoverable;
71 let body = envelope.to_wire_error_body(self.status.as_u16());
72 (self.status, Json(body)).into_response()
73 }
74}