use async_trait::async_trait;
use bytes::Bytes;
use hyper::StatusCode;
use reinhardt_http::{Request, Response};
use std::fmt;
use std::future::Future;
use tracing::{error, warn};
use crate::DispatchError;
pub type ExceptionResult = Result<Response, DispatchError>;
#[async_trait]
pub trait ExceptionHandler: Send + Sync {
async fn handle_exception(&self, request: &Request, error: DispatchError) -> Response;
}
pub struct DefaultExceptionHandler;
#[async_trait]
impl ExceptionHandler for DefaultExceptionHandler {
async fn handle_exception(&self, _request: &Request, error: DispatchError) -> Response {
let (status, message) = match &error {
DispatchError::View(msg) => {
warn!("View error: {}", msg);
(StatusCode::INTERNAL_SERVER_ERROR, msg.clone())
}
DispatchError::UrlResolution(msg) => {
warn!("URL resolution error: {}", msg);
(StatusCode::NOT_FOUND, msg.clone())
}
DispatchError::Middleware(msg) => {
error!("Middleware error: {}", msg);
(StatusCode::INTERNAL_SERVER_ERROR, msg.clone())
}
DispatchError::Http(msg) => {
warn!("HTTP error: {}", msg);
(StatusCode::BAD_REQUEST, msg.clone())
}
DispatchError::Internal(msg) => {
error!("Internal error: {}", msg);
(StatusCode::INTERNAL_SERVER_ERROR, msg.clone())
}
};
let mut response = Response::new(status);
response.body = Bytes::from(message.into_bytes());
response
}
}
pub async fn convert_exception_to_response<F, Fut>(handler: F, request: Request) -> Response
where
F: FnOnce(Request) -> Fut,
Fut: Future<Output = Result<Response, DispatchError>>,
{
match handler(request).await {
Ok(response) => response,
Err(error) => {
let exception_handler = DefaultExceptionHandler;
let dummy_request = Request::builder()
.method(hyper::Method::GET)
.uri("/")
.version(hyper::Version::HTTP_11)
.headers(hyper::HeaderMap::new())
.body(Bytes::new())
.build()
.unwrap();
exception_handler
.handle_exception(&dummy_request, error)
.await
}
}
}
pub trait IntoResponse {
fn into_response(self) -> Response;
}
impl IntoResponse for Response {
fn into_response(self) -> Response {
self
}
}
impl IntoResponse for String {
fn into_response(self) -> Response {
let mut response = Response::new(StatusCode::OK);
response.body = Bytes::from(self.into_bytes());
response
}
}
impl IntoResponse for &str {
fn into_response(self) -> Response {
let mut response = Response::new(StatusCode::OK);
response.body = Bytes::from(self.as_bytes().to_vec());
response
}
}
impl IntoResponse for Vec<u8> {
fn into_response(self) -> Response {
let mut response = Response::new(StatusCode::OK);
response.body = Bytes::from(self);
response
}
}
impl IntoResponse for StatusCode {
fn into_response(self) -> Response {
Response::new(self)
}
}
impl<T: IntoResponse, E: fmt::Display> IntoResponse for Result<T, E> {
fn into_response(self) -> Response {
match self {
Ok(value) => value.into_response(),
Err(error) => {
error!("Error converting to response: {}", error);
let mut response = Response::new(StatusCode::INTERNAL_SERVER_ERROR);
response.body = Bytes::from(error.to_string().into_bytes());
response
}
}
}
}