character_service/
error.rs1use axum::{
2 Json,
3 http::{StatusCode, header},
4 response::{IntoResponse, Response},
5};
6use edda_types::{ActionError, Character, ValidationError};
7use serde_json::json;
8
9use crate::routes::etag;
10
11#[derive(Debug, thiserror::Error)]
13pub enum ApiError {
14 #[error("character not found")]
15 NotFound,
16 #[error("{0}")]
17 BadRequest(String),
18 #[error("{0}")]
19 Forbidden(String),
20 #[error("{0}")]
22 Conflict(String),
23 #[error("an If-Match header with the character's current ETag is required")]
24 PreconditionRequired,
25 #[error("character has been modified (current version is {})", .0.version)]
28 VersionMismatch(Box<Character>),
29 #[error(transparent)]
30 Validation(#[from] ValidationError),
31 #[error(transparent)]
32 Action(#[from] ActionError),
33 #[error("database error: {0}")]
34 Database(#[from] sqlx::Error),
35 #[error("corrupt stored data: {0}")]
36 Corrupt(String),
37}
38
39impl IntoResponse for ApiError {
40 fn into_response(self) -> Response {
41 let status = match &self {
42 ApiError::NotFound => StatusCode::NOT_FOUND,
43 ApiError::BadRequest(_) => StatusCode::BAD_REQUEST,
44 ApiError::Forbidden(_) => StatusCode::FORBIDDEN,
45 ApiError::Conflict(_) => StatusCode::CONFLICT,
46 ApiError::PreconditionRequired => StatusCode::PRECONDITION_REQUIRED,
47 ApiError::VersionMismatch(_) => StatusCode::PRECONDITION_FAILED,
48 ApiError::Action(e) if e.is_exhausted() => StatusCode::CONFLICT,
51 ApiError::Validation(_) | ApiError::Action(_) => StatusCode::UNPROCESSABLE_ENTITY,
52 ApiError::Database(_) | ApiError::Corrupt(_) => StatusCode::INTERNAL_SERVER_ERROR,
53 };
54 let message = edda_server::error_message(status, &self);
55 match self {
56 ApiError::VersionMismatch(current) => (
57 status,
58 [(header::ETAG, etag(¤t))],
59 Json(json!({ "error": message, "current": current })),
60 )
61 .into_response(),
62 _ => (status, Json(json!({ "error": message }))).into_response(),
63 }
64 }
65}