1use axum::{
2 response::{IntoResponse, Response},
3 Json,
4};
5use serde::Serialize;
6
7#[derive(Debug, Clone, Serialize)]
34#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
35pub struct ListResponse<T: Serialize> {
36 pub data: Vec<T>,
38 pub total: i64,
40 pub limit: u32,
42 pub offset: u32,
44}
45
46impl<T: Serialize> IntoResponse for ListResponse<T> {
47 fn into_response(self) -> Response {
48 Json(self).into_response()
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use serde_json::json;
56
57 #[derive(Serialize)]
58 struct Item {
59 id: String,
60 }
61
62 #[test]
63 fn serializes_fields_correctly() {
64 let resp = ListResponse {
65 data: vec![Item { id: "abc".into() }],
66 total: 10,
67 limit: 25,
68 offset: 0,
69 };
70 let v = serde_json::to_value(&resp).unwrap();
71 assert_eq!(v["total"], 10);
72 assert_eq!(v["limit"], 25);
73 assert_eq!(v["offset"], 0);
74 assert_eq!(v["data"][0]["id"], "abc");
75 }
76
77 #[test]
78 fn empty_data_serializes() {
79 let resp: ListResponse<Item> = ListResponse {
80 data: vec![],
81 total: 0,
82 limit: 50,
83 offset: 0,
84 };
85 let v = serde_json::to_value(&resp).unwrap();
86 assert_eq!(v["data"], json!([]));
87 assert_eq!(v["total"], 0);
88 }
89
90 #[test]
91 fn offset_pagination_fields() {
92 let resp: ListResponse<Item> = ListResponse {
93 data: vec![],
94 total: 100,
95 limit: 10,
96 offset: 30,
97 };
98 let v = serde_json::to_value(&resp).unwrap();
99 assert_eq!(v["limit"], 10);
100 assert_eq!(v["offset"], 30);
101 }
102}