use std::fmt;
use axum::{
Json,
http::StatusCode,
response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ApiError {
pub error: String,
pub code: String,
pub details: Option<String>,
}
impl ApiError {
pub fn new(error: impl Into<String>, code: impl Into<String>) -> Self {
Self {
error: error.into(),
code: code.into(),
details: None,
}
}
pub fn with_details(mut self, details: impl Into<String>) -> Self {
self.details = Some(details.into());
self
}
pub fn parse_error(msg: impl fmt::Display) -> Self {
Self::new(format!("Parse error: {}", msg), "PARSE_ERROR")
}
pub fn validation_error(msg: impl fmt::Display) -> Self {
Self::new(format!("Validation error: {}", msg), "VALIDATION_ERROR")
}
pub fn internal_error(msg: impl fmt::Display) -> Self {
Self::new(format!("Internal server error: {}", msg), "INTERNAL_ERROR")
}
pub fn unauthorized() -> Self {
Self::new("Unauthorized", "UNAUTHORIZED")
}
pub fn not_found(msg: impl fmt::Display) -> Self {
Self::new(format!("Not found: {}", msg), "NOT_FOUND")
}
}
impl fmt::Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code, self.error)
}
}
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let status = match self.code.as_str() {
"UNAUTHORIZED" => StatusCode::UNAUTHORIZED,
"FORBIDDEN" => StatusCode::FORBIDDEN,
"NOT_FOUND" => StatusCode::NOT_FOUND,
"VALIDATION_ERROR" | "PARSE_ERROR" => StatusCode::BAD_REQUEST,
"SERVICE_UNAVAILABLE" => StatusCode::SERVICE_UNAVAILABLE,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
(status, Json(self)).into_response()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiResponse<T> {
pub status: String,
pub data: T,
}
impl<T: Serialize> ApiResponse<T> {
pub fn success(data: T) -> Json<Self> {
Json(Self {
status: "success".to_string(),
data,
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SanitizedConfig {
pub port: u16,
pub host: String,
pub workers: Option<usize>,
pub tls_enabled: bool,
pub sanitized: bool,
}
impl SanitizedConfig {
pub fn from_config(config: &crate::config::HttpServerConfig) -> Self {
Self {
port: config.port,
host: config.host.clone(),
workers: config.workers,
tls_enabled: config.tls.is_some(),
sanitized: true,
}
}
pub const fn is_sanitized(&self) -> bool {
self.sanitized
}
}