1use axum::{Json, http::StatusCode, response::IntoResponse};
2use serde::Serialize;
3
4#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
6pub struct ApiResponse<T>
7where
8 T: Serialize,
9{
10 pub success: bool,
11 pub err_code: String,
12 pub err_message: String,
13 pub data: Option<T>,
14}
15
16impl<T> ApiResponse<T>
17where
18 T: Serialize,
19{
20 pub fn success(data: T) -> Self {
22 Self {
23 success: true,
24 err_code: "0".to_string(),
25 err_message: String::new(),
26 data: Some(data),
27 }
28 }
29
30 pub fn success_no_data() -> Self {
32 Self {
33 success: true,
34 err_code: "0".to_string(),
35 err_message: String::new(),
36 data: None,
37 }
38 }
39
40 pub fn fail(code: impl Into<String>, message: impl Into<String>) -> Self {
42 Self {
43 success: false,
44 err_code: code.into(),
45 err_message: message.into(),
46 data: None,
47 }
48 }
49}
50
51impl<T> IntoResponse for ApiResponse<T>
52where
53 T: Serialize,
54{
55 fn into_response(self) -> axum::response::Response {
56 (StatusCode::OK, Json(self)).into_response()
57 }
58}
59
60#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
62pub struct PageResponse<T>
63where
64 T: Serialize,
65{
66 pub success: bool,
67 pub err_code: String,
68 pub err_message: String,
69 pub data: Vec<T>,
70 pub total: i64,
71 pub page_size: i64,
72 pub has_more: bool,
73 pub current_page: i64,
74 pub total_pages: i64,
75}
76
77impl<T> PageResponse<T>
78where
79 T: Serialize,
80{
81 pub fn success(data: Vec<T>, total: i64, page_size: i64, current_page: i64) -> Self {
83 Self {
84 success: true,
85 err_code: "0".to_string(),
86 err_message: String::new(),
87 data,
88 total,
89 page_size,
90 has_more: total > page_size * current_page,
91 current_page,
92 total_pages: if page_size <= 0 {
93 0
94 } else {
95 (total + page_size - 1) / page_size
96 },
97 }
98 }
99
100 pub fn empty() -> Self {
102 Self {
103 success: true,
104 err_code: "0".to_string(),
105 err_message: String::new(),
106 data: Vec::new(),
107 total: 0,
108 page_size: 0,
109 has_more: false,
110 current_page: 0,
111 total_pages: 0,
112 }
113 }
114}
115
116impl<T> IntoResponse for PageResponse<T>
117where
118 T: Serialize,
119{
120 fn into_response(self) -> axum::response::Response {
121 (StatusCode::OK, Json(self)).into_response()
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::{ApiResponse, PageResponse};
128
129 #[test]
130 fn response_success_uses_zero_error_code() {
131 let response = ApiResponse::success("ok");
132
133 assert!(response.success);
134 assert_eq!(response.err_code, "0");
135 assert_eq!(response.data, Some("ok"));
136 }
137
138 #[test]
139 fn page_response_calculates_total_pages() {
140 let response = PageResponse::success(vec![1, 2], 11, 5, 2);
141
142 assert!(response.has_more);
143 assert_eq!(response.total_pages, 3);
144 }
145}