Skip to main content

zagens_runtime_api/
error.rs

1//! Shared HTTP error envelope for runtime API handlers (D16 E1-c phase 3).
2
3use 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}