use sfr_types as st;
use crate::SlashCommandResponse;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::BoxError;
use axum::Json;
use serde_json::json;
#[derive(Debug)]
pub enum ResponseError {
InvalidHeader(String),
ReadBody,
DeserializeBody(BoxError),
InvalidToken,
MethodNotAllowed,
InternalServerError(Box<dyn std::error::Error + 'static + Send + Sync>),
Unknown,
Custom(StatusCode, String),
CustomResponse(SlashCommandResponse),
}
impl ResponseError {
pub fn internal_server_error(inner: impl std::error::Error + 'static + Send + Sync) -> Self {
Self::InternalServerError(Box::new(inner))
}
pub fn custom_response<T>(resp: T) -> Self
where
T: Into<SlashCommandResponse>,
{
Self::CustomResponse(resp.into())
}
pub fn custom<T1, T2>(status: T1, message: T2) -> Result<Self, st::Error>
where
T1: TryInto<StatusCode>,
<T1 as TryInto<StatusCode>>::Error: std::error::Error + 'static + Send + Sync,
T2: Into<String>,
{
let status = status
.try_into()
.map_err(|e| st::ServerError::InvalidStatusCode(Box::new(e)))?;
Ok(Self::Custom(status, message.into()))
}
fn map(&self) -> (StatusCode, &str) {
match self {
Self::InvalidHeader(_) => (StatusCode::BAD_REQUEST, "Invalid header"),
Self::ReadBody => (StatusCode::BAD_REQUEST, "Failed to read body"),
Self::DeserializeBody(_) => (StatusCode::BAD_REQUEST, "Failed to deserialize"),
Self::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid body"),
Self::MethodNotAllowed => (StatusCode::METHOD_NOT_ALLOWED, "method not allowed"),
Self::InternalServerError(_) | Self::Unknown => {
(StatusCode::INTERNAL_SERVER_ERROR, "Unknown error")
}
Self::Custom(code, message) => (*code, message.as_str()),
Self::CustomResponse(_) => unreachable!(),
}
}
}
impl IntoResponse for ResponseError {
fn into_response(self) -> Response {
if let Self::CustomResponse(resp) = self {
return resp.into_response();
}
tracing::info!("response error: {self:?}");
let (status, error_message) = self.map();
let body = Json(json!({
"error": error_message,
}));
(status, body).into_response()
}
}
impl std::fmt::Display for ResponseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let (status, error_message) = self.map();
write!(f, "{status}: {error_message}")
}
}
impl std::error::Error for ResponseError {}