Skip to main content

uls_api/
error.rs

1//! API error types and JSON error responses.
2
3use axum::http::StatusCode;
4use axum::response::{IntoResponse, Response};
5use axum::Json;
6use serde::Serialize;
7
8/// JSON error response body.
9#[derive(Debug, Serialize)]
10pub struct ErrorResponse {
11    pub error: String,
12    pub message: String,
13}
14
15/// API-level error type.
16#[derive(Debug, thiserror::Error)]
17pub enum ApiError {
18    #[error("not found: {0}")]
19    NotFound(String),
20
21    #[error("bad request: {0}")]
22    BadRequest(String),
23
24    #[error("database not initialized")]
25    NotInitialized,
26
27    #[error("internal error: {0}")]
28    Internal(String),
29}
30
31impl From<uls_query::QueryError> for ApiError {
32    fn from(err: uls_query::QueryError) -> Self {
33        match err {
34            uls_query::QueryError::NotInitialized => ApiError::NotInitialized,
35            other => ApiError::Internal(other.to_string()),
36        }
37    }
38}
39
40impl IntoResponse for ApiError {
41    fn into_response(self) -> Response {
42        let (status, error_key, message) = match &self {
43            ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, "not_found", msg.clone()),
44            ApiError::BadRequest(msg) => (StatusCode::BAD_REQUEST, "bad_request", msg.clone()),
45            ApiError::NotInitialized => (
46                StatusCode::SERVICE_UNAVAILABLE,
47                "not_initialized",
48                "Database not initialized. Run 'uls update' first.".to_string(),
49            ),
50            ApiError::Internal(msg) => (
51                StatusCode::INTERNAL_SERVER_ERROR,
52                "internal_error",
53                msg.clone(),
54            ),
55        };
56
57        let body = ErrorResponse {
58            error: error_key.to_string(),
59            message,
60        };
61
62        (status, Json(body)).into_response()
63    }
64}