1use axum::{
2 http::StatusCode,
3 response::{IntoResponse, Response},
4};
5use edda_client::ClientError;
6use edda_types::combat::CombatError;
7
8#[derive(Debug, thiserror::Error)]
10pub enum ApiError {
11 #[error("combat not found")]
12 NotFound,
13 #[error("{0}")]
14 BadRequest(String),
15 #[error("{0}")]
16 Forbidden(String),
17 #[error("{0}")]
19 Conflict(String),
20 #[error("{0}")]
22 Sheet(String),
23 #[error("couldn't reach the character's sheet: {0}")]
25 SheetUnavailable(String),
26 #[error("couldn't reach the monsters: {0}")]
28 MonstersUnavailable(String),
29 #[error(transparent)]
30 Combat(#[from] CombatError),
31 #[error("database error: {0}")]
32 Database(#[from] sqlx::Error),
33 #[error("corrupt stored data: {0}")]
34 Corrupt(String),
35}
36
37impl ApiError {
38 pub fn from_sheet(error: ClientError) -> Self {
40 match error {
41 ClientError::Exhausted(message) | ClientError::Rejected(message) => {
42 ApiError::Sheet(message)
43 }
44 ClientError::NotFound => ApiError::Sheet("the character's sheet is gone".into()),
45 other => ApiError::SheetUnavailable(other.to_string()),
46 }
47 }
48
49 pub fn from_monsters(error: ClientError) -> Self {
51 match error {
52 ClientError::NotFound => ApiError::BadRequest("there's no such monster".into()),
53 other => ApiError::MonstersUnavailable(other.to_string()),
54 }
55 }
56}
57
58impl IntoResponse for ApiError {
59 fn into_response(self) -> Response {
60 let status = match &self {
61 ApiError::NotFound => StatusCode::NOT_FOUND,
62 ApiError::BadRequest(_) => StatusCode::BAD_REQUEST,
63 ApiError::Forbidden(_) => StatusCode::FORBIDDEN,
64 ApiError::Conflict(_) => StatusCode::CONFLICT,
65 ApiError::Sheet(_) | ApiError::Combat(_) => StatusCode::UNPROCESSABLE_ENTITY,
66 ApiError::SheetUnavailable(_) | ApiError::MonstersUnavailable(_) => {
67 StatusCode::BAD_GATEWAY
68 }
69 ApiError::Database(_) | ApiError::Corrupt(_) => StatusCode::INTERNAL_SERVER_ERROR,
70 };
71 edda_server::error_response(status, &self)
72 }
73}