spikard 0.13.0 → 0.15.2

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 (207) hide show
  1. checksums.yaml +4 -4
  2. data/Steepfile +6 -0
  3. data/ext/spikard_rb/extconf.rb +1 -2
  4. data/ext/spikard_rb/{Cargo.lock → native/Cargo.lock} +819 -424
  5. data/ext/spikard_rb/native/Cargo.toml +24 -0
  6. data/ext/spikard_rb/src/lib.rs +5366 -3
  7. data/lib/spikard/native.rb +86 -0
  8. data/lib/spikard/version.rb +6 -1
  9. data/lib/spikard.rb +8 -52
  10. data/lib/spikard_rb.so +0 -0
  11. data/sig/types.rbs +427 -0
  12. metadata +14 -243
  13. data/LICENSE +0 -1
  14. data/README.md +0 -285
  15. data/ext/spikard_rb/Cargo.toml +0 -17
  16. data/lib/spikard/app.rb +0 -458
  17. data/lib/spikard/background.rb +0 -58
  18. data/lib/spikard/config.rb +0 -506
  19. data/lib/spikard/converters.rb +0 -13
  20. data/lib/spikard/grpc.rb +0 -232
  21. data/lib/spikard/handler_wrapper.rb +0 -113
  22. data/lib/spikard/provide.rb +0 -315
  23. data/lib/spikard/response.rb +0 -198
  24. data/lib/spikard/schema.rb +0 -243
  25. data/lib/spikard/sse.rb +0 -111
  26. data/lib/spikard/streaming_response.rb +0 -44
  27. data/lib/spikard/testing.rb +0 -474
  28. data/lib/spikard/upload_file.rb +0 -131
  29. data/lib/spikard/websocket.rb +0 -59
  30. data/sig/spikard.rbs +0 -739
  31. data/vendor/crates/spikard-bindings-shared/Cargo.toml +0 -75
  32. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +0 -132
  33. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +0 -905
  34. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +0 -210
  35. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +0 -252
  36. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +0 -404
  37. data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +0 -199
  38. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +0 -252
  39. data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +0 -829
  40. data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +0 -587
  41. data/vendor/crates/spikard-bindings-shared/src/lib.rs +0 -33
  42. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +0 -298
  43. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +0 -594
  44. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +0 -743
  45. data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +0 -944
  46. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +0 -260
  47. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +0 -369
  48. data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +0 -192
  49. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +0 -383
  50. data/vendor/crates/spikard-bindings-shared/tests/full_coverage.rs +0 -459
  51. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +0 -280
  52. data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +0 -669
  53. data/vendor/crates/spikard-core/Cargo.toml +0 -55
  54. data/vendor/crates/spikard-core/src/bindings/mod.rs +0 -3
  55. data/vendor/crates/spikard-core/src/bindings/response.rs +0 -130
  56. data/vendor/crates/spikard-core/src/debug.rs +0 -127
  57. data/vendor/crates/spikard-core/src/di/container.rs +0 -711
  58. data/vendor/crates/spikard-core/src/di/dependency.rs +0 -273
  59. data/vendor/crates/spikard-core/src/di/error.rs +0 -118
  60. data/vendor/crates/spikard-core/src/di/factory.rs +0 -548
  61. data/vendor/crates/spikard-core/src/di/graph.rs +0 -507
  62. data/vendor/crates/spikard-core/src/di/mod.rs +0 -192
  63. data/vendor/crates/spikard-core/src/di/resolved.rs +0 -428
  64. data/vendor/crates/spikard-core/src/di/value.rs +0 -282
  65. data/vendor/crates/spikard-core/src/errors.rs +0 -72
  66. data/vendor/crates/spikard-core/src/http.rs +0 -492
  67. data/vendor/crates/spikard-core/src/lib.rs +0 -29
  68. data/vendor/crates/spikard-core/src/lifecycle.rs +0 -1273
  69. data/vendor/crates/spikard-core/src/metadata.rs +0 -378
  70. data/vendor/crates/spikard-core/src/parameters.rs +0 -2546
  71. data/vendor/crates/spikard-core/src/problem.rs +0 -358
  72. data/vendor/crates/spikard-core/src/request_data.rs +0 -1146
  73. data/vendor/crates/spikard-core/src/router.rs +0 -530
  74. data/vendor/crates/spikard-core/src/schema_registry.rs +0 -197
  75. data/vendor/crates/spikard-core/src/type_hints.rs +0 -311
  76. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +0 -710
  77. data/vendor/crates/spikard-core/src/validation/mod.rs +0 -470
  78. data/vendor/crates/spikard-core/tests/bindings_response_tests.rs +0 -136
  79. data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +0 -37
  80. data/vendor/crates/spikard-core/tests/error_mapper.rs +0 -761
  81. data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +0 -106
  82. data/vendor/crates/spikard-core/tests/parameters_full.rs +0 -701
  83. data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +0 -301
  84. data/vendor/crates/spikard-core/tests/request_data_roundtrip.rs +0 -67
  85. data/vendor/crates/spikard-core/tests/validation_coverage.rs +0 -250
  86. data/vendor/crates/spikard-core/tests/validation_error_paths.rs +0 -45
  87. data/vendor/crates/spikard-http/Cargo.toml +0 -82
  88. data/vendor/crates/spikard-http/examples/sse-notifications.rs +0 -148
  89. data/vendor/crates/spikard-http/examples/websocket-chat.rs +0 -92
  90. data/vendor/crates/spikard-http/src/auth.rs +0 -301
  91. data/vendor/crates/spikard-http/src/background.rs +0 -1859
  92. data/vendor/crates/spikard-http/src/bindings/mod.rs +0 -3
  93. data/vendor/crates/spikard-http/src/bindings/response.rs +0 -1
  94. data/vendor/crates/spikard-http/src/body_metadata.rs +0 -8
  95. data/vendor/crates/spikard-http/src/cors.rs +0 -1026
  96. data/vendor/crates/spikard-http/src/debug.rs +0 -128
  97. data/vendor/crates/spikard-http/src/di_handler.rs +0 -1672
  98. data/vendor/crates/spikard-http/src/grpc/framing.rs +0 -653
  99. data/vendor/crates/spikard-http/src/grpc/handler.rs +0 -1211
  100. data/vendor/crates/spikard-http/src/grpc/mod.rs +0 -556
  101. data/vendor/crates/spikard-http/src/grpc/service.rs +0 -706
  102. data/vendor/crates/spikard-http/src/grpc/streaming.rs +0 -319
  103. data/vendor/crates/spikard-http/src/handler_response.rs +0 -901
  104. data/vendor/crates/spikard-http/src/handler_trait.rs +0 -1015
  105. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +0 -290
  106. data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +0 -502
  107. data/vendor/crates/spikard-http/src/jsonrpc/method_registry.rs +0 -648
  108. data/vendor/crates/spikard-http/src/jsonrpc/mod.rs +0 -60
  109. data/vendor/crates/spikard-http/src/jsonrpc/openrpc.rs +0 -325
  110. data/vendor/crates/spikard-http/src/jsonrpc/protocol.rs +0 -1207
  111. data/vendor/crates/spikard-http/src/jsonrpc/router.rs +0 -2262
  112. data/vendor/crates/spikard-http/src/lib.rs +0 -566
  113. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +0 -230
  114. data/vendor/crates/spikard-http/src/lifecycle.rs +0 -1193
  115. data/vendor/crates/spikard-http/src/middleware/mod.rs +0 -560
  116. data/vendor/crates/spikard-http/src/middleware/multipart.rs +0 -912
  117. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +0 -513
  118. data/vendor/crates/spikard-http/src/middleware/validation.rs +0 -768
  119. data/vendor/crates/spikard-http/src/openapi/mod.rs +0 -309
  120. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +0 -535
  121. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +0 -1363
  122. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +0 -667
  123. data/vendor/crates/spikard-http/src/query_parser.rs +0 -793
  124. data/vendor/crates/spikard-http/src/response.rs +0 -720
  125. data/vendor/crates/spikard-http/src/server/fast_router.rs +0 -186
  126. data/vendor/crates/spikard-http/src/server/grpc_routing.rs +0 -1243
  127. data/vendor/crates/spikard-http/src/server/handler.rs +0 -1661
  128. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +0 -253
  129. data/vendor/crates/spikard-http/src/server/mod.rs +0 -1717
  130. data/vendor/crates/spikard-http/src/server/request_extraction.rs +0 -871
  131. data/vendor/crates/spikard-http/src/server/routing_factory.rs +0 -618
  132. data/vendor/crates/spikard-http/src/sse.rs +0 -1409
  133. data/vendor/crates/spikard-http/src/testing/form.rs +0 -52
  134. data/vendor/crates/spikard-http/src/testing/multipart.rs +0 -64
  135. data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -825
  136. data/vendor/crates/spikard-http/src/testing.rs +0 -617
  137. data/vendor/crates/spikard-http/src/websocket.rs +0 -1477
  138. data/vendor/crates/spikard-http/tests/auth_integration.rs +0 -645
  139. data/vendor/crates/spikard-http/tests/background_behavior.rs +0 -832
  140. data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +0 -1012
  141. data/vendor/crates/spikard-http/tests/common/handlers.rs +0 -309
  142. data/vendor/crates/spikard-http/tests/common/mod.rs +0 -33
  143. data/vendor/crates/spikard-http/tests/common/test_builders.rs +0 -628
  144. data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +0 -162
  145. data/vendor/crates/spikard-http/tests/di_integration.rs +0 -192
  146. data/vendor/crates/spikard-http/tests/doc_snippets.rs +0 -5
  147. data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +0 -430
  148. data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +0 -738
  149. data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +0 -652
  150. data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +0 -334
  151. data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +0 -532
  152. data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +0 -495
  153. data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +0 -975
  154. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +0 -1093
  155. data/vendor/crates/spikard-http/tests/middleware_stack_integration.rs +0 -389
  156. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +0 -656
  157. data/vendor/crates/spikard-http/tests/request_extraction_full.rs +0 -513
  158. data/vendor/crates/spikard-http/tests/server_auth_middleware_behavior.rs +0 -328
  159. data/vendor/crates/spikard-http/tests/server_config_builder.rs +0 -335
  160. data/vendor/crates/spikard-http/tests/server_configured_router_behavior.rs +0 -374
  161. data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +0 -83
  162. data/vendor/crates/spikard-http/tests/server_handler_wrappers.rs +0 -464
  163. data/vendor/crates/spikard-http/tests/server_method_router_additional_behavior.rs +0 -286
  164. data/vendor/crates/spikard-http/tests/server_method_router_coverage.rs +0 -118
  165. data/vendor/crates/spikard-http/tests/server_middleware_behavior.rs +0 -99
  166. data/vendor/crates/spikard-http/tests/server_middleware_branches.rs +0 -204
  167. data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +0 -427
  168. data/vendor/crates/spikard-http/tests/server_router_behavior.rs +0 -121
  169. data/vendor/crates/spikard-http/tests/sse_behavior.rs +0 -620
  170. data/vendor/crates/spikard-http/tests/sse_full_behavior.rs +0 -584
  171. data/vendor/crates/spikard-http/tests/sse_handler_behavior.rs +0 -130
  172. data/vendor/crates/spikard-http/tests/test_client_requests.rs +0 -167
  173. data/vendor/crates/spikard-http/tests/testing_helpers.rs +0 -87
  174. data/vendor/crates/spikard-http/tests/testing_module_coverage.rs +0 -155
  175. data/vendor/crates/spikard-http/tests/urlencoded_content_type.rs +0 -82
  176. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +0 -663
  177. data/vendor/crates/spikard-http/tests/websocket_full_behavior.rs +0 -440
  178. data/vendor/crates/spikard-http/tests/websocket_integration.rs +0 -150
  179. data/vendor/crates/spikard-rb/Cargo.toml +0 -63
  180. data/vendor/crates/spikard-rb/build.rs +0 -200
  181. data/vendor/crates/spikard-rb/src/background.rs +0 -63
  182. data/vendor/crates/spikard-rb/src/config/mod.rs +0 -5
  183. data/vendor/crates/spikard-rb/src/config/server_config.rs +0 -401
  184. data/vendor/crates/spikard-rb/src/conversion.rs +0 -688
  185. data/vendor/crates/spikard-rb/src/di/builder.rs +0 -100
  186. data/vendor/crates/spikard-rb/src/di/mod.rs +0 -410
  187. data/vendor/crates/spikard-rb/src/grpc/handler.rs +0 -875
  188. data/vendor/crates/spikard-rb/src/grpc/mod.rs +0 -13
  189. data/vendor/crates/spikard-rb/src/gvl.rs +0 -80
  190. data/vendor/crates/spikard-rb/src/handler.rs +0 -699
  191. data/vendor/crates/spikard-rb/src/integration/mod.rs +0 -3
  192. data/vendor/crates/spikard-rb/src/lib.rs +0 -2268
  193. data/vendor/crates/spikard-rb/src/lifecycle.rs +0 -334
  194. data/vendor/crates/spikard-rb/src/metadata/mod.rs +0 -5
  195. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +0 -507
  196. data/vendor/crates/spikard-rb/src/request.rs +0 -439
  197. data/vendor/crates/spikard-rb/src/runtime/mod.rs +0 -5
  198. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +0 -368
  199. data/vendor/crates/spikard-rb/src/server.rs +0 -304
  200. data/vendor/crates/spikard-rb/src/sse.rs +0 -231
  201. data/vendor/crates/spikard-rb/src/testing/client.rs +0 -698
  202. data/vendor/crates/spikard-rb/src/testing/mod.rs +0 -7
  203. data/vendor/crates/spikard-rb/src/testing/sse.rs +0 -108
  204. data/vendor/crates/spikard-rb/src/testing/websocket.rs +0 -573
  205. data/vendor/crates/spikard-rb/src/websocket.rs +0 -521
  206. data/vendor/crates/spikard-rb-macros/Cargo.toml +0 -20
  207. data/vendor/crates/spikard-rb-macros/src/lib.rs +0 -51
@@ -1,428 +0,0 @@
1
- //! Storage for resolved dependencies
2
- //!
3
- //! This module provides the `ResolvedDependencies` type which holds all dependencies
4
- //! resolved for a particular request, with type-safe access and cleanup support.
5
-
6
- use std::any::Any;
7
- use std::collections::HashMap;
8
- use std::future::Future;
9
- use std::pin::Pin;
10
- use std::sync::{Arc, Mutex};
11
-
12
- type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
13
- type CleanupTask = Box<dyn FnOnce() -> BoxFuture<'static, ()> + Send>;
14
-
15
- /// Storage for resolved dependencies with type-safe access
16
- ///
17
- /// This type stores all dependencies that have been resolved for a request,
18
- /// allowing type-safe retrieval and supporting cleanup tasks for generator-pattern
19
- /// dependencies.
20
- ///
21
- /// Uses Arc<Mutex<>> internally for thread-safe shared access.
22
- ///
23
- /// # Examples
24
- ///
25
- /// ```ignore
26
- /// use spikard_core::di::ResolvedDependencies;
27
- /// use std::sync::Arc;
28
- ///
29
- /// # tokio_test::block_on(async {
30
- /// let mut resolved = ResolvedDependencies::new();
31
- ///
32
- /// // Insert a dependency
33
- /// let value = Arc::new(42i32);
34
- /// resolved.insert("answer".to_string(), value);
35
- ///
36
- /// // Retrieve with type safety
37
- /// let retrieved: Option<Arc<i32>> = resolved.get("answer");
38
- /// assert_eq!(retrieved.map(|v| *v), Some(42));
39
- ///
40
- /// // Type mismatch returns None
41
- /// let wrong_type: Option<Arc<String>> = resolved.get("answer");
42
- /// assert!(wrong_type.is_none());
43
- ///
44
- /// // Cleanup
45
- /// resolved.cleanup().await;
46
- /// # });
47
- /// ```
48
- #[derive(Default, Clone)]
49
- pub struct ResolvedDependencies {
50
- /// Map of dependency keys to type-erased values
51
- dependencies: Arc<Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
52
- /// Cleanup tasks to run when dependencies are dropped
53
- cleanup_tasks: Arc<Mutex<Vec<CleanupTask>>>,
54
- }
55
-
56
- impl ResolvedDependencies {
57
- /// Create a new empty resolved dependencies storage
58
- ///
59
- /// # Examples
60
- ///
61
- /// ```ignore
62
- /// use spikard_core::di::ResolvedDependencies;
63
- ///
64
- /// let resolved = ResolvedDependencies::new();
65
- /// ```
66
- #[must_use]
67
- pub fn new() -> Self {
68
- Self {
69
- dependencies: Arc::new(Mutex::new(HashMap::new())),
70
- cleanup_tasks: Arc::new(Mutex::new(Vec::new())),
71
- }
72
- }
73
-
74
- /// Insert a dependency into the storage
75
- ///
76
- /// # Arguments
77
- ///
78
- /// * `key` - The unique key for this dependency
79
- /// * `value` - The dependency value wrapped in Arc<dyn Any + Send + Sync>
80
- ///
81
- /// # Examples
82
- ///
83
- /// ```ignore
84
- /// use spikard_core::di::ResolvedDependencies;
85
- /// use std::sync::Arc;
86
- ///
87
- /// let mut resolved = ResolvedDependencies::new();
88
- /// let config = Arc::new("production".to_string());
89
- /// resolved.insert("config".to_string(), config);
90
- /// ```
91
- ///
92
- /// # Panics
93
- /// Panics if the lock is poisoned.
94
- pub fn insert(&mut self, key: String, value: Arc<dyn Any + Send + Sync>) {
95
- self.dependencies.lock().unwrap().insert(key, value);
96
- }
97
-
98
- /// Get a dependency with type-safe downcasting
99
- ///
100
- /// Returns `Some(Arc<T>)` if the dependency exists and is of type `T`,
101
- /// or `None` if it doesn't exist or has a different type.
102
- ///
103
- /// # Type Parameters
104
- ///
105
- /// * `T` - The expected type of the dependency (must be Send + Sync)
106
- ///
107
- /// # Arguments
108
- ///
109
- /// * `key` - The key of the dependency to retrieve
110
- ///
111
- /// # Examples
112
- ///
113
- /// ```ignore
114
- /// use spikard_core::di::ResolvedDependencies;
115
- /// use std::sync::Arc;
116
- ///
117
- /// let mut resolved = ResolvedDependencies::new();
118
- /// resolved.insert("count".to_string(), Arc::new(100i32));
119
- ///
120
- /// // Correct type
121
- /// let count: Option<Arc<i32>> = resolved.get("count");
122
- /// assert_eq!(count.map(|v| *v), Some(100));
123
- ///
124
- /// // Wrong type
125
- /// let wrong: Option<Arc<String>> = resolved.get("count");
126
- /// assert!(wrong.is_none());
127
- ///
128
- /// // Missing key
129
- /// let missing: Option<Arc<i32>> = resolved.get("missing");
130
- /// assert!(missing.is_none());
131
- /// ```
132
- ///
133
- /// # Panics
134
- /// Panics if the lock is poisoned.
135
- #[must_use]
136
- pub fn get<T: Send + Sync + 'static>(&self, key: &str) -> Option<Arc<T>> {
137
- self.dependencies
138
- .lock()
139
- .unwrap()
140
- .get(key)
141
- .and_then(|value| Arc::clone(value).downcast::<T>().ok())
142
- }
143
-
144
- /// Get a dependency as Arc<dyn Any> without type checking
145
- ///
146
- /// This is useful when you need to pass dependencies around without
147
- /// knowing their concrete type.
148
- ///
149
- /// # Arguments
150
- ///
151
- /// * `key` - The key of the dependency to retrieve
152
- ///
153
- /// # Examples
154
- ///
155
- /// ```ignore
156
- /// use spikard_core::di::ResolvedDependencies;
157
- /// use std::sync::Arc;
158
- ///
159
- /// let mut resolved = ResolvedDependencies::new();
160
- /// resolved.insert("data".to_string(), Arc::new(vec![1, 2, 3]));
161
- ///
162
- /// let any_ref = resolved.get_arc("data");
163
- /// assert!(any_ref.is_some());
164
- /// ```
165
- ///
166
- /// # Panics
167
- /// Panics if the lock is poisoned.
168
- #[must_use]
169
- pub fn get_arc(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
170
- self.dependencies.lock().unwrap().get(key).cloned()
171
- }
172
-
173
- /// Check if a dependency exists
174
- ///
175
- /// # Arguments
176
- ///
177
- /// * `key` - The key to check
178
- ///
179
- /// # Examples
180
- ///
181
- /// ```ignore
182
- /// use spikard_core::di::ResolvedDependencies;
183
- /// use std::sync::Arc;
184
- ///
185
- /// let mut resolved = ResolvedDependencies::new();
186
- /// resolved.insert("exists".to_string(), Arc::new(true));
187
- ///
188
- /// assert!(resolved.contains("exists"));
189
- /// assert!(!resolved.contains("missing"));
190
- /// ```
191
- ///
192
- /// # Panics
193
- /// Panics if the lock is poisoned.
194
- #[must_use]
195
- pub fn contains(&self, key: &str) -> bool {
196
- self.dependencies.lock().unwrap().contains_key(key)
197
- }
198
-
199
- /// Get all dependency keys
200
- ///
201
- /// Returns a vector of all keys currently stored in this resolved dependencies.
202
- /// Useful for iterating over all dependencies when you need to extract them.
203
- ///
204
- /// # Examples
205
- ///
206
- /// ```ignore
207
- /// use spikard_core::di::ResolvedDependencies;
208
- /// use std::sync::Arc;
209
- ///
210
- /// let mut resolved = ResolvedDependencies::new();
211
- /// resolved.insert("config".to_string(), Arc::new("prod".to_string()));
212
- /// resolved.insert("db".to_string(), Arc::new(42i32));
213
- ///
214
- /// let keys = resolved.keys();
215
- /// assert_eq!(keys.len(), 2);
216
- /// assert!(keys.contains(&"config".to_string()));
217
- /// assert!(keys.contains(&"db".to_string()));
218
- /// ```
219
- ///
220
- /// # Panics
221
- /// Panics if the lock is poisoned.
222
- #[must_use]
223
- pub fn keys(&self) -> Vec<String> {
224
- self.dependencies.lock().unwrap().keys().cloned().collect()
225
- }
226
-
227
- /// Add a cleanup task to be run when dependencies are cleaned up
228
- ///
229
- /// Cleanup tasks are useful for generator-pattern dependencies that need
230
- /// to perform cleanup (e.g., closing database connections, releasing locks).
231
- ///
232
- /// # Arguments
233
- ///
234
- /// * `task` - A function that returns a future performing cleanup
235
- ///
236
- /// # Examples
237
- ///
238
- /// ```ignore
239
- /// use spikard_core::di::ResolvedDependencies;
240
- ///
241
- /// # tokio_test::block_on(async {
242
- /// let mut resolved = ResolvedDependencies::new();
243
- ///
244
- /// resolved.add_cleanup_task(Box::new(|| {
245
- /// Box::pin(async {
246
- /// println!("Cleaning up resources");
247
- /// })
248
- /// }));
249
- ///
250
- /// resolved.cleanup().await;
251
- /// # });
252
- /// ```
253
- ///
254
- /// # Panics
255
- /// Panics if the lock is poisoned.
256
- pub fn add_cleanup_task(&self, task: CleanupTask) {
257
- self.cleanup_tasks.lock().unwrap().push(task);
258
- }
259
-
260
- /// Run all cleanup tasks in reverse order
261
- ///
262
- /// Cleanup tasks are executed in LIFO order (last added, first executed)
263
- /// to properly handle nested resource dependencies.
264
- ///
265
- /// This consumes self to ensure cleanup is only run once.
266
- ///
267
- /// # Examples
268
- ///
269
- /// ```ignore
270
- /// use spikard_core::di::ResolvedDependencies;
271
- /// use std::sync::{Arc, Mutex};
272
- ///
273
- /// # tokio_test::block_on(async {
274
- /// let order = Arc::new(Mutex::new(Vec::new()));
275
- ///
276
- /// let mut resolved = ResolvedDependencies::new();
277
- ///
278
- /// let order1 = order.clone();
279
- /// resolved.add_cleanup_task(Box::new(move || {
280
- /// Box::pin(async move {
281
- /// order1.lock().unwrap().push(1);
282
- /// })
283
- /// }));
284
- ///
285
- /// let order2 = order.clone();
286
- /// resolved.add_cleanup_task(Box::new(move || {
287
- /// Box::pin(async move {
288
- /// order2.lock().unwrap().push(2);
289
- /// })
290
- /// }));
291
- ///
292
- /// resolved.cleanup().await;
293
- ///
294
- /// // Tasks run in reverse order (LIFO)
295
- /// assert_eq!(*order.lock().unwrap(), vec![2, 1]);
296
- /// # });
297
- /// ```
298
- ///
299
- /// # Panics
300
- /// Panics if the lock is poisoned.
301
- pub async fn cleanup(self) {
302
- let tasks = {
303
- let mut cleanup_tasks = self.cleanup_tasks.lock().unwrap();
304
- std::mem::take(&mut *cleanup_tasks)
305
- };
306
-
307
- for task in tasks.into_iter().rev() {
308
- task().await;
309
- }
310
- }
311
- }
312
-
313
- impl std::fmt::Debug for ResolvedDependencies {
314
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315
- let deps = self.dependencies.lock().unwrap();
316
- let tasks = self.cleanup_tasks.lock().unwrap();
317
- f.debug_struct("ResolvedDependencies")
318
- .field("dependencies", &deps.keys())
319
- .field("cleanup_tasks_count", &tasks.len())
320
- .finish()
321
- }
322
- }
323
-
324
- #[cfg(test)]
325
- mod tests {
326
- use super::*;
327
-
328
- #[test]
329
- fn test_new() {
330
- let resolved = ResolvedDependencies::new();
331
- assert!(!resolved.contains("anything"));
332
- }
333
-
334
- #[test]
335
- fn test_insert_and_get() {
336
- let mut resolved = ResolvedDependencies::new();
337
- let value = Arc::new(42i32);
338
- resolved.insert("answer".to_string(), value);
339
-
340
- let retrieved: Option<Arc<i32>> = resolved.get("answer");
341
- assert_eq!(retrieved.map(|v| *v), Some(42));
342
- }
343
-
344
- #[test]
345
- fn test_get_type_mismatch() {
346
- let mut resolved = ResolvedDependencies::new();
347
- resolved.insert("number".to_string(), Arc::new(42i32));
348
-
349
- let wrong: Option<Arc<String>> = resolved.get("number");
350
- assert!(wrong.is_none());
351
- }
352
-
353
- #[test]
354
- fn test_get_missing() {
355
- let resolved = ResolvedDependencies::new();
356
- let missing: Option<Arc<i32>> = resolved.get("missing");
357
- assert!(missing.is_none());
358
- }
359
-
360
- #[test]
361
- fn test_get_arc() {
362
- let mut resolved = ResolvedDependencies::new();
363
- resolved.insert("data".to_string(), Arc::new(vec![1, 2, 3]));
364
-
365
- let any_ref = resolved.get_arc("data");
366
- assert!(any_ref.is_some());
367
-
368
- let vec_ref = any_ref.unwrap().downcast::<Vec<i32>>().ok();
369
- assert!(vec_ref.is_some());
370
- }
371
-
372
- #[test]
373
- fn test_contains() {
374
- let mut resolved = ResolvedDependencies::new();
375
- resolved.insert("exists".to_string(), Arc::new(true));
376
-
377
- assert!(resolved.contains("exists"));
378
- assert!(!resolved.contains("missing"));
379
- }
380
-
381
- #[tokio::test]
382
- async fn test_cleanup_order() {
383
- let order = Arc::new(Mutex::new(Vec::new()));
384
-
385
- let resolved = ResolvedDependencies::new();
386
-
387
- let order1 = order.clone();
388
- resolved.add_cleanup_task(Box::new(move || {
389
- Box::pin(async move {
390
- order1.lock().unwrap().push(1);
391
- })
392
- }));
393
-
394
- let order2 = order.clone();
395
- resolved.add_cleanup_task(Box::new(move || {
396
- Box::pin(async move {
397
- order2.lock().unwrap().push(2);
398
- })
399
- }));
400
-
401
- let order3 = order.clone();
402
- resolved.add_cleanup_task(Box::new(move || {
403
- Box::pin(async move {
404
- order3.lock().unwrap().push(3);
405
- })
406
- }));
407
-
408
- resolved.cleanup().await;
409
-
410
- assert_eq!(*order.lock().unwrap(), vec![3, 2, 1]);
411
- }
412
-
413
- #[tokio::test]
414
- async fn test_cleanup_empty() {
415
- let resolved = ResolvedDependencies::new();
416
- resolved.cleanup().await;
417
- }
418
-
419
- #[test]
420
- fn test_clone() {
421
- let mut resolved1 = ResolvedDependencies::new();
422
- resolved1.insert("key".to_string(), Arc::new(42i32));
423
-
424
- let resolved2 = resolved1.clone();
425
- let value: Option<Arc<i32>> = resolved2.get("key");
426
- assert_eq!(value.map(|v| *v), Some(42));
427
- }
428
- }
@@ -1,282 +0,0 @@
1
- //! Value dependency implementation
2
- //!
3
- //! This module provides `ValueDependency<T>`, a simple dependency that wraps
4
- //! a static value and returns it whenever resolved.
5
-
6
- use super::dependency::Dependency;
7
- use super::error::DependencyError;
8
- use super::resolved::ResolvedDependencies;
9
- use crate::request_data::RequestData;
10
- use http::Request;
11
- use std::any::Any;
12
- use std::future::Future;
13
- use std::marker::PhantomData;
14
- use std::pin::Pin;
15
- use std::sync::Arc;
16
-
17
- /// A dependency that wraps a static value
18
- ///
19
- /// This is the simplest form of dependency - it just returns a pre-configured
20
- /// value whenever resolved. Useful for configuration values, constants, or
21
- /// pre-built objects.
22
- ///
23
- /// # Type Parameters
24
- ///
25
- /// * `T` - The type of value to provide. Must be `Clone + Send + Sync + 'static`.
26
- ///
27
- /// # Examples
28
- ///
29
- /// ```ignore
30
- /// use spikard_core::di::{Dependency, ValueDependency};
31
- /// use http::Request;
32
- /// use crate::request_data::RequestData;
33
- /// use std::collections::HashMap;
34
- /// use std::sync::Arc;
35
- ///
36
- /// # tokio_test::block_on(async {
37
- /// // Create a value dependency with a configuration string
38
- /// let config = ValueDependency::new("database_url", "postgresql://localhost/mydb");
39
- ///
40
- /// // Resolve it (returns the same value every time)
41
- /// let request = Request::builder().body(()).unwrap();
42
- /// let request_data = RequestData {
43
- /// path_params: Arc::new(HashMap::new()),
44
- /// query_params: serde_json::Value::Null,
45
- /// validated_params: None,
46
- /// raw_query_params: Arc::new(HashMap::new()),
47
- /// body: serde_json::Value::Null,
48
- /// raw_body: None,
49
- /// headers: Arc::new(HashMap::new()),
50
- /// cookies: Arc::new(HashMap::new()),
51
- /// method: "GET".to_string(),
52
- /// path: "/".to_string(),
53
- /// };
54
- /// let resolved = spikard_core::di::ResolvedDependencies::new();
55
- ///
56
- /// let result = config.resolve(&request, &request_data, &resolved).await.unwrap();
57
- /// let value: Arc<String> = result.downcast().unwrap();
58
- /// assert_eq!(*value, "postgresql://localhost/mydb");
59
- /// # });
60
- /// ```
61
- pub struct ValueDependency<T: Clone + Send + Sync + 'static> {
62
- key: String,
63
- value: Arc<T>,
64
- _phantom: PhantomData<T>,
65
- }
66
-
67
- impl<T: Clone + Send + Sync + 'static> ValueDependency<T> {
68
- /// Create a new value dependency
69
- ///
70
- /// # Arguments
71
- ///
72
- /// * `key` - The unique key for this dependency
73
- /// * `value` - The value to provide when resolved
74
- ///
75
- /// # Examples
76
- ///
77
- /// ```ignore
78
- /// use spikard_core::di::ValueDependency;
79
- ///
80
- /// // Simple value
81
- /// let port = ValueDependency::new("port", 8080u16);
82
- ///
83
- /// // Complex value
84
- /// #[derive(Clone)]
85
- /// struct Config {
86
- /// debug: bool,
87
- /// timeout: u64,
88
- /// }
89
- ///
90
- /// let config = ValueDependency::new("config", Config {
91
- /// debug: true,
92
- /// timeout: 30,
93
- /// });
94
- /// ```
95
- pub fn new(key: impl Into<String>, value: T) -> Self {
96
- Self {
97
- key: key.into(),
98
- value: Arc::new(value),
99
- _phantom: PhantomData,
100
- }
101
- }
102
- }
103
-
104
- impl<T: Clone + Send + Sync + 'static> Dependency for ValueDependency<T> {
105
- fn resolve(
106
- &self,
107
- _request: &Request<()>,
108
- _request_data: &RequestData,
109
- _resolved: &ResolvedDependencies,
110
- ) -> Pin<Box<dyn Future<Output = Result<Arc<dyn Any + Send + Sync>, DependencyError>> + Send>> {
111
- let value = Arc::clone(&self.value);
112
- Box::pin(async move { Ok(value as Arc<dyn Any + Send + Sync>) })
113
- }
114
-
115
- fn key(&self) -> &str {
116
- &self.key
117
- }
118
-
119
- fn depends_on(&self) -> Vec<String> {
120
- vec![]
121
- }
122
-
123
- fn cacheable(&self) -> bool {
124
- true
125
- }
126
-
127
- fn singleton(&self) -> bool {
128
- true
129
- }
130
- }
131
-
132
- impl<T: Clone + Send + Sync + 'static> std::fmt::Debug for ValueDependency<T> {
133
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134
- f.debug_struct("ValueDependency")
135
- .field("key", &self.key)
136
- .field("value_type", &std::any::type_name::<T>())
137
- .field("value", &"<T>")
138
- .finish()
139
- }
140
- }
141
-
142
- #[cfg(test)]
143
- mod tests {
144
- use super::*;
145
- use std::collections::HashMap;
146
-
147
- fn make_request_data() -> RequestData {
148
- RequestData {
149
- path_params: Arc::new(HashMap::new()),
150
- query_params: serde_json::Value::Null,
151
- validated_params: None,
152
- raw_query_params: Arc::new(HashMap::new()),
153
- body: serde_json::Value::Null,
154
- raw_body: None,
155
- headers: Arc::new(HashMap::new()),
156
- cookies: Arc::new(HashMap::new()),
157
- method: "GET".to_string(),
158
- path: "/".to_string(),
159
- #[cfg(feature = "di")]
160
- dependencies: None,
161
- }
162
- }
163
-
164
- #[test]
165
- fn test_new() {
166
- let dep = ValueDependency::new("test", 42i32);
167
- assert_eq!(dep.key(), "test");
168
- }
169
-
170
- #[test]
171
- fn test_key() {
172
- let dep = ValueDependency::new("my_key", "value");
173
- assert_eq!(dep.key(), "my_key");
174
- }
175
-
176
- #[test]
177
- fn test_depends_on() {
178
- let dep = ValueDependency::new("test", 42i32);
179
- assert_eq!(dep.depends_on(), Vec::<String>::new());
180
- }
181
-
182
- #[test]
183
- fn test_cacheable() {
184
- let dep = ValueDependency::new("test", 42i32);
185
- assert!(dep.cacheable());
186
- }
187
-
188
- #[test]
189
- fn test_singleton() {
190
- let dep = ValueDependency::new("test", 42i32);
191
- assert!(dep.singleton());
192
- }
193
-
194
- #[tokio::test]
195
- async fn test_resolve_simple() {
196
- let dep = ValueDependency::new("answer", 42i32);
197
- let request = Request::builder().body(()).unwrap();
198
- let request_data = make_request_data();
199
- let resolved = ResolvedDependencies::new();
200
-
201
- let result = dep.resolve(&request, &request_data, &resolved).await;
202
- assert!(result.is_ok());
203
-
204
- let value: Arc<i32> = result.unwrap().downcast().unwrap();
205
- assert_eq!(*value, 42);
206
- }
207
-
208
- #[tokio::test]
209
- async fn test_resolve_string() {
210
- let dep = ValueDependency::new("message", "Hello, World!".to_string());
211
- let request = Request::builder().body(()).unwrap();
212
- let request_data = make_request_data();
213
- let resolved = ResolvedDependencies::new();
214
-
215
- let result = dep.resolve(&request, &request_data, &resolved).await;
216
- assert!(result.is_ok());
217
-
218
- let value: Arc<String> = result.unwrap().downcast().unwrap();
219
- assert_eq!(*value, "Hello, World!");
220
- }
221
-
222
- #[tokio::test]
223
- async fn test_resolve_concurrent() {
224
- let dep = Arc::new(ValueDependency::new("shared", 100i32));
225
- let request = Request::builder().body(()).unwrap();
226
- let request_data = make_request_data();
227
-
228
- let handles: Vec<_> = (0..10)
229
- .map(|_| {
230
- let dep = Arc::clone(&dep);
231
- let req = request.clone();
232
- let data = request_data.clone();
233
- tokio::spawn(async move {
234
- let resolved = ResolvedDependencies::new();
235
- let result = dep.resolve(&req, &data, &resolved).await.unwrap();
236
- let value: Arc<i32> = result.downcast().unwrap();
237
- *value
238
- })
239
- })
240
- .collect();
241
-
242
- for handle in handles {
243
- let value = handle.await.unwrap();
244
- assert_eq!(value, 100);
245
- }
246
- }
247
-
248
- #[derive(Clone, Debug, PartialEq)]
249
- struct ComplexValue {
250
- name: String,
251
- count: i32,
252
- tags: Vec<String>,
253
- }
254
-
255
- #[tokio::test]
256
- async fn test_resolve_complex_type() {
257
- let complex = ComplexValue {
258
- name: "test".to_string(),
259
- count: 42,
260
- tags: vec!["tag1".to_string(), "tag2".to_string()],
261
- };
262
-
263
- let dep = ValueDependency::new("complex", complex.clone());
264
- let request = Request::builder().body(()).unwrap();
265
- let request_data = make_request_data();
266
- let resolved = ResolvedDependencies::new();
267
-
268
- let result = dep.resolve(&request, &request_data, &resolved).await;
269
- assert!(result.is_ok());
270
-
271
- let value: Arc<ComplexValue> = result.unwrap().downcast().unwrap();
272
- assert_eq!(*value, complex);
273
- }
274
-
275
- #[test]
276
- fn test_debug() {
277
- let dep = ValueDependency::new("test", 42i32);
278
- let debug_str = format!("{dep:?}");
279
- assert!(debug_str.contains("ValueDependency"));
280
- assert!(debug_str.contains("test"));
281
- }
282
- }