use std::convert::Infallible; use axum::{response::IntoResponse, body::{Full, Bytes}, Json}; use hyper::StatusCode; use serde::{Serializer, Serialize}; use thiserror::Error; #[derive(Error, Debug)] pub enum ApplicationError { #[error("File read error: {0}")] Read(#[from] std::io::Error), #[error("Deserialize error: {0}")] Deserialize(#[from] toml::de::Error), #[error("Database connection error: {0}")] Database(#[from] sibyl::Error), #[error("Axum error: {0}")] Axum(#[from] hyper::Error), } #[derive(Error, Debug)] pub enum ServiceError { #[error("SQL error: {0}")] Database(#[from] sibyl::Error), #[error("Response code: {0}")] ErrorResponse(StatusCode, Option), } fn serialize_status(x: &StatusCode, s: S) -> Result where S: Serializer, { s.serialize_u16(x.as_u16()) } #[derive(Serialize)] pub struct ErrorResponse { #[serde(serialize_with = "serialize_status")] pub status: StatusCode, pub error: String, } impl IntoResponse for ServiceError { type Body = Full; type BodyError = Infallible; fn into_response(self) -> axum::http::Response { let res = match self { ServiceError::Database(err) => ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, Some(err.to_string())), ServiceError::ErrorResponse(code, reason) => ErrorResponse::new(code, reason), }; let status = res.status; (status, Json(res)).into_response() } } impl ErrorResponse { fn new(status: StatusCode, message: Option) -> Self { let reason = status.canonical_reason().unwrap_or_default(); Self { status, error: message.unwrap_or_else(|| reason.to_string()), } } }