spikard 0.3.6 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -6
  3. data/ext/spikard_rb/Cargo.toml +2 -2
  4. data/lib/spikard/app.rb +33 -14
  5. data/lib/spikard/testing.rb +47 -12
  6. data/lib/spikard/version.rb +1 -1
  7. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
  8. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -0
  9. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -0
  10. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
  11. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
  12. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -0
  13. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -0
  14. data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -0
  15. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -0
  16. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -0
  17. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -0
  18. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
  19. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -0
  20. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -0
  21. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -0
  22. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -0
  23. data/vendor/crates/spikard-core/Cargo.toml +4 -4
  24. data/vendor/crates/spikard-core/src/debug.rs +64 -0
  25. data/vendor/crates/spikard-core/src/di/container.rs +3 -27
  26. data/vendor/crates/spikard-core/src/di/factory.rs +1 -5
  27. data/vendor/crates/spikard-core/src/di/graph.rs +8 -47
  28. data/vendor/crates/spikard-core/src/di/mod.rs +1 -1
  29. data/vendor/crates/spikard-core/src/di/resolved.rs +1 -7
  30. data/vendor/crates/spikard-core/src/di/value.rs +2 -4
  31. data/vendor/crates/spikard-core/src/errors.rs +30 -0
  32. data/vendor/crates/spikard-core/src/http.rs +262 -0
  33. data/vendor/crates/spikard-core/src/lib.rs +1 -1
  34. data/vendor/crates/spikard-core/src/lifecycle.rs +764 -0
  35. data/vendor/crates/spikard-core/src/metadata.rs +389 -0
  36. data/vendor/crates/spikard-core/src/parameters.rs +1962 -159
  37. data/vendor/crates/spikard-core/src/problem.rs +34 -0
  38. data/vendor/crates/spikard-core/src/request_data.rs +966 -1
  39. data/vendor/crates/spikard-core/src/router.rs +263 -2
  40. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +688 -0
  41. data/vendor/crates/spikard-core/src/{validation.rs → validation/mod.rs} +26 -268
  42. data/vendor/crates/spikard-http/Cargo.toml +12 -16
  43. data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -0
  44. data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -0
  45. data/vendor/crates/spikard-http/src/auth.rs +65 -16
  46. data/vendor/crates/spikard-http/src/background.rs +1614 -3
  47. data/vendor/crates/spikard-http/src/cors.rs +515 -0
  48. data/vendor/crates/spikard-http/src/debug.rs +65 -0
  49. data/vendor/crates/spikard-http/src/di_handler.rs +1322 -77
  50. data/vendor/crates/spikard-http/src/handler_response.rs +711 -0
  51. data/vendor/crates/spikard-http/src/handler_trait.rs +607 -5
  52. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +6 -0
  53. data/vendor/crates/spikard-http/src/lib.rs +33 -28
  54. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +81 -0
  55. data/vendor/crates/spikard-http/src/lifecycle.rs +765 -0
  56. data/vendor/crates/spikard-http/src/middleware/mod.rs +372 -117
  57. data/vendor/crates/spikard-http/src/middleware/multipart.rs +836 -10
  58. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +409 -43
  59. data/vendor/crates/spikard-http/src/middleware/validation.rs +513 -65
  60. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +345 -0
  61. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1055 -0
  62. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +473 -3
  63. data/vendor/crates/spikard-http/src/query_parser.rs +455 -31
  64. data/vendor/crates/spikard-http/src/response.rs +321 -0
  65. data/vendor/crates/spikard-http/src/server/handler.rs +1572 -9
  66. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +136 -0
  67. data/vendor/crates/spikard-http/src/server/mod.rs +875 -178
  68. data/vendor/crates/spikard-http/src/server/request_extraction.rs +674 -23
  69. data/vendor/crates/spikard-http/src/server/routing_factory.rs +599 -0
  70. data/vendor/crates/spikard-http/src/sse.rs +983 -21
  71. data/vendor/crates/spikard-http/src/testing/form.rs +38 -0
  72. data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -2
  73. data/vendor/crates/spikard-http/src/testing.rs +7 -7
  74. data/vendor/crates/spikard-http/src/websocket.rs +1055 -4
  75. data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -0
  76. data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -0
  77. data/vendor/crates/spikard-http/tests/common/mod.rs +26 -0
  78. data/vendor/crates/spikard-http/tests/di_integration.rs +192 -0
  79. data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -0
  80. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -0
  81. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -0
  82. data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -0
  83. data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -0
  84. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -0
  85. data/vendor/crates/spikard-rb/Cargo.toml +10 -4
  86. data/vendor/crates/spikard-rb/build.rs +196 -5
  87. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
  88. data/vendor/crates/spikard-rb/src/{config.rs → config/server_config.rs} +100 -109
  89. data/vendor/crates/spikard-rb/src/conversion.rs +121 -20
  90. data/vendor/crates/spikard-rb/src/di/builder.rs +100 -0
  91. data/vendor/crates/spikard-rb/src/{di.rs → di/mod.rs} +12 -46
  92. data/vendor/crates/spikard-rb/src/handler.rs +100 -107
  93. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
  94. data/vendor/crates/spikard-rb/src/lib.rs +467 -1428
  95. data/vendor/crates/spikard-rb/src/lifecycle.rs +1 -0
  96. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
  97. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +447 -0
  98. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
  99. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -0
  100. data/vendor/crates/spikard-rb/src/server.rs +47 -22
  101. data/vendor/crates/spikard-rb/src/{test_client.rs → testing/client.rs} +187 -40
  102. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
  103. data/vendor/crates/spikard-rb/src/testing/websocket.rs +635 -0
  104. data/vendor/crates/spikard-rb/src/websocket.rs +178 -37
  105. metadata +46 -13
  106. data/vendor/crates/spikard-http/src/parameters.rs +0 -1
  107. data/vendor/crates/spikard-http/src/problem.rs +0 -1
  108. data/vendor/crates/spikard-http/src/router.rs +0 -1
  109. data/vendor/crates/spikard-http/src/schema_registry.rs +0 -1
  110. data/vendor/crates/spikard-http/src/type_hints.rs +0 -1
  111. data/vendor/crates/spikard-http/src/validation.rs +0 -1
  112. data/vendor/crates/spikard-rb/src/test_websocket.rs +0 -221
  113. /data/vendor/crates/spikard-rb/src/{test_sse.rs → testing/sse.rs} +0 -0
@@ -0,0 +1,599 @@
1
+ //! Method routing factory for consolidating HTTP method handler creation.
2
+ //!
3
+ //! This module provides a factory pattern to eliminate duplication in the
4
+ //! create_method_router function. It handles:
5
+ //! - Different HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE)
6
+ //! - Both path variants (with and without path parameters)
7
+ //! - Handler wrapping and request data extraction
8
+ //! - Lifecycle hook integration
9
+
10
+ use crate::handler_trait::Handler;
11
+ use axum::body::Body;
12
+ use axum::extract::{Path, Request as AxumRequest};
13
+ use axum::http::Request;
14
+ use axum::routing::MethodRouter;
15
+ use bytes::Bytes;
16
+ use std::collections::HashMap;
17
+ use std::sync::Arc;
18
+
19
+ use super::lifecycle_execution;
20
+ use super::request_extraction;
21
+
22
+ /// HTTP method type enumeration for routing
23
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
24
+ pub enum HttpMethod {
25
+ Get,
26
+ Post,
27
+ Put,
28
+ Patch,
29
+ Delete,
30
+ Head,
31
+ Trace,
32
+ Options,
33
+ }
34
+
35
+ impl HttpMethod {
36
+ /// Parse from string representation (e.g., "GET", "POST")
37
+ pub fn from_str(s: &str) -> Option<Self> {
38
+ match s {
39
+ "GET" => Some(HttpMethod::Get),
40
+ "POST" => Some(HttpMethod::Post),
41
+ "PUT" => Some(HttpMethod::Put),
42
+ "PATCH" => Some(HttpMethod::Patch),
43
+ "DELETE" => Some(HttpMethod::Delete),
44
+ "HEAD" => Some(HttpMethod::Head),
45
+ "TRACE" => Some(HttpMethod::Trace),
46
+ "OPTIONS" => Some(HttpMethod::Options),
47
+ _ => None,
48
+ }
49
+ }
50
+
51
+ /// Check if this method typically has a request body
52
+ pub fn expects_body(&self) -> bool {
53
+ matches!(self, HttpMethod::Post | HttpMethod::Put | HttpMethod::Patch)
54
+ }
55
+ }
56
+
57
+ /// Factory for creating method routers
58
+ pub struct MethodRouterFactory;
59
+
60
+ impl MethodRouterFactory {
61
+ /// Create a method router for the given HTTP method
62
+ ///
63
+ /// # Arguments
64
+ ///
65
+ /// * `method` - HTTP method string (e.g., "GET", "POST")
66
+ /// * `has_path_params` - Whether the route has path parameters
67
+ /// * `handler` - The request handler
68
+ /// * `hooks` - Optional lifecycle hooks
69
+ ///
70
+ /// # Returns
71
+ ///
72
+ /// A configured MethodRouter or an error if the method is unsupported
73
+ pub fn create(
74
+ method: &str,
75
+ has_path_params: bool,
76
+ handler: Arc<dyn Handler>,
77
+ hooks: Option<Arc<crate::LifecycleHooks>>,
78
+ include_raw_query_params: bool,
79
+ ) -> Result<MethodRouter, String> {
80
+ let http_method = HttpMethod::from_str(method)
81
+ .ok_or_else(|| format!("[spikard-router] unsupported HTTP method: {}", method))?;
82
+
83
+ let include_query_params_json = !handler.prefers_parameter_extraction();
84
+ let include_headers = handler.wants_headers();
85
+ let include_cookies = handler.wants_cookies();
86
+
87
+ Ok(if has_path_params {
88
+ Self::create_with_path_params(
89
+ http_method,
90
+ handler,
91
+ hooks,
92
+ include_raw_query_params,
93
+ include_query_params_json,
94
+ include_headers,
95
+ include_cookies,
96
+ )
97
+ } else {
98
+ Self::create_without_path_params(
99
+ http_method,
100
+ handler,
101
+ hooks,
102
+ include_raw_query_params,
103
+ include_query_params_json,
104
+ include_headers,
105
+ include_cookies,
106
+ )
107
+ })
108
+ }
109
+
110
+ /// Create a method router for a route with path parameters
111
+ fn create_with_path_params(
112
+ method: HttpMethod,
113
+ handler: Arc<dyn Handler>,
114
+ hooks: Option<Arc<crate::LifecycleHooks>>,
115
+ include_raw_query_params: bool,
116
+ include_query_params_json: bool,
117
+ include_headers: bool,
118
+ include_cookies: bool,
119
+ ) -> MethodRouter {
120
+ let handler_clone = handler.clone();
121
+ let hooks_clone = hooks.clone();
122
+ let without_body_options = request_extraction::WithoutBodyExtractionOptions {
123
+ include_raw_query_params,
124
+ include_query_params_json,
125
+ include_headers,
126
+ include_cookies,
127
+ };
128
+
129
+ if method.expects_body() {
130
+ match method {
131
+ HttpMethod::Post => {
132
+ let handler_clone = handler_clone.clone();
133
+ let hooks_clone = hooks_clone.clone();
134
+ axum::routing::post(
135
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
136
+ let handler = handler_clone.clone();
137
+ let hooks = hooks_clone.clone();
138
+ async move {
139
+ let (parts, body) = req.into_parts();
140
+ let request_data =
141
+ request_extraction::create_request_data_with_body(
142
+ &parts,
143
+ path_params.0,
144
+ body,
145
+ include_raw_query_params,
146
+ include_query_params_json,
147
+ include_headers,
148
+ include_cookies,
149
+ )
150
+ .await?;
151
+ let mut req = Request::from_parts(
152
+ parts,
153
+ Body::from(request_data.raw_body.clone().unwrap_or_else(Bytes::new)),
154
+ );
155
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
156
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
157
+ .await
158
+ }
159
+ },
160
+ )
161
+ }
162
+ HttpMethod::Put => {
163
+ let handler_clone = handler_clone.clone();
164
+ let hooks_clone = hooks_clone.clone();
165
+ axum::routing::put(
166
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
167
+ let handler = handler_clone.clone();
168
+ let hooks = hooks_clone.clone();
169
+ async move {
170
+ let (parts, body) = req.into_parts();
171
+ let request_data =
172
+ request_extraction::create_request_data_with_body(
173
+ &parts,
174
+ path_params.0,
175
+ body,
176
+ include_raw_query_params,
177
+ include_query_params_json,
178
+ include_headers,
179
+ include_cookies,
180
+ )
181
+ .await?;
182
+ let mut req = Request::from_parts(
183
+ parts,
184
+ Body::from(request_data.raw_body.clone().unwrap_or_else(Bytes::new)),
185
+ );
186
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
187
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
188
+ .await
189
+ }
190
+ },
191
+ )
192
+ }
193
+ HttpMethod::Patch => {
194
+ let handler_clone = handler_clone.clone();
195
+ let hooks_clone = hooks_clone.clone();
196
+ axum::routing::patch(
197
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
198
+ let handler = handler_clone.clone();
199
+ let hooks = hooks_clone.clone();
200
+ async move {
201
+ let (parts, body) = req.into_parts();
202
+ let request_data =
203
+ request_extraction::create_request_data_with_body(
204
+ &parts,
205
+ path_params.0,
206
+ body,
207
+ include_raw_query_params,
208
+ include_query_params_json,
209
+ include_headers,
210
+ include_cookies,
211
+ )
212
+ .await?;
213
+ let mut req = Request::from_parts(
214
+ parts,
215
+ Body::from(request_data.raw_body.clone().unwrap_or_else(Bytes::new)),
216
+ );
217
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
218
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
219
+ .await
220
+ }
221
+ },
222
+ )
223
+ }
224
+ _ => MethodRouter::new(),
225
+ }
226
+ } else {
227
+ match method {
228
+ HttpMethod::Get => {
229
+ let handler_clone = handler_clone.clone();
230
+ let hooks_clone = hooks_clone.clone();
231
+ axum::routing::get(
232
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
233
+ let handler = handler_clone.clone();
234
+ let hooks = hooks_clone.clone();
235
+ async move {
236
+ let request_data = request_extraction::create_request_data_without_body(
237
+ req.uri(),
238
+ req.method(),
239
+ req.headers(),
240
+ path_params.0,
241
+ without_body_options,
242
+ );
243
+ let mut req = req;
244
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
245
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
246
+ .await
247
+ }
248
+ },
249
+ )
250
+ }
251
+ HttpMethod::Delete => {
252
+ let handler_clone = handler_clone.clone();
253
+ let hooks_clone = hooks_clone.clone();
254
+ axum::routing::delete(
255
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
256
+ let handler = handler_clone.clone();
257
+ let hooks = hooks_clone.clone();
258
+ async move {
259
+ let request_data = request_extraction::create_request_data_without_body(
260
+ req.uri(),
261
+ req.method(),
262
+ req.headers(),
263
+ path_params.0,
264
+ without_body_options,
265
+ );
266
+ let mut req = req;
267
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
268
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
269
+ .await
270
+ }
271
+ },
272
+ )
273
+ }
274
+ HttpMethod::Head => {
275
+ let handler_clone = handler_clone.clone();
276
+ let hooks_clone = hooks_clone.clone();
277
+ axum::routing::head(
278
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
279
+ let handler = handler_clone.clone();
280
+ let hooks = hooks_clone.clone();
281
+ async move {
282
+ let request_data = request_extraction::create_request_data_without_body(
283
+ req.uri(),
284
+ req.method(),
285
+ req.headers(),
286
+ path_params.0,
287
+ without_body_options,
288
+ );
289
+ let mut req = req;
290
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
291
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
292
+ .await
293
+ }
294
+ },
295
+ )
296
+ }
297
+ HttpMethod::Trace => {
298
+ let handler_clone = handler_clone.clone();
299
+ let hooks_clone = hooks_clone.clone();
300
+ axum::routing::trace(
301
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
302
+ let handler = handler_clone.clone();
303
+ let hooks = hooks_clone.clone();
304
+ async move {
305
+ let request_data = request_extraction::create_request_data_without_body(
306
+ req.uri(),
307
+ req.method(),
308
+ req.headers(),
309
+ path_params.0,
310
+ without_body_options,
311
+ );
312
+ let mut req = req;
313
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
314
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
315
+ .await
316
+ }
317
+ },
318
+ )
319
+ }
320
+ HttpMethod::Options => {
321
+ let handler_clone = handler_clone.clone();
322
+ let hooks_clone = hooks_clone.clone();
323
+ axum::routing::options(
324
+ move |path_params: Path<HashMap<String, String>>, req: AxumRequest| {
325
+ let handler = handler_clone.clone();
326
+ let hooks = hooks_clone.clone();
327
+ async move {
328
+ let request_data = request_extraction::create_request_data_without_body(
329
+ req.uri(),
330
+ req.method(),
331
+ req.headers(),
332
+ path_params.0,
333
+ without_body_options,
334
+ );
335
+ let mut req = req;
336
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
337
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
338
+ .await
339
+ }
340
+ },
341
+ )
342
+ }
343
+ _ => MethodRouter::new(),
344
+ }
345
+ }
346
+ }
347
+
348
+ /// Create a method router for a route without path parameters
349
+ fn create_without_path_params(
350
+ method: HttpMethod,
351
+ handler: Arc<dyn Handler>,
352
+ hooks: Option<Arc<crate::LifecycleHooks>>,
353
+ include_raw_query_params: bool,
354
+ include_query_params_json: bool,
355
+ include_headers: bool,
356
+ include_cookies: bool,
357
+ ) -> MethodRouter {
358
+ let handler_clone = handler.clone();
359
+ let hooks_clone = hooks.clone();
360
+ let without_body_options = request_extraction::WithoutBodyExtractionOptions {
361
+ include_raw_query_params,
362
+ include_query_params_json,
363
+ include_headers,
364
+ include_cookies,
365
+ };
366
+
367
+ if method.expects_body() {
368
+ match method {
369
+ HttpMethod::Post => {
370
+ let handler_clone = handler_clone.clone();
371
+ let hooks_clone = hooks_clone.clone();
372
+ axum::routing::post(move |req: AxumRequest| {
373
+ let handler = handler_clone.clone();
374
+ let hooks = hooks_clone.clone();
375
+ async move {
376
+ let (parts, body) = req.into_parts();
377
+ let request_data =
378
+ request_extraction::create_request_data_with_body(
379
+ &parts,
380
+ HashMap::new(),
381
+ body,
382
+ include_raw_query_params,
383
+ include_query_params_json,
384
+ include_headers,
385
+ include_cookies,
386
+ )
387
+ .await?;
388
+ let mut req = Request::from_parts(
389
+ parts,
390
+ Body::from(request_data.raw_body.clone().unwrap_or_else(Bytes::new)),
391
+ );
392
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
393
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
394
+ .await
395
+ }
396
+ })
397
+ }
398
+ HttpMethod::Put => {
399
+ let handler_clone = handler_clone.clone();
400
+ let hooks_clone = hooks_clone.clone();
401
+ axum::routing::put(move |req: AxumRequest| {
402
+ let handler = handler_clone.clone();
403
+ let hooks = hooks_clone.clone();
404
+ async move {
405
+ let (parts, body) = req.into_parts();
406
+ let request_data =
407
+ request_extraction::create_request_data_with_body(
408
+ &parts,
409
+ HashMap::new(),
410
+ body,
411
+ include_raw_query_params,
412
+ include_query_params_json,
413
+ include_headers,
414
+ include_cookies,
415
+ )
416
+ .await?;
417
+ let mut req = Request::from_parts(
418
+ parts,
419
+ Body::from(request_data.raw_body.clone().unwrap_or_else(Bytes::new)),
420
+ );
421
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
422
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
423
+ .await
424
+ }
425
+ })
426
+ }
427
+ HttpMethod::Patch => {
428
+ let handler_clone = handler_clone.clone();
429
+ let hooks_clone = hooks_clone.clone();
430
+ axum::routing::patch(move |req: AxumRequest| {
431
+ let handler = handler_clone.clone();
432
+ let hooks = hooks_clone.clone();
433
+ async move {
434
+ let (parts, body) = req.into_parts();
435
+ let request_data =
436
+ request_extraction::create_request_data_with_body(
437
+ &parts,
438
+ HashMap::new(),
439
+ body,
440
+ include_raw_query_params,
441
+ include_query_params_json,
442
+ include_headers,
443
+ include_cookies,
444
+ )
445
+ .await?;
446
+ let mut req = Request::from_parts(
447
+ parts,
448
+ Body::from(request_data.raw_body.clone().unwrap_or_else(Bytes::new)),
449
+ );
450
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
451
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
452
+ .await
453
+ }
454
+ })
455
+ }
456
+ _ => MethodRouter::new(),
457
+ }
458
+ } else {
459
+ match method {
460
+ HttpMethod::Get => {
461
+ let handler_clone = handler_clone.clone();
462
+ let hooks_clone = hooks_clone.clone();
463
+ axum::routing::get(move |req: AxumRequest| {
464
+ let handler = handler_clone.clone();
465
+ let hooks = hooks_clone.clone();
466
+ async move {
467
+ let request_data = request_extraction::create_request_data_without_body(
468
+ req.uri(),
469
+ req.method(),
470
+ req.headers(),
471
+ HashMap::new(),
472
+ without_body_options,
473
+ );
474
+ let mut req = req;
475
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
476
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
477
+ .await
478
+ }
479
+ })
480
+ }
481
+ HttpMethod::Delete => {
482
+ let handler_clone = handler_clone.clone();
483
+ let hooks_clone = hooks_clone.clone();
484
+ axum::routing::delete(move |req: AxumRequest| {
485
+ let handler = handler_clone.clone();
486
+ let hooks = hooks_clone.clone();
487
+ async move {
488
+ let request_data = request_extraction::create_request_data_without_body(
489
+ req.uri(),
490
+ req.method(),
491
+ req.headers(),
492
+ HashMap::new(),
493
+ without_body_options,
494
+ );
495
+ let mut req = req;
496
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
497
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
498
+ .await
499
+ }
500
+ })
501
+ }
502
+ HttpMethod::Head => {
503
+ let handler_clone = handler_clone.clone();
504
+ let hooks_clone = hooks_clone.clone();
505
+ axum::routing::head(move |req: AxumRequest| {
506
+ let handler = handler_clone.clone();
507
+ let hooks = hooks_clone.clone();
508
+ async move {
509
+ let request_data = request_extraction::create_request_data_without_body(
510
+ req.uri(),
511
+ req.method(),
512
+ req.headers(),
513
+ HashMap::new(),
514
+ without_body_options,
515
+ );
516
+ let mut req = req;
517
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
518
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
519
+ .await
520
+ }
521
+ })
522
+ }
523
+ HttpMethod::Trace => {
524
+ let handler_clone = handler_clone.clone();
525
+ let hooks_clone = hooks_clone.clone();
526
+ axum::routing::trace(move |req: AxumRequest| {
527
+ let handler = handler_clone.clone();
528
+ let hooks = hooks_clone.clone();
529
+ async move {
530
+ let request_data = request_extraction::create_request_data_without_body(
531
+ req.uri(),
532
+ req.method(),
533
+ req.headers(),
534
+ HashMap::new(),
535
+ without_body_options,
536
+ );
537
+ let mut req = req;
538
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
539
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
540
+ .await
541
+ }
542
+ })
543
+ }
544
+ HttpMethod::Options => {
545
+ let handler_clone = handler_clone.clone();
546
+ let hooks_clone = hooks_clone.clone();
547
+ axum::routing::options(move |req: AxumRequest| {
548
+ let handler = handler_clone.clone();
549
+ let hooks = hooks_clone.clone();
550
+ async move {
551
+ let request_data = request_extraction::create_request_data_without_body(
552
+ req.uri(),
553
+ req.method(),
554
+ req.headers(),
555
+ HashMap::new(),
556
+ without_body_options,
557
+ );
558
+ let mut req = req;
559
+ req.extensions_mut().insert(Arc::new(request_data.clone()));
560
+ lifecycle_execution::execute_with_lifecycle_hooks(req, request_data, handler, hooks)
561
+ .await
562
+ }
563
+ })
564
+ }
565
+ _ => MethodRouter::new(),
566
+ }
567
+ }
568
+ }
569
+ }
570
+
571
+ #[cfg(test)]
572
+ mod tests {
573
+ use super::*;
574
+
575
+ #[test]
576
+ fn test_http_method_from_str() {
577
+ assert_eq!(HttpMethod::from_str("GET"), Some(HttpMethod::Get));
578
+ assert_eq!(HttpMethod::from_str("POST"), Some(HttpMethod::Post));
579
+ assert_eq!(HttpMethod::from_str("PUT"), Some(HttpMethod::Put));
580
+ assert_eq!(HttpMethod::from_str("PATCH"), Some(HttpMethod::Patch));
581
+ assert_eq!(HttpMethod::from_str("DELETE"), Some(HttpMethod::Delete));
582
+ assert_eq!(HttpMethod::from_str("HEAD"), Some(HttpMethod::Head));
583
+ assert_eq!(HttpMethod::from_str("TRACE"), Some(HttpMethod::Trace));
584
+ assert_eq!(HttpMethod::from_str("OPTIONS"), Some(HttpMethod::Options));
585
+ assert_eq!(HttpMethod::from_str("INVALID"), None);
586
+ }
587
+
588
+ #[test]
589
+ fn test_http_method_expects_body() {
590
+ assert!(!HttpMethod::Get.expects_body());
591
+ assert!(HttpMethod::Post.expects_body());
592
+ assert!(HttpMethod::Put.expects_body());
593
+ assert!(HttpMethod::Patch.expects_body());
594
+ assert!(!HttpMethod::Delete.expects_body());
595
+ assert!(!HttpMethod::Head.expects_body());
596
+ assert!(!HttpMethod::Trace.expects_body());
597
+ assert!(!HttpMethod::Options.expects_body());
598
+ }
599
+ }