Skip to main content

miryad_core/rest/
error.rs

1use axum::Json;
2use axum::http::StatusCode;
3use axum::response::{IntoResponse, Response};
4use serde::Serialize;
5
6use crate::resource::HookError;
7
8#[derive(Debug, thiserror::Error)]
9pub enum RestError {
10    #[error("MRD-REST-001: resource not found")]
11    NotFound,
12    #[error("MRD-REST-002: access denied")]
13    Forbidden,
14    #[error("MRD-REST-003: database error: {0}")]
15    Database(#[from] sea_orm::DbErr),
16    /// Erreur métier applicative (hook) — jamais un `MRD-*`, cf. `HookError`.
17    #[error("{}", .0.message)]
18    Application(HookError),
19    /// Enveloppe une erreur d'un autre sous-système (ex. `auth::AuthError` dans
20    /// `rest::tokens`) dont seule une variante est réellement atteignable depuis un handler REST
21    /// — pas de `From` générique qui laisserait croire à une conversion sans perte.
22    #[error("MRD-REST-004: internal error: {0}")]
23    Internal(String),
24}
25
26#[derive(Serialize)]
27struct ApplicationErrorBody<'a> {
28    code: Option<&'a str>,
29    message: &'a str,
30}
31
32impl IntoResponse for RestError {
33    fn into_response(self) -> Response {
34        match self {
35            RestError::NotFound => (StatusCode::NOT_FOUND, self.to_string()).into_response(),
36            RestError::Forbidden => (StatusCode::FORBIDDEN, self.to_string()).into_response(),
37            RestError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
38            RestError::Application(ref err) => (
39                StatusCode::UNPROCESSABLE_ENTITY,
40                Json(ApplicationErrorBody {
41                    code: err.code.as_deref(),
42                    message: &err.message,
43                }),
44            )
45                .into_response(),
46            RestError::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response(),
47        }
48    }
49}