use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::Serialize;
use serde_json::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorType {
InvalidRequestError,
AuthenticationError,
PermissionError,
RateLimitError,
HarnessError,
ServerError,
}
impl ErrorType {
const fn status(self) -> StatusCode {
match self {
Self::InvalidRequestError => StatusCode::BAD_REQUEST,
Self::AuthenticationError => StatusCode::UNAUTHORIZED,
Self::PermissionError => StatusCode::FORBIDDEN,
Self::RateLimitError => StatusCode::TOO_MANY_REQUESTS,
Self::HarnessError | Self::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct UhpError {
#[serde(rename = "type")]
pub kind: ErrorType,
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ErrorEnvelope {
pub error: UhpError,
}
#[derive(Debug, Clone)]
pub struct UhpFailure {
status: StatusCode,
error: UhpError,
}
impl UhpFailure {
pub fn new(kind: ErrorType, code: &str, message: impl Into<String>) -> Self {
Self {
status: kind.status(),
error: UhpError {
kind,
code: code.to_owned(),
message: message.into(),
detail: None,
},
}
}
#[must_use]
pub const fn with_status(mut self, status: StatusCode) -> Self {
self.status = status;
self
}
#[must_use]
pub fn with_detail(mut self, detail: Value) -> Self {
self.error.detail = Some(detail);
self
}
pub fn unauthorized(code: &str, message: impl Into<String>) -> Self {
Self::new(ErrorType::AuthenticationError, code, message)
}
pub fn not_found(code: &str, message: impl Into<String>) -> Self {
Self::new(ErrorType::InvalidRequestError, code, message).with_status(StatusCode::NOT_FOUND)
}
}
impl UhpFailure {
#[must_use]
pub fn into_error(self) -> UhpError {
self.error
}
}
impl IntoResponse for UhpFailure {
fn into_response(self) -> Response {
(self.status, Json(ErrorEnvelope { error: self.error })).into_response()
}
}