use axum::Json;
use serde::Serialize;
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Clone)]
pub struct ApiMeta {
pub page: Option<u32>,
pub per_page: Option<u32>,
pub total: Option<u64>,
}
impl ApiMeta {
pub fn empty() -> Self {
ApiMeta {
page: None,
per_page: None,
total: None,
}
}
pub fn paginated(page: u32, per_page: u32, total: u64) -> Self {
ApiMeta {
page: Some(page),
per_page: Some(per_page),
total: Some(total),
}
}
}
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize)]
pub struct ApiResponse<T: Serialize> {
pub data: T,
pub meta: Option<ApiMeta>,
}
pub fn ok<T: Serialize>(data: T) -> Json<ApiResponse<T>> {
Json(ApiResponse { data, meta: None })
}
pub fn ok_paged<T: Serialize>(
data: T,
page: u32,
per_page: u32,
total: u64,
) -> Json<ApiResponse<T>> {
Json(ApiResponse {
data,
meta: Some(ApiMeta::paginated(page, per_page, total)),
})
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn api_meta_empty() {
let meta = ApiMeta::empty();
assert_eq!(meta.page, None);
assert_eq!(meta.per_page, None);
assert_eq!(meta.total, None);
}
#[test]
fn api_meta_paginated() {
let meta = ApiMeta::paginated(2, 50, 200);
assert_eq!(meta.page, Some(2));
assert_eq!(meta.per_page, Some(50));
assert_eq!(meta.total, Some(200));
}
#[test]
fn api_response_serialize() {
let response = ApiResponse {
data: vec![1, 2, 3],
meta: None,
};
let json_val = serde_json::to_value(&response).expect("serialize");
assert_eq!(json_val["data"], json!([1, 2, 3]));
assert_eq!(json_val["meta"], serde_json::json!(null));
}
#[test]
fn api_response_with_meta_serialize() {
let response = ApiResponse {
data: vec!["a"],
meta: Some(ApiMeta::paginated(1, 10, 50)),
};
let json_val = serde_json::to_value(&response).expect("serialize");
assert_eq!(json_val["data"], json!(["a"]));
assert_eq!(json_val["meta"]["page"], 1);
assert_eq!(json_val["meta"]["per_page"], 10);
assert_eq!(json_val["meta"]["total"], 50);
}
}