spikard 0.8.2 → 0.10.1

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -10
  3. data/ext/spikard_rb/Cargo.lock +234 -162
  4. data/ext/spikard_rb/Cargo.toml +3 -3
  5. data/ext/spikard_rb/extconf.rb +4 -3
  6. data/lib/spikard/config.rb +88 -12
  7. data/lib/spikard/testing.rb +3 -1
  8. data/lib/spikard/version.rb +1 -1
  9. data/lib/spikard.rb +11 -0
  10. data/vendor/crates/spikard-bindings-shared/Cargo.toml +11 -6
  11. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +8 -8
  12. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +63 -25
  13. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +20 -4
  14. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +10 -4
  15. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +25 -22
  16. data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +14 -12
  17. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +24 -10
  18. data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +829 -0
  19. data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +587 -0
  20. data/vendor/crates/spikard-bindings-shared/src/lib.rs +7 -0
  21. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +17 -11
  22. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +51 -73
  23. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +442 -4
  24. data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +944 -0
  25. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +22 -10
  26. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +28 -10
  27. data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +3 -2
  28. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +13 -13
  29. data/vendor/crates/spikard-bindings-shared/tests/{comprehensive_coverage.rs → full_coverage.rs} +10 -5
  30. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +14 -14
  31. data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +669 -0
  32. data/vendor/crates/spikard-core/Cargo.toml +11 -3
  33. data/vendor/crates/spikard-core/src/bindings/response.rs +6 -9
  34. data/vendor/crates/spikard-core/src/debug.rs +2 -2
  35. data/vendor/crates/spikard-core/src/di/container.rs +2 -2
  36. data/vendor/crates/spikard-core/src/di/error.rs +1 -1
  37. data/vendor/crates/spikard-core/src/di/factory.rs +9 -5
  38. data/vendor/crates/spikard-core/src/di/graph.rs +1 -0
  39. data/vendor/crates/spikard-core/src/di/resolved.rs +25 -2
  40. data/vendor/crates/spikard-core/src/di/value.rs +2 -1
  41. data/vendor/crates/spikard-core/src/errors.rs +3 -0
  42. data/vendor/crates/spikard-core/src/http.rs +94 -18
  43. data/vendor/crates/spikard-core/src/lifecycle.rs +85 -61
  44. data/vendor/crates/spikard-core/src/parameters.rs +75 -54
  45. data/vendor/crates/spikard-core/src/problem.rs +19 -5
  46. data/vendor/crates/spikard-core/src/request_data.rs +16 -24
  47. data/vendor/crates/spikard-core/src/router.rs +26 -6
  48. data/vendor/crates/spikard-core/src/schema_registry.rs +25 -11
  49. data/vendor/crates/spikard-core/src/type_hints.rs +14 -7
  50. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +30 -16
  51. data/vendor/crates/spikard-core/src/validation/mod.rs +46 -33
  52. data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +1 -1
  53. data/vendor/crates/spikard-core/tests/error_mapper.rs +2 -2
  54. data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +1 -1
  55. data/vendor/crates/spikard-core/tests/parameters_full.rs +1 -1
  56. data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +1 -1
  57. data/vendor/crates/spikard-core/tests/validation_coverage.rs +4 -4
  58. data/vendor/crates/spikard-http/Cargo.toml +11 -2
  59. data/vendor/crates/spikard-http/src/cors.rs +32 -11
  60. data/vendor/crates/spikard-http/src/di_handler.rs +12 -8
  61. data/vendor/crates/spikard-http/src/grpc/framing.rs +469 -0
  62. data/vendor/crates/spikard-http/src/grpc/handler.rs +887 -25
  63. data/vendor/crates/spikard-http/src/grpc/mod.rs +114 -22
  64. data/vendor/crates/spikard-http/src/grpc/service.rs +232 -2
  65. data/vendor/crates/spikard-http/src/grpc/streaming.rs +80 -2
  66. data/vendor/crates/spikard-http/src/handler_trait.rs +204 -27
  67. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +15 -15
  68. data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +2 -2
  69. data/vendor/crates/spikard-http/src/jsonrpc/router.rs +2 -2
  70. data/vendor/crates/spikard-http/src/lib.rs +1 -1
  71. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +2 -2
  72. data/vendor/crates/spikard-http/src/lifecycle.rs +4 -4
  73. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +2 -0
  74. data/vendor/crates/spikard-http/src/server/fast_router.rs +186 -0
  75. data/vendor/crates/spikard-http/src/server/grpc_routing.rs +324 -23
  76. data/vendor/crates/spikard-http/src/server/handler.rs +33 -22
  77. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +21 -2
  78. data/vendor/crates/spikard-http/src/server/mod.rs +125 -20
  79. data/vendor/crates/spikard-http/src/server/request_extraction.rs +126 -44
  80. data/vendor/crates/spikard-http/src/server/routing_factory.rs +80 -69
  81. data/vendor/crates/spikard-http/tests/common/handlers.rs +2 -2
  82. data/vendor/crates/spikard-http/tests/common/test_builders.rs +12 -12
  83. data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +2 -2
  84. data/vendor/crates/spikard-http/tests/di_integration.rs +6 -6
  85. data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +430 -0
  86. data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +738 -0
  87. data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +13 -9
  88. data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +974 -0
  89. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +2 -2
  90. data/vendor/crates/spikard-http/tests/request_extraction_full.rs +4 -4
  91. data/vendor/crates/spikard-http/tests/server_config_builder.rs +2 -2
  92. data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +1 -0
  93. data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +140 -0
  94. data/vendor/crates/spikard-rb/Cargo.toml +11 -1
  95. data/vendor/crates/spikard-rb/build.rs +1 -0
  96. data/vendor/crates/spikard-rb/src/conversion.rs +138 -4
  97. data/vendor/crates/spikard-rb/src/grpc/handler.rs +706 -229
  98. data/vendor/crates/spikard-rb/src/grpc/mod.rs +6 -2
  99. data/vendor/crates/spikard-rb/src/gvl.rs +2 -2
  100. data/vendor/crates/spikard-rb/src/handler.rs +169 -91
  101. data/vendor/crates/spikard-rb/src/lib.rs +502 -62
  102. data/vendor/crates/spikard-rb/src/lifecycle.rs +31 -3
  103. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +108 -43
  104. data/vendor/crates/spikard-rb/src/request.rs +117 -20
  105. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +52 -25
  106. data/vendor/crates/spikard-rb/src/server.rs +23 -14
  107. data/vendor/crates/spikard-rb/src/testing/client.rs +5 -4
  108. data/vendor/crates/spikard-rb/src/testing/sse.rs +1 -36
  109. data/vendor/crates/spikard-rb/src/testing/websocket.rs +3 -38
  110. data/vendor/crates/spikard-rb/src/websocket.rs +32 -23
  111. data/vendor/crates/spikard-rb-macros/Cargo.toml +9 -1
  112. data/vendor/crates/spikard-rb-macros/src/lib.rs +4 -5
  113. metadata +14 -4
  114. data/vendor/bundle/ruby/3.4.0/gems/diff-lcs-1.6.2/mise.toml +0 -5
  115. data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.10.0/build/buildkitd.toml +0 -2
@@ -10,7 +10,7 @@
10
10
  //! `LanguageLifecycleHook` to provide language-specific hook invocation, while
11
11
  //! `LifecycleExecutor` handles the common logic:
12
12
  //!
13
- //! - Hook result type handling (Continue vs ShortCircuit)
13
+ //! - Hook result type handling (Continue vs `ShortCircuit`)
14
14
  //! - Response/Request building from hook results
15
15
  //! - Error handling and conversion
16
16
  //!
@@ -73,7 +73,8 @@ pub struct RequestModifications {
73
73
 
74
74
  impl HookResultData {
75
75
  /// Create a Continue result (pass through)
76
- pub fn continue_execution() -> Self {
76
+ #[must_use]
77
+ pub const fn continue_execution() -> Self {
77
78
  Self {
78
79
  continue_execution: true,
79
80
  status_code: None,
@@ -84,7 +85,8 @@ impl HookResultData {
84
85
  }
85
86
 
86
87
  /// Create a short-circuit response result
87
- pub fn short_circuit(status_code: u16, body: Vec<u8>, headers: Option<HashMap<String, String>>) -> Self {
88
+ #[must_use]
89
+ pub const fn short_circuit(status_code: u16, body: Vec<u8>, headers: Option<HashMap<String, String>>) -> Self {
88
90
  Self {
89
91
  continue_execution: false,
90
92
  status_code: Some(status_code),
@@ -95,7 +97,8 @@ impl HookResultData {
95
97
  }
96
98
 
97
99
  /// Create a request modification result
98
- pub fn modify_request(modifications: RequestModifications) -> Self {
100
+ #[must_use]
101
+ pub const fn modify_request(modifications: RequestModifications) -> Self {
99
102
  Self {
100
103
  continue_execution: true,
101
104
  status_code: None,
@@ -117,6 +120,10 @@ pub trait LanguageLifecycleHook: Send + Sync {
117
120
  /// Prepare hook data from the incoming request/response
118
121
  ///
119
122
  /// This should convert axum HTTP types to language-specific representations.
123
+ ///
124
+ /// # Errors
125
+ ///
126
+ /// Returns an error if hook data preparation fails.
120
127
  fn prepare_hook_data(&self, req: &Request<Body>) -> Result<Self::HookData, String>;
121
128
 
122
129
  /// Invoke the language hook and return normalized result data
@@ -139,13 +146,17 @@ pub struct LifecycleExecutor<L: LanguageLifecycleHook> {
139
146
 
140
147
  impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
141
148
  /// Create a new executor for the given hook
142
- pub fn new(hook: Arc<L>) -> Self {
149
+ pub const fn new(hook: Arc<L>) -> Self {
143
150
  Self { hook }
144
151
  }
145
152
 
146
153
  /// Execute a request hook, handling Continue/ShortCircuit semantics
147
154
  ///
148
155
  /// Returns either the modified request or a short-circuit response.
156
+ ///
157
+ /// # Errors
158
+ ///
159
+ /// Returns an error if hook execution or modification fails.
149
160
  pub async fn execute_request_hook(
150
161
  &self,
151
162
  req: Request<Body>,
@@ -154,12 +165,12 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
154
165
  let result = self.hook.invoke_hook(hook_data).await?;
155
166
 
156
167
  if !result.continue_execution {
157
- let response = self.build_response_from_hook_result(&result)?;
168
+ let response = Self::build_response_from_hook_result(&result)?;
158
169
  return Ok(Err(response));
159
170
  }
160
171
 
161
172
  if let Some(modifications) = result.request_modifications {
162
- let modified_req = self.apply_request_modifications(req, modifications)?;
173
+ let modified_req = Self::apply_request_modifications(req, modifications)?;
163
174
  Ok(Ok(modified_req))
164
175
  } else {
165
176
  Ok(Ok(req))
@@ -170,6 +181,11 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
170
181
  ///
171
182
  /// Response hooks can only continue or modify the response,
172
183
  /// never short-circuit.
184
+ /// Execute the lifecycle hook on an outgoing response
185
+ ///
186
+ /// # Errors
187
+ ///
188
+ /// Returns an error if hook execution or response building fails.
173
189
  pub async fn execute_response_hook(&self, resp: Response<Body>) -> Result<Response<Body>, String> {
174
190
  let (parts, body) = resp.into_parts();
175
191
  let body_bytes = extract_body(body).await?;
@@ -178,7 +194,7 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
178
194
  .method("GET")
179
195
  .uri("/")
180
196
  .body(Body::empty())
181
- .map_err(|e| format!("Failed to build dummy request: {}", e))?;
197
+ .map_err(|e| format!("Failed to build dummy request: {e}"))?;
182
198
 
183
199
  let hook_data = self.hook.prepare_hook_data(&dummy_req)?;
184
200
  let result = self.hook.invoke_hook(hook_data).await?;
@@ -198,7 +214,7 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
198
214
  }
199
215
  }
200
216
 
201
- for (name, value) in parts.headers.iter() {
217
+ for (name, value) in &parts.headers {
202
218
  let key_str = name.as_str().to_lowercase();
203
219
  if !header_mod_keys.contains(&key_str) {
204
220
  builder = builder.header(name, value);
@@ -208,7 +224,7 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
208
224
  let body = modifications.body.unwrap_or(body_bytes);
209
225
  return builder
210
226
  .body(Body::from(body))
211
- .map_err(|e| format!("Failed to build modified response: {}", e));
227
+ .map_err(|e| format!("Failed to build modified response: {e}"));
212
228
  }
213
229
 
214
230
  let mut builder = Response::builder().status(parts.status);
@@ -219,14 +235,14 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
219
235
  }
220
236
  builder
221
237
  .body(Body::from(body_bytes))
222
- .map_err(|e| format!("Failed to rebuild response: {}", e))
238
+ .map_err(|e| format!("Failed to rebuild response: {e}"))
223
239
  }
224
240
 
225
241
  /// Build an axum Response from hook result data
226
- fn build_response_from_hook_result(&self, result: &HookResultData) -> Result<Response<Body>, String> {
242
+ fn build_response_from_hook_result(result: &HookResultData) -> Result<Response<Body>, String> {
227
243
  let status_code = result.status_code.unwrap_or(200);
228
244
  let status =
229
- StatusCode::from_u16(status_code).map_err(|e| format!("Invalid status code {}: {}", status_code, e))?;
245
+ StatusCode::from_u16(status_code).map_err(|e| format!("Invalid status code {status_code}: {e}"))?;
230
246
 
231
247
  let mut builder = Response::builder().status(status);
232
248
 
@@ -236,11 +252,7 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
236
252
  }
237
253
  }
238
254
 
239
- if !builder
240
- .headers_ref()
241
- .map(|h| h.contains_key("content-type"))
242
- .unwrap_or(false)
243
- {
255
+ if !builder.headers_ref().is_some_and(|h| h.contains_key("content-type")) {
244
256
  builder = builder.header("content-type", "application/json");
245
257
  }
246
258
 
@@ -248,43 +260,33 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
248
260
 
249
261
  builder
250
262
  .body(Body::from(body))
251
- .map_err(|e| format!("Failed to build response: {}", e))
263
+ .map_err(|e| format!("Failed to build response: {e}"))
252
264
  }
253
265
 
254
266
  /// Apply request modifications to a request
255
- fn apply_request_modifications(
256
- &self,
257
- req: Request<Body>,
258
- mods: RequestModifications,
259
- ) -> Result<Request<Body>, String> {
267
+ fn apply_request_modifications(req: Request<Body>, mods: RequestModifications) -> Result<Request<Body>, String> {
260
268
  let (mut parts, body) = req.into_parts();
261
269
 
262
270
  if let Some(method) = &mods.method {
263
- parts.method = method
264
- .parse()
265
- .map_err(|e| format!("Invalid method '{}': {}", method, e))?;
271
+ parts.method = method.parse().map_err(|e| format!("Invalid method '{method}': {e}"))?;
266
272
  }
267
273
 
268
274
  if let Some(path) = &mods.path {
269
- parts.uri = path.parse().map_err(|e| format!("Invalid path '{}': {}", path, e))?;
275
+ parts.uri = path.parse().map_err(|e| format!("Invalid path '{path}': {e}"))?;
270
276
  }
271
277
 
272
278
  if let Some(new_headers) = &mods.headers {
273
279
  for (key, value) in new_headers {
274
280
  let header_name: http::header::HeaderName =
275
- key.parse().map_err(|_| format!("Invalid header name: {}", key))?;
281
+ key.parse().map_err(|_| format!("Invalid header name: {key}"))?;
276
282
  let header_value: http::header::HeaderValue = value
277
283
  .parse()
278
- .map_err(|_| format!("Invalid header value for {}: {}", key, value))?;
284
+ .map_err(|_| format!("Invalid header value for {key}: {value}"))?;
279
285
  parts.headers.insert(header_name, header_value);
280
286
  }
281
287
  }
282
288
 
283
- let body = if let Some(new_body) = mods.body {
284
- Body::from(new_body)
285
- } else {
286
- body
287
- };
289
+ let body = mods.body.map_or(body, Body::from);
288
290
 
289
291
  Ok(Request::from_parts(parts, body))
290
292
  }
@@ -293,13 +295,17 @@ impl<L: LanguageLifecycleHook> LifecycleExecutor<L> {
293
295
  /// Extract body bytes from an axum Body
294
296
  ///
295
297
  /// This is a helper used by lifecycle executors to read response bodies.
298
+ ///
299
+ /// # Errors
300
+ ///
301
+ /// Returns an error if body collection fails.
296
302
  pub async fn extract_body(body: Body) -> Result<Vec<u8>, String> {
297
303
  use http_body_util::BodyExt;
298
304
 
299
305
  let bytes = body
300
306
  .collect()
301
307
  .await
302
- .map_err(|e| format!("Failed to read body: {}", e))?
308
+ .map_err(|e| format!("Failed to read body: {e}"))?
303
309
  .to_bytes();
304
310
  Ok(bytes.to_vec())
305
311
  }
@@ -338,7 +344,7 @@ mod tests {
338
344
  body: None,
339
345
  };
340
346
 
341
- let result = HookResultData::modify_request(mods.clone());
347
+ let result = HookResultData::modify_request(mods);
342
348
  assert!(result.continue_execution);
343
349
  assert_eq!(result.status_code, None);
344
350
  assert_eq!(
@@ -469,12 +475,8 @@ mod tests {
469
475
  headers.insert("X-Custom".to_string(), "value".to_string());
470
476
 
471
477
  let result = HookResultData::short_circuit(201, b"Created".to_vec(), Some(headers));
472
- let hook = Arc::new(MockHook {
473
- result: HookResultData::continue_execution(),
474
- });
475
- let executor = LifecycleExecutor::new(hook);
476
478
 
477
- let response = executor.build_response_from_hook_result(&result).unwrap();
479
+ let response = LifecycleExecutor::<MockHook>::build_response_from_hook_result(&result).unwrap();
478
480
  assert_eq!(response.status(), StatusCode::CREATED);
479
481
  assert_eq!(response.headers().get("X-Custom").unwrap().to_str().unwrap(), "value");
480
482
  }
@@ -482,12 +484,8 @@ mod tests {
482
484
  #[tokio::test]
483
485
  async fn test_build_response_from_hook_result_default_content_type() {
484
486
  let result = HookResultData::short_circuit(200, b"{}".to_vec(), None);
485
- let hook = Arc::new(MockHook {
486
- result: HookResultData::continue_execution(),
487
- });
488
- let executor = LifecycleExecutor::new(hook);
489
487
 
490
- let response = executor.build_response_from_hook_result(&result).unwrap();
488
+ let response = LifecycleExecutor::<MockHook>::build_response_from_hook_result(&result).unwrap();
491
489
  assert_eq!(
492
490
  response.headers().get("content-type").unwrap().to_str().unwrap(),
493
491
  "application/json"
@@ -502,13 +500,9 @@ mod tests {
502
500
  headers: None,
503
501
  body: None,
504
502
  };
505
- let hook = Arc::new(MockHook {
506
- result: HookResultData::continue_execution(),
507
- });
508
- let executor = LifecycleExecutor::new(hook);
509
503
 
510
504
  let req = Request::builder().method("GET").body(Body::empty()).unwrap();
511
- let modified = executor.apply_request_modifications(req, mods).unwrap();
505
+ let modified = LifecycleExecutor::<MockHook>::apply_request_modifications(req, mods).unwrap();
512
506
 
513
507
  assert_eq!(modified.method(), "PATCH");
514
508
  }
@@ -521,13 +515,9 @@ mod tests {
521
515
  headers: None,
522
516
  body: None,
523
517
  };
524
- let hook = Arc::new(MockHook {
525
- result: HookResultData::continue_execution(),
526
- });
527
- let executor = LifecycleExecutor::new(hook);
528
518
 
529
519
  let req = Request::builder().uri("/api/v1/users").body(Body::empty()).unwrap();
530
- let modified = executor.apply_request_modifications(req, mods).unwrap();
520
+ let modified = LifecycleExecutor::<MockHook>::apply_request_modifications(req, mods).unwrap();
531
521
 
532
522
  assert_eq!(modified.uri().path(), "/api/v2/users");
533
523
  }
@@ -543,13 +533,9 @@ mod tests {
543
533
  headers: Some(new_headers),
544
534
  body: None,
545
535
  };
546
- let hook = Arc::new(MockHook {
547
- result: HookResultData::continue_execution(),
548
- });
549
- let executor = LifecycleExecutor::new(hook);
550
536
 
551
537
  let req = Request::builder().body(Body::empty()).unwrap();
552
- let modified = executor.apply_request_modifications(req, mods).unwrap();
538
+ let modified = LifecycleExecutor::<MockHook>::apply_request_modifications(req, mods).unwrap();
553
539
 
554
540
  assert_eq!(
555
541
  modified.headers().get("Authorization").unwrap().to_str().unwrap(),
@@ -566,13 +552,9 @@ mod tests {
566
552
  headers: None,
567
553
  body: Some(new_body.clone()),
568
554
  };
569
- let hook = Arc::new(MockHook {
570
- result: HookResultData::continue_execution(),
571
- });
572
- let executor = LifecycleExecutor::new(hook);
573
555
 
574
556
  let req = Request::builder().body(Body::from("original body")).unwrap();
575
- let modified = executor.apply_request_modifications(req, mods).unwrap();
557
+ let modified = LifecycleExecutor::<MockHook>::apply_request_modifications(req, mods).unwrap();
576
558
 
577
559
  let body_bytes = extract_body(modified.into_body()).await.unwrap();
578
560
  assert_eq!(body_bytes, new_body);
@@ -581,18 +563,14 @@ mod tests {
581
563
  #[tokio::test]
582
564
  async fn test_apply_request_modifications_invalid_method() {
583
565
  let mods = RequestModifications {
584
- method: Some("".to_string()),
566
+ method: Some(String::new()),
585
567
  path: None,
586
568
  headers: None,
587
569
  body: None,
588
570
  };
589
- let hook = Arc::new(MockHook {
590
- result: HookResultData::continue_execution(),
591
- });
592
- let executor = LifecycleExecutor::new(hook);
593
571
 
594
572
  let req = Request::builder().body(Body::empty()).unwrap();
595
- let result = executor.apply_request_modifications(req, mods);
573
+ let result = LifecycleExecutor::<MockHook>::apply_request_modifications(req, mods);
596
574
 
597
575
  assert!(result.is_err());
598
576
  assert!(result.unwrap_err().contains("Invalid method"));