next_web_common/response/api_response.rs
1use axum_core::response::{IntoResponse, Response};
2use serde::Serialize;
3
4/// A generic structure representing a standardized API response.
5///
6/// This struct is designed to encapsulate the result of an API call in a consistent format.
7/// It includes fields for the HTTP status code, a human-readable message, and optional data.
8/// The use of generics allows this structure to be flexible and work with any type that implements
9/// the `Serialize` trait, making it suitable for JSON serialization (e.g., using `serde`).
10///
11/// # Type Parameters
12/// - `T`: The type of the data field. It must implement the `Serialize` trait to ensure
13/// compatibility with serialization libraries like `serde`.
14///
15/// # Fields
16/// - `status: u16`
17/// Represents the HTTP status code of the response (e.g., 200 for success, 404 for not found).
18/// This provides a machine-readable indicator of the response outcome.
19///
20/// - `message: String`
21/// A human-readable description of the response. This can be used to provide additional context
22/// or details about the result (e.g., "Resource created successfully" or "Invalid input").
23///
24/// - `data: Option<T>`
25/// Contains the payload of the response, if any. The use of `Option` allows this field to be
26/// omitted when there is no data to return (e.g., in case of an error). The type `T` is generic,
27/// enabling flexibility for different kinds of data (e.g., user information, list of items, etc.).
28///
29/// # Example
30/// ```rust
31/// use serde::Serialize;
32///
33/// #[derive(Serialize)]
34/// struct User {
35/// id: u32,
36/// name: String,
37/// }
38///
39/// let response = ApiResponse {
40/// status: 0,
41/// message: "User retrieved successfully".to_string(),
42/// data: Some(User {
43/// id: 1,
44/// name: "Alice".to_string(),
45/// }),
46/// };
47///
48/// // Serialize the response to JSON
49/// let json_response = serde_json::to_string(&response).unwrap();
50/// println!("{}", json_response);
51/// ```
52#[derive(Debug, Serialize)]
53pub struct ApiResponse<T: Serialize> {
54 status: u16,
55 message: String,
56 data: Option<T>,
57}
58
59impl<T: Serialize> ApiResponse<T> {
60 pub fn new(status: u16, message: String, data: T) -> Self {
61 ApiResponse {
62 status,
63 message,
64 data: Some(data),
65 }
66 }
67
68 pub fn get_status(&self) -> u16 {
69 self.status
70 }
71
72 pub fn get_message(&self) -> &str {
73 &self.message
74 }
75
76 pub fn get_data(&self) -> Option<&T> {
77 self.data.as_ref()
78 }
79
80 pub fn set_status(mut self, status_code: u16) -> ApiResponse<T> {
81 self.status = status_code;
82 return self;
83 }
84
85 pub fn set_message(mut self, message: impl Into<String>) -> ApiResponse<T> {
86 self.message = message.into();
87 return self;
88 }
89
90 pub fn set_data(mut self, data: T) -> ApiResponse<T> {
91 self.data = Some(data);
92 return self;
93 }
94}
95
96impl<T: Serialize> ApiResponse<T> {
97 pub fn ok(data: T) -> ApiResponse<T> {
98 return ApiResponse {
99 status: 0,
100 message: "ok".into(),
101 data: Some(data),
102 };
103 }
104
105 pub fn fail(data: T) -> ApiResponse<T> {
106 return ApiResponse {
107 status: 500,
108 message: "fail".into(),
109 data: Some(data),
110 };
111 }
112
113 pub fn empty() -> ApiResponse<T> {
114 return ApiResponse {
115 status: 204,
116 message: "".into(),
117 data: None,
118 };
119 }
120}
121
122impl<T> IntoResponse for ApiResponse<T>
123where
124 T: Serialize,
125{
126 fn into_response(self) -> Response {
127 Response::builder()
128 .header("Content-Type", "application/json")
129 .status(200)
130 .body(serde_json::json!(self).to_string().into())
131 .unwrap()
132 }
133}