1use axum::response::{IntoResponse, Response};
2use axum::http::StatusCode;
3use axum::Json;
4use serde_json::json;
5
6#[derive(Debug)]
11pub enum ActionError {
12 BadRequest(String),
13 Unauthorized,
14 Forbidden,
15 NotFound,
16 Conflict(String),
17 Internal(String),
18}
19
20impl std::fmt::Display for ActionError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 ActionError::BadRequest(msg) => write!(f, "bad request: {msg}"),
24 ActionError::Unauthorized => f.write_str("unauthorized"),
25 ActionError::Forbidden => f.write_str("forbidden"),
26 ActionError::NotFound => f.write_str("not found"),
27 ActionError::Conflict(msg) => write!(f, "conflict: {msg}"),
28 ActionError::Internal(msg) => write!(f, "internal error: {msg}"),
29 }
30 }
31}
32
33impl std::error::Error for ActionError {}
34
35impl IntoResponse for ActionError {
36 fn into_response(self) -> Response {
37 let (status, code, message) = match &self {
38 ActionError::BadRequest(msg) => (StatusCode::BAD_REQUEST, "BAD_REQUEST", msg.as_str()),
39 ActionError::Unauthorized => (StatusCode::UNAUTHORIZED, "UNAUTHORIZED", "unauthorized"),
40 ActionError::Forbidden => (StatusCode::FORBIDDEN, "FORBIDDEN", "forbidden"),
41 ActionError::NotFound => (StatusCode::NOT_FOUND, "NOT_FOUND", "not found"),
42 ActionError::Conflict(msg) => (StatusCode::CONFLICT, "CONFLICT", msg.as_str()),
43 ActionError::Internal(msg) => {
44 tracing::error!(error = %msg, "action internal error");
45 (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL", "internal server error")
46 }
47 };
48 (status, Json(json!({ "error": message, "code": code }))).into_response()
49 }
50}