use std::borrow::Cow; use anyhow::Error; use axum::Json; use axum::extract::rejection::JsonRejection; use axum::extract::{FromRequest, Request}; use axum::response::IntoResponse; use reqwest::StatusCode; use serde::Serialize; use serde::de::DeserializeOwned; use validator::Validate; use crate::api::error::InnerApiError; #[derive(Serialize)] pub struct ApiResponse { status_code: u16, #[serde(skip_serializing_if = "Option::is_none")] data: Option, #[serde(skip_serializing_if = "Option::is_none")] message: Option>, } impl ApiResponse { pub fn ok(data: T) -> Self { Self { status_code: 200, data: Some(data), message: None, } } pub fn bad_request(message: impl Into>) -> Self { Self { status_code: 400, data: None, message: Some(message.into()), } } pub fn unauthorized(message: impl Into>) -> Self { Self { status_code: 401, data: None, message: Some(message.into()), } } pub fn not_found(message: impl Into>) -> Self { Self { status_code: 404, data: None, message: Some(message.into()), } } pub fn internal_server_error(message: impl Into>) -> Self { Self { status_code: 500, data: None, message: Some(message.into()), } } } impl IntoResponse for ApiResponse { fn into_response(self) -> axum::response::Response { ( StatusCode::from_u16(self.status_code).expect("invalid Http Status Code"), Json(self), ) .into_response() } } pub struct ApiError(Error); impl From for ApiError where E: Into, { fn from(value: E) -> Self { Self(value.into()) } } impl IntoResponse for ApiError { fn into_response(self) -> axum::response::Response { if let Some(inner_error) = self.0.downcast_ref::() { match inner_error { InnerApiError::NotFound(_) => return ApiResponse::<()>::not_found(self.0.to_string()).into_response(), InnerApiError::BadRequest(_) => { return ApiResponse::<()>::bad_request(self.0.to_string()).into_response(); } } } ApiResponse::<()>::internal_server_error(self.0.to_string()).into_response() } } #[derive(Debug, Clone, Copy, Default)] pub struct ValidatedJson(pub T); impl FromRequest for ValidatedJson where T: DeserializeOwned + Validate, S: Send + Sync, Json: FromRequest, { type Rejection = ApiError; async fn from_request(req: Request, state: &S) -> Result { let Json(value) = Json::::from_request(req, state).await?; value .validate() .map_err(|e| ApiError::from(InnerApiError::BadRequest(e.to_string())))?; Ok(ValidatedJson(value)) } }