1#[macro_export]
41macro_rules! generate_responses_functions {
42 (
43 $doc_family:expr,
44 $enum_name:ident,
45 $first_variant:ident => ($std_code_first:expr, $std_name_first:expr, $desc_first:expr, $int_code_first:expr, $int_std_name_first:expr)
46 $(, $variant:ident => ($std_code:expr, $std_name:expr, $desc:expr, $int_code:expr, $int_name:expr) )* $(,)?
47 ) => {
48 #[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
50 #[doc = $doc_family]
51 #[doc = concat!(
52 "\n\nEnum representing HTTP response status codes and descriptions for `",
53 stringify!($enum_name),
54 "`. This file defines the following methods:\n",
55 "- `to_http_code`: Converts the enum variant to its corresponding `HttpCode`.\n",
56 "- `get_code`: Returns the standard code as `u16`.\n",
57 "- `from_u16`: Constructs an enum variant from a given `u16` code (first matching standard, then internal).\n",
58 "- `from_internal_code`: Constructs an enum variant from a given internal `u16` code.\n",
59 "- `as_tuple`: Returns a unified tuple representation.\n",
60 "- `as_json`: Returns a JSON representation.\n\n",
61 "# Example\n```rust,no_run\n",
62 "use strum_macros::EnumIter;\n",
63 "use simbld_http::responses::CustomResponse;\n",
64 "use simbld_http::traits::get_code_trait::GetCode;\n",
65 "use simbld_http::responses::",
66 stringify!($enum_name),
67 ";\n\nlet example = ",
68 stringify!($enum_name),
69 "::",
70 stringify!($first_variant),
71 ";\nassert_eq!(example.get_code(), ",
72 stringify!($std_code_first),
73 ");\n// L'as_tuple retourne une structure UnifiedTuple avec les données du code de réponse\nlet tuple = example.as_tuple();\nassert_eq!(tuple.standard_code, ",
74 stringify!($std_code_first),
75 ");\nassert_eq!(tuple.standard_name, ",
76 stringify!($std_name_first),
77 ");\n```"
78 )]
79 pub enum $enum_name {
80 $first_variant,
81 $($variant,)*
82 }
83
84 impl serde::Serialize for $enum_name {
86 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87 where
88 S: serde::Serializer,
89 {
90 let unified = self.to_http_code().as_unified_tuple();
93 use serde::ser::SerializeStruct;
94 let mut state = serializer.serialize_struct(stringify!($enum_name), 2)?;
95 state.serialize_field("type", $doc_family)?;
97 state.serialize_field("details", &unified.as_json())?;
99 state.end()
100 }
101 }
102
103 impl $enum_name {
104 pub fn get_description(&self) -> &'static str {
106 match self {
107 Self::$first_variant => $desc_first,
108 $(
109 Self::$variant => $desc,
110 )*
111 }
112 }
113
114 pub fn get_code(&self) -> u16 {
116 match self {
117 Self::$first_variant => $std_code_first,
118 $(
119 Self::$variant => $std_code,
120 )*
121 }
122 }
123
124 pub fn get_name(&self) -> &'static str {
126 match self {
127 Self::$first_variant => $std_name_first,
128 $(
129 Self::$variant => $std_name,
130 )*
131 }
132 }
133
134 pub fn get_data(&self) -> &'static str {
136 ""
137 }
138
139 pub fn get_all_data(&self) -> (u16, &'static str, &'static str, &'static str) {
141 (
142 self.get_code(),
143 self.get_name(),
144 self.get_data(),
145 self.get_description()
146 )
147 }
148
149 pub fn into_response(&self) -> $crate::responses::CustomResponse {
151 let (code, name, data, desc) = self.get_all_data();
152 CustomResponse::new(code, name, data, desc)
153 }
154
155 pub fn to_http_code(&self) -> $crate::helpers::http_code_helper::HttpCode {
157 match self {
158 Self::$first_variant => {
159 let internal_code = if $int_code_first == $std_code_first {
160 None
161 } else {
162 Some($int_code_first)
163 };
164
165 let internal_name = if $int_std_name_first == $std_name_first {
166 None
167 } else {
168 Some($int_std_name_first)
169 };
170
171 $crate::helpers::http_code_helper::HttpCode {
172 standard_code: $std_code_first,
173 standard_name: $std_name_first,
174 unified_description: $desc_first,
175 internal_code,
176 internal_name,
177 }
178 },
179 $(
180 Self::$variant => {
181 let internal_code = if $int_code == $std_code {
182 None
183 } else {
184 Some($int_code)
185 };
186
187 let internal_name = if $int_name == $std_name {
188 None
189 } else {
190 Some($int_name)
191 };
192
193 $crate::helpers::http_code_helper::HttpCode {
194 standard_code: $std_code,
195 standard_name: $std_name,
196 unified_description: $desc,
197 internal_code,
198 internal_name,
199 }
200 },
201 )*
202 }
203 }
204
205 pub fn internal_code(&self) -> u16 {
207 match self {
208 Self::$first_variant => $int_code_first,
209 $(
210 Self::$variant => $int_code,
211 )*
212 }
213 }
214
215 pub fn from_u16(code: u16) -> Option<Self> {
218 if code == $std_code_first { return Some(Self::$first_variant); }
219 $(
220 if code == $std_code { return Some(Self::$variant); }
221 )*
222 if code == $int_code_first { return Some(Self::$first_variant); }
223 $(
224 if code == $int_code { return Some(Self::$variant); }
225 )*
226 None
227 }
228
229 pub fn from_internal_code(code: u16) -> Option<Self> {
232 match code {
233 $int_code_first => Some(Self::$first_variant),
234 $(
235 $int_code => Some(Self::$variant),
236 )*
237 _ => None,
238 }
239 }
240
241 pub fn as_tuple(&self) -> $crate::helpers::unified_tuple_helper::UnifiedTuple {
243 $crate::helpers::unified_tuple_helper::UnifiedTuple {
244 standard_code: self.get_code(),
245 standard_name: self.get_name(),
246 unified_description: self.get_description(),
247 internal_code: Some(match self {
248 Self::$first_variant => $int_code_first,
249 $(Self::$variant => $int_code,)*
250 }),
251 internal_name: Some(match self {
252 Self::$first_variant => $int_std_name_first,
253 $(Self::$variant => $int_name,)*
254 }),
255 }
256 }
257
258
259 pub fn as_json(&self) -> serde_json::Value {
261 serde_json::to_value(self).unwrap()
262 }
263 }
264
265 impl $crate::traits::tuple_traits::IntoTwoFieldsTuple for $enum_name {
267 fn into_two_fields_tuple(self) -> $crate::helpers::two_fields_tuple_helper::TwoFieldsTuple {
268 let http_code = self.to_http_code();
269 http_code.into_two_fields_tuple()
270 }
271 }
272
273 impl $crate::traits::tuple_traits::IntoThreeFieldsTuple for $enum_name {
275 fn into_three_fields_tuple(self) -> $crate::helpers::three_fields_tuple_helper::ThreeFieldsTuple {
276 let http_code = self.to_http_code();
277 http_code.into_three_fields_tuple()
278 }
279 }
280
281 impl GetCode for $enum_name {
283 fn get_code(&self) -> u16 {
284 match self {
285 Self::$first_variant => $std_code_first,
286 $(
287 Self::$variant => $std_code,
288 )*
289 }
290 }
291 }
292
293
294 impl From<$enum_name> for (u16, &'static str) {
296 fn from(value: $enum_name) -> Self {
297 (value.get_code(), value.get_description())
298 }
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use crate::helpers::unified_tuple_helper::UnifiedTuple;
306 use crate::responses::ResponsesClientCodes;
307 use crate::traits::tuple_traits::{IntoThreeFieldsTuple, IntoTwoFieldsTuple};
308 use crate::ResponsesSuccessCodes;
309 use serde_json::json;
310
311 #[test]
312 fn test_get_description() {
313 let ex = ResponsesClientCodes::BadRequest;
314 assert_eq!(ex.get_description(), "The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)." );
315 }
316 #[test]
317 fn test_get_code() {
318 let ex = ResponsesClientCodes::BadRequest;
319 assert_eq!(ex.get_code(), 400);
320 }
321
322 #[test]
323 fn test_get_name() {
324 let ex = ResponsesClientCodes::BadRequest;
325 assert_eq!(ex.get_name(), "Bad Request");
326 }
327
328 #[test]
329 fn test_to_http_code() {
330 let ex = ResponsesClientCodes::PageExpired;
331 let expected = crate::helpers::http_code_helper::HttpCode::new(
332 401,
333 "Unauthorized",
334 "Although the HTTP standard specifies 'unauthorized', semantically this response means 'unauthenticated'. That is, the client must authenticate itself to get the requested response.",
335 419,
336 "PageExpired");
337
338 assert_eq!(ex.to_http_code(), expected);
339 }
340
341 #[test]
342 fn test_from_internal_code() {
343 assert_eq!(
344 ResponsesClientCodes::from_internal_code(444),
345 Some(ResponsesClientCodes::NoResponse)
346 );
347 assert_eq!(
348 ResponsesClientCodes::from_internal_code(445),
349 Some(ResponsesClientCodes::TooManyForwardedIPAddresses)
350 );
351 assert_eq!(ResponsesClientCodes::from_internal_code(492), None);
352 }
353
354 #[test]
355 fn test_as_tuple() {
356 let ex = ResponsesClientCodes::BadRequest;
357 let expected = UnifiedTuple {
358 standard_code: 400,
359 standard_name: "Bad Request",
360 unified_description:
361 "The server cannot or will not process the request due to something \
362 that is perceived to be a client error (e.g., malformed request \
363 syntax, invalid request message framing, or deceptive request \
364 routing).",
365 internal_code: Some(400),
366 internal_name: Some("Bad Request"),
367 };
368
369 assert_eq!(ex.as_tuple(), expected);
370 }
371
372 #[test]
373 fn test_as_json() {
374 let ex = ResponsesClientCodes::BadRequest;
376 let ex_json = ex.as_json();
377
378 let expected_json = json!({
380 "type": "Client errors",
381 "details": {
382 "standard http code": {
383 "code": 400,
384 "name": "Bad Request"
385 },
386 "description": "The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).",
387 "internal http code": {
388 "code": null,
389 "name": null
390 }
391 }
392 });
393
394 assert_eq!(ex_json, expected_json);
396 }
397
398 #[test]
399 fn test_from_u16() {
400 assert_eq!(ResponsesClientCodes::from_u16(400), Some(ResponsesClientCodes::BadRequest));
401 assert_eq!(
402 ResponsesClientCodes::from_u16(456),
403 Some(ResponsesClientCodes::UnrecoverableError)
404 );
405 assert_eq!(ResponsesClientCodes::from_u16(500), None);
406 }
407
408 #[test]
409 fn test_internal_code() {
410 let ex = ResponsesClientCodes::NoResponse;
411 assert_eq!(ex.internal_code(), 444);
412 }
413
414 #[test]
415 fn test_into_two_fields_tuple() {
416 let response = ResponsesClientCodes::BadRequest;
417 let tuple = response.into_two_fields_tuple();
418 let json_result = serde_json::to_value(&tuple).unwrap();
419
420 let expected = json!({
421 "code": 400,
422 "name": "Bad Request"
423 });
424 assert_eq!(json_result, expected);
425 }
426
427 #[test]
428 fn test_into_three_fields_tuple() {
429 let response = ResponsesSuccessCodes::Ok;
430 let tuple = response.into_three_fields_tuple();
431 let json_result = serde_json::to_value(&tuple).unwrap();
432
433 let expected = json!({
434 "code": 200,
435 "name": "OK",
436 "description": "Request processed successfully. Response will depend on the request method used, and the result will be either a representation of the requested resource or an empty response"
437 });
438 assert_eq!(json_result, expected);
439 }
440}