Skip to content
Clone
440bd07
95 lines 2.7 KB Rust
use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::Serialize;
use thiserror::Error;
pub type SpaceResult<T> = Result<T, SpaceError>;
#[derive(Debug, Error)]
pub enum SpaceError {
#[error("bad request: {0}")]
BadRequest(String),
#[error("unauthorized: {0}")]
Unauthorized(String),
#[error("forbidden: {0}")]
Forbidden(String),
#[error("not found: {0}")]
NotFound(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("rate limited: {0}")]
RateLimited(String),
#[error("internal error: {0}")]
Internal(String),
#[error("database error: {0}")]
Sqlx(#[from] db::conn::SqlxError),
#[error("uuid: {0}")]
Uuid(#[from] uuid::Error),
}
impl From<axum::extract::rejection::JsonRejection> for SpaceError {
fn from(re: axum::extract::rejection::JsonRejection) -> Self {
SpaceError::BadRequest(re.body_text())
}
}
impl From<axum::extract::rejection::PathRejection> for SpaceError {
fn from(re: axum::extract::rejection::PathRejection) -> Self {
SpaceError::BadRequest(re.body_text())
}
}
impl From<axum::extract::rejection::QueryRejection> for SpaceError {
fn from(re: axum::extract::rejection::QueryRejection) -> Self {
SpaceError::BadRequest(re.body_text())
}
}
impl SpaceError {
fn status(&self) -> StatusCode {
match self {
SpaceError::BadRequest(_) => StatusCode::BAD_REQUEST,
SpaceError::Forbidden(_) => StatusCode::FORBIDDEN,
SpaceError::Conflict(_) => StatusCode::CONFLICT,
SpaceError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
SpaceError::NotFound(_) => StatusCode::NOT_FOUND,
SpaceError::RateLimited(_) => StatusCode::TOO_MANY_REQUESTS,
SpaceError::Internal(_) | SpaceError::Sqlx(_) | SpaceError::Uuid(_) => {
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
fn is_user_facing(&self) -> bool {
matches!(
self,
SpaceError::BadRequest(_)
| SpaceError::Unauthorized(_)
| SpaceError::Forbidden(_)
| SpaceError::NotFound(_)
| SpaceError::Conflict(_)
| SpaceError::RateLimited(_)
)
}
}
#[derive(Serialize)]
struct ErrorBody<'a> {
error: &'a str,
}
impl IntoResponse for SpaceError {
fn into_response(self) -> Response {
let status = self.status();
let msg = if self.is_user_facing() {
self.to_string()
} else {
tracing::error!(error = %self, "space internal error");
"internal error".to_string()
};
let body = ErrorBody { error: &msg };
(status, Json(body)).into_response()
}
}