use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde::Serialize;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize)]
pub struct PaginationMeta {
pub total: i64,
pub page: i64,
pub per_page: i64,
pub last_page: i64,
}
impl PaginationMeta {
pub fn new(total: i64, page: i64, per_page: i64) -> Self {
let last_page = if per_page > 0 {
(total + per_page - 1) / per_page
} else {
1
};
Self {
total,
page,
per_page,
last_page,
}
}
}
pub struct ApiResponse {
status: StatusCode,
body: serde_json::Value,
}
impl ApiResponse {
pub fn ok<T: Serialize>(data: T) -> Self {
Self {
status: StatusCode::OK,
body: serde_json::json!({ "data": data }),
}
}
pub fn created<T: Serialize>(data: T) -> Self {
Self {
status: StatusCode::CREATED,
body: serde_json::json!({
"data": data,
"meta": { "message": "Resource created" }
}),
}
}
pub fn no_content() -> Self {
Self {
status: StatusCode::NO_CONTENT,
body: serde_json::Value::Null,
}
}
pub fn paginated<T: Serialize>(data: Vec<T>, pagination: PaginationMeta) -> Self {
Self {
status: StatusCode::OK,
body: serde_json::json!({
"data": data,
"meta": pagination,
}),
}
}
pub fn error(code: &'static str, message: impl Into<String>, status: u16) -> Self {
let status_code = StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
Self {
status: status_code,
body: serde_json::json!({
"error": {
"code": code,
"message": message.into(),
"statusCode": status,
}
}),
}
}
pub fn validation(message: impl Into<String>, errors: HashMap<String, Vec<String>>) -> Self {
Self {
status: StatusCode::UNPROCESSABLE_ENTITY,
body: serde_json::json!({
"error": {
"code": "E_VALIDATION_FAILURE",
"message": message.into(),
"statusCode": 422,
"details": errors,
}
}),
}
}
pub fn from_status(status: StatusCode) -> Self {
Self {
status,
body: serde_json::Value::Null,
}
}
pub fn status_code(&self) -> StatusCode {
self.status
}
pub fn into_body(self) -> serde_json::Value {
self.body
}
}
impl IntoResponse for ApiResponse {
fn into_response(self) -> Response {
if self.body.is_null() {
self.status.into_response()
} else {
(self.status, Json(self.body)).into_response()
}
}
}