Skip to content
Clone
b5f0be2
200 lines 5.0 KB Rust
use axum::extract::{FromRequest, Request};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::{Deref, DerefMut};
use utoipa::ToSchema;
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct NoData;
#[derive(Serialize, Deserialize, Debug, ToSchema)]
pub struct AppResponseData<T> {
pub code: i32,
pub message: String,
pub data: Option<T>,
pub error: Option<String>,
pub duration: String,
pub request_id: String,
pub at: String,
pub log_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct AppRequestData<T> {
pub request_id: String,
pub at: String,
pub data: Option<T>,
}
impl<T> AppResponseData<T>
where
T: Serialize,
{
pub fn ok(data: T) -> Self {
AppResponseData {
code: 200,
message: "success".to_string(),
data: Some(data),
error: None,
duration: String::new(),
request_id: String::new(),
at: String::new(),
log_id: String::new(),
}
}
pub fn err(code: i32, message: impl Into<String>) -> Self {
AppResponseData {
code,
message: message.into(),
data: None,
error: None,
duration: String::new(),
request_id: String::new(),
at: String::new(),
log_id: String::new(),
}
}
pub fn err_with_detail(
code: i32,
message: impl Into<String>,
detail: impl Into<String>,
) -> Self {
AppResponseData {
code,
message: message.into(),
data: None,
error: Some(detail.into()),
duration: String::new(),
request_id: String::new(),
at: String::new(),
log_id: String::new(),
}
}
pub fn with_code(mut self, code: i32) -> Self {
self.code = code;
self
}
pub fn with_message(mut self, message: impl Into<String>) -> Self {
self.message = message.into();
self
}
pub fn with_error(mut self, error: impl Into<String>) -> Self {
self.error = Some(error.into());
self
}
pub fn with_duration(mut self, duration: impl Into<String>) -> Self {
self.duration = duration.into();
self
}
pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
self.request_id = request_id.into();
self
}
pub fn with_at(mut self, at: impl Into<String>) -> Self {
self.at = at.into();
self
}
pub fn with_log_id(mut self, log_id: impl Into<String>) -> Self {
self.log_id = log_id.into();
self
}
}
impl AppResponseData<NoData> {
pub fn empty() -> Self {
AppResponseData {
code: 200,
message: "success".to_string(),
data: None,
error: None,
duration: String::new(),
request_id: String::new(),
at: String::new(),
log_id: String::new(),
}
}
pub fn err_plain(code: i32, message: impl Into<String>) -> Self {
let msg = message.into();
AppResponseData {
code,
message: msg.clone(),
data: None,
error: Some(msg),
duration: String::new(),
request_id: String::new(),
at: String::new(),
log_id: String::new(),
}
}
}
impl fmt::Display for AppResponseData<()> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "API error: code={}, message={}", self.code, self.message)
}
}
impl<T: Serialize> IntoResponse for AppResponseData<T> {
fn into_response(self) -> Response {
let status = StatusCode::from_u16(self.code as u16)
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
(status, Json(self)).into_response()
}
}
#[derive(Debug)]
pub struct AppRequest<T>(pub T);
impl<T> AppRequest<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for AppRequest<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for AppRequest<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T, S> FromRequest<S> for AppRequest<T>
where
T: DeserializeOwned + 'static,
S: Send + Sync,
{
type Rejection = AppResponseData<()>;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let Json(payload) = Json::<AppRequestData<T>>::from_request(req, state)
.await
.map_err(|e| {
AppResponseData::<()>::err(400, format!("invalid request body: {}", e))
})?;
match payload.data {
Some(data) => Ok(AppRequest(data)),
None => Err(AppResponseData::<()>::err(400, "missing request data")),
}
}
}