use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use lora_database::{LoraError, LoraErrorCode};
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
pub error: ErrorBody,
}
#[derive(Debug, Serialize)]
pub struct ErrorBody {
pub code: &'static str,
pub message: String,
pub category: &'static str,
}
impl ErrorResponse {
fn from_lora(err: &LoraError) -> Self {
Self {
error: ErrorBody {
code: err.code().as_str(),
message: err.message().to_string(),
category: err.category().as_str(),
},
}
}
pub(crate) fn from_parts(code: LoraErrorCode, message: impl Into<String>) -> Self {
Self {
error: ErrorBody {
code: code.as_str(),
message: message.into(),
category: code.category().as_str(),
},
}
}
}
fn status_for(err: &LoraError) -> StatusCode {
match err.code() {
LoraErrorCode::WalPoisoned => StatusCode::SERVICE_UNAVAILABLE,
LoraErrorCode::Io
| LoraErrorCode::WalCorruption
| LoraErrorCode::SnapshotCodec
| LoraErrorCode::SnapshotCrypto
| LoraErrorCode::Internal => StatusCode::INTERNAL_SERVER_ERROR,
LoraErrorCode::Timeout => StatusCode::REQUEST_TIMEOUT,
LoraErrorCode::NotFound => StatusCode::NOT_FOUND,
LoraErrorCode::InvalidParams | LoraErrorCode::InvalidVector => {
StatusCode::UNPROCESSABLE_ENTITY
}
LoraErrorCode::ConstraintViolation => StatusCode::CONFLICT,
LoraErrorCode::Parse
| LoraErrorCode::Semantic
| LoraErrorCode::ReadOnlyViolation
| LoraErrorCode::DatabaseName
| LoraErrorCode::Config => StatusCode::BAD_REQUEST,
}
}
pub(crate) fn lora_error_response(err: impl Into<LoraError>) -> Response {
let lora = err.into();
let status = status_for(&lora);
(status, Json(ErrorResponse::from_lora(&lora))).into_response()
}