1use sfr_types as st;
4
5use crate::SlashCommandResponse;
6use axum::http::StatusCode;
7use axum::response::{IntoResponse, Response};
8use axum::BoxError;
9use axum::Json;
10use serde_json::json;
11
12#[derive(Debug)]
14pub enum ResponseError {
15 InvalidHeader(String),
17
18 ReadBody,
20
21 DeserializeBody(BoxError),
23
24 InvalidToken,
26
27 MethodNotAllowed,
33
34 InternalServerError(Box<dyn std::error::Error + 'static + Send + Sync>),
36
37 Unknown,
39
40 Custom(StatusCode, String),
42
43 CustomResponse(SlashCommandResponse),
45}
46
47impl ResponseError {
48 pub fn internal_server_error(inner: impl std::error::Error + 'static + Send + Sync) -> Self {
50 Self::InternalServerError(Box::new(inner))
51 }
52
53 pub fn custom_response<T>(resp: T) -> Self
55 where
56 T: Into<SlashCommandResponse>,
57 {
58 Self::CustomResponse(resp.into())
59 }
60
61 pub fn custom<T1, T2>(status: T1, message: T2) -> Result<Self, st::Error>
63 where
64 T1: TryInto<StatusCode>,
65 <T1 as TryInto<StatusCode>>::Error: std::error::Error + 'static + Send + Sync,
66 T2: Into<String>,
67 {
68 let status = status
69 .try_into()
70 .map_err(|e| st::ServerError::InvalidStatusCode(Box::new(e)))?;
71 Ok(Self::Custom(status, message.into()))
72 }
73
74 fn map(&self) -> (StatusCode, &str) {
76 match self {
77 Self::InvalidHeader(_) => (StatusCode::BAD_REQUEST, "Invalid header"),
78 Self::ReadBody => (StatusCode::BAD_REQUEST, "Failed to read body"),
79 Self::DeserializeBody(_) => (StatusCode::BAD_REQUEST, "Failed to deserialize"),
80 Self::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid body"),
81
82 Self::MethodNotAllowed => (StatusCode::METHOD_NOT_ALLOWED, "method not allowed"),
83
84 Self::InternalServerError(_) | Self::Unknown => {
85 (StatusCode::INTERNAL_SERVER_ERROR, "Unknown error")
86 }
87
88 Self::Custom(code, message) => (*code, message.as_str()),
89
90 Self::CustomResponse(_) => unreachable!(),
91 }
92 }
93}
94
95impl IntoResponse for ResponseError {
96 fn into_response(self) -> Response {
97 if let Self::CustomResponse(resp) = self {
98 return resp.into_response();
99 }
100
101 tracing::info!("response error: {self:?}");
102
103 let (status, error_message) = self.map();
104 let body = Json(json!({
105 "error": error_message,
106 }));
107 (status, body).into_response()
108 }
109}
110
111impl std::fmt::Display for ResponseError {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
113 let (status, error_message) = self.map();
114 write!(f, "{status}: {error_message}")
115 }
116}
117
118impl std::error::Error for ResponseError {}