spikard 0.3.5 → 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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +674 -659
  4. data/ext/spikard_rb/Cargo.toml +17 -17
  5. data/ext/spikard_rb/extconf.rb +10 -10
  6. data/ext/spikard_rb/src/lib.rs +6 -6
  7. data/lib/spikard/app.rb +405 -386
  8. data/lib/spikard/background.rb +27 -27
  9. data/lib/spikard/config.rb +396 -396
  10. data/lib/spikard/converters.rb +13 -13
  11. data/lib/spikard/handler_wrapper.rb +113 -113
  12. data/lib/spikard/provide.rb +214 -214
  13. data/lib/spikard/response.rb +173 -173
  14. data/lib/spikard/schema.rb +243 -243
  15. data/lib/spikard/sse.rb +111 -111
  16. data/lib/spikard/streaming_response.rb +44 -44
  17. data/lib/spikard/testing.rb +256 -221
  18. data/lib/spikard/upload_file.rb +131 -131
  19. data/lib/spikard/version.rb +5 -5
  20. data/lib/spikard/websocket.rb +59 -59
  21. data/lib/spikard.rb +43 -43
  22. data/sig/spikard.rbs +366 -360
  23. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
  24. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -0
  25. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -0
  26. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
  27. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
  28. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -0
  29. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -0
  30. data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -0
  31. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -0
  32. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -0
  33. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -0
  34. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
  35. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -0
  36. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -0
  37. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -0
  38. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -0
  39. data/vendor/crates/spikard-core/Cargo.toml +40 -40
  40. data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -3
  41. data/vendor/crates/spikard-core/src/bindings/response.rs +133 -133
  42. data/vendor/crates/spikard-core/src/debug.rs +127 -63
  43. data/vendor/crates/spikard-core/src/di/container.rs +702 -726
  44. data/vendor/crates/spikard-core/src/di/dependency.rs +273 -273
  45. data/vendor/crates/spikard-core/src/di/error.rs +118 -118
  46. data/vendor/crates/spikard-core/src/di/factory.rs +534 -538
  47. data/vendor/crates/spikard-core/src/di/graph.rs +506 -545
  48. data/vendor/crates/spikard-core/src/di/mod.rs +192 -192
  49. data/vendor/crates/spikard-core/src/di/resolved.rs +405 -411
  50. data/vendor/crates/spikard-core/src/di/value.rs +281 -283
  51. data/vendor/crates/spikard-core/src/errors.rs +69 -39
  52. data/vendor/crates/spikard-core/src/http.rs +415 -153
  53. data/vendor/crates/spikard-core/src/lib.rs +29 -29
  54. data/vendor/crates/spikard-core/src/lifecycle.rs +1186 -422
  55. data/vendor/crates/spikard-core/src/metadata.rs +389 -0
  56. data/vendor/crates/spikard-core/src/parameters.rs +2525 -722
  57. data/vendor/crates/spikard-core/src/problem.rs +344 -310
  58. data/vendor/crates/spikard-core/src/request_data.rs +1154 -189
  59. data/vendor/crates/spikard-core/src/router.rs +510 -249
  60. data/vendor/crates/spikard-core/src/schema_registry.rs +183 -183
  61. data/vendor/crates/spikard-core/src/type_hints.rs +304 -304
  62. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +688 -0
  63. data/vendor/crates/spikard-core/src/{validation.rs → validation/mod.rs} +457 -699
  64. data/vendor/crates/spikard-http/Cargo.toml +64 -68
  65. data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -0
  66. data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -0
  67. data/vendor/crates/spikard-http/src/auth.rs +296 -247
  68. data/vendor/crates/spikard-http/src/background.rs +1860 -249
  69. data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -3
  70. data/vendor/crates/spikard-http/src/bindings/response.rs +1 -1
  71. data/vendor/crates/spikard-http/src/body_metadata.rs +8 -8
  72. data/vendor/crates/spikard-http/src/cors.rs +1005 -490
  73. data/vendor/crates/spikard-http/src/debug.rs +128 -63
  74. data/vendor/crates/spikard-http/src/di_handler.rs +1668 -423
  75. data/vendor/crates/spikard-http/src/handler_response.rs +901 -190
  76. data/vendor/crates/spikard-http/src/handler_trait.rs +830 -228
  77. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +290 -284
  78. data/vendor/crates/spikard-http/src/lib.rs +534 -529
  79. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +230 -149
  80. data/vendor/crates/spikard-http/src/lifecycle.rs +1193 -428
  81. data/vendor/crates/spikard-http/src/middleware/mod.rs +540 -285
  82. data/vendor/crates/spikard-http/src/middleware/multipart.rs +912 -86
  83. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +513 -147
  84. data/vendor/crates/spikard-http/src/middleware/validation.rs +735 -287
  85. data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -309
  86. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +535 -190
  87. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1363 -308
  88. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +665 -195
  89. data/vendor/crates/spikard-http/src/query_parser.rs +793 -369
  90. data/vendor/crates/spikard-http/src/response.rs +720 -399
  91. data/vendor/crates/spikard-http/src/server/handler.rs +1650 -87
  92. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +234 -98
  93. data/vendor/crates/spikard-http/src/server/mod.rs +1502 -805
  94. data/vendor/crates/spikard-http/src/server/request_extraction.rs +770 -119
  95. data/vendor/crates/spikard-http/src/server/routing_factory.rs +599 -0
  96. data/vendor/crates/spikard-http/src/sse.rs +1409 -447
  97. data/vendor/crates/spikard-http/src/testing/form.rs +52 -14
  98. data/vendor/crates/spikard-http/src/testing/multipart.rs +60 -60
  99. data/vendor/crates/spikard-http/src/testing/test_client.rs +283 -285
  100. data/vendor/crates/spikard-http/src/testing.rs +377 -377
  101. data/vendor/crates/spikard-http/src/websocket.rs +1375 -324
  102. data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -0
  103. data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -0
  104. data/vendor/crates/spikard-http/tests/common/mod.rs +26 -0
  105. data/vendor/crates/spikard-http/tests/di_integration.rs +192 -0
  106. data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -0
  107. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -0
  108. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -0
  109. data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -0
  110. data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -0
  111. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -0
  112. data/vendor/crates/spikard-rb/Cargo.toml +48 -42
  113. data/vendor/crates/spikard-rb/build.rs +199 -8
  114. data/vendor/crates/spikard-rb/src/background.rs +63 -63
  115. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
  116. data/vendor/crates/spikard-rb/src/{config.rs → config/server_config.rs} +285 -294
  117. data/vendor/crates/spikard-rb/src/conversion.rs +554 -453
  118. data/vendor/crates/spikard-rb/src/di/builder.rs +100 -0
  119. data/vendor/crates/spikard-rb/src/{di.rs → di/mod.rs} +375 -409
  120. data/vendor/crates/spikard-rb/src/handler.rs +618 -625
  121. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
  122. data/vendor/crates/spikard-rb/src/lib.rs +1810 -2771
  123. data/vendor/crates/spikard-rb/src/lifecycle.rs +275 -274
  124. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
  125. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +447 -0
  126. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
  127. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -0
  128. data/vendor/crates/spikard-rb/src/server.rs +308 -283
  129. data/vendor/crates/spikard-rb/src/sse.rs +231 -231
  130. data/vendor/crates/spikard-rb/src/{test_client.rs → testing/client.rs} +551 -404
  131. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
  132. data/vendor/crates/spikard-rb/src/{test_sse.rs → testing/sse.rs} +143 -143
  133. data/vendor/crates/spikard-rb/src/testing/websocket.rs +635 -0
  134. data/vendor/crates/spikard-rb/src/websocket.rs +374 -233
  135. metadata +46 -13
  136. data/vendor/crates/spikard-http/src/parameters.rs +0 -1
  137. data/vendor/crates/spikard-http/src/problem.rs +0 -1
  138. data/vendor/crates/spikard-http/src/router.rs +0 -1
  139. data/vendor/crates/spikard-http/src/schema_registry.rs +0 -1
  140. data/vendor/crates/spikard-http/src/type_hints.rs +0 -1
  141. data/vendor/crates/spikard-http/src/validation.rs +0 -1
  142. data/vendor/crates/spikard-rb/src/test_websocket.rs +0 -221
@@ -1,192 +1,192 @@
1
- //! Dependency Injection system for Spikard
2
- //!
3
- //! This module provides a comprehensive dependency injection system with:
4
- //!
5
- //! - **Type-safe dependency resolution**: Dependencies are stored as `Arc<dyn Any>` but
6
- //! can be retrieved with type safety using `ResolvedDependencies::get<T>()`
7
- //! - **Async resolution**: All dependencies can perform async operations during resolution
8
- //! - **Batched parallel resolution**: Dependencies with no interdependencies are resolved
9
- //! in parallel using topological sorting
10
- //! - **Multiple caching strategies**:
11
- //! - Singleton: Resolved once globally, cached forever
12
- //! - Per-request cacheable: Resolved once per request
13
- //! - Non-cacheable: Resolved every time
14
- //! - **Cycle detection**: Circular dependencies are detected at registration time
15
- //! - **Cleanup support**: Generator-pattern dependencies can register cleanup tasks
16
- //!
17
- //! # Architecture
18
- //!
19
- //! The DI system is built on several core components:
20
- //!
21
- //! - [`Dependency`] trait: The core abstraction that all dependencies implement
22
- //! - [`DependencyContainer`]: Manages registration and resolution
23
- //! - [`ResolvedDependencies`]: Stores resolved dependencies with type-safe access
24
- //! - [`DependencyGraph`]: Handles topological sorting and cycle detection
25
- //! - [`ValueDependency<T>`]: Simple static value dependencies
26
- //! - [`FactoryDependency`]: Dynamic factory-based dependencies
27
- //!
28
- //! # Examples
29
- //!
30
- //! ## Basic Usage
31
- //!
32
- //! ```ignore
33
- //! use spikard_core::di::{DependencyContainer, ValueDependency, FactoryDependency};
34
- //! use std::sync::Arc;
35
- //!
36
- //! # tokio_test::block_on(async {
37
- //! let mut container = DependencyContainer::new();
38
- //!
39
- //! // Register a simple value dependency
40
- //! let config = ValueDependency::new("database_url", "postgresql://localhost/mydb");
41
- //! container.register("database_url".to_string(), Arc::new(config)).unwrap();
42
- //!
43
- //! // Register a factory dependency that depends on the config
44
- //! let pool = FactoryDependency::builder("db_pool")
45
- //! .depends_on(vec!["database_url".to_string()])
46
- //! .factory(|_req, _data, resolved| {
47
- //! Box::pin(async move {
48
- //! let url: Arc<String> = resolved.get("database_url").unwrap();
49
- //! let pool = format!("Pool connected to {}", *url);
50
- //! Ok(Arc::new(pool) as Arc<dyn std::any::Any + Send + Sync>)
51
- //! })
52
- //! })
53
- //! .singleton(true) // Share across all requests
54
- //! .build();
55
- //! container.register("db_pool".to_string(), Arc::new(pool)).unwrap();
56
- //!
57
- //! // Resolve for a handler
58
- //! use http::Request;
59
- //! use crate::request_data::RequestData;
60
- //! use std::collections::HashMap;
61
- //!
62
- //! let request = Request::builder().body(()).unwrap();
63
- //! let request_data = RequestData {
64
- //! path_params: Arc::new(HashMap::new()),
65
- //! query_params: serde_json::Value::Null,
66
- //! raw_query_params: Arc::new(HashMap::new()),
67
- //! body: serde_json::Value::Null,
68
- //! raw_body: None,
69
- //! headers: Arc::new(HashMap::new()),
70
- //! cookies: Arc::new(HashMap::new()),
71
- //! method: "GET".to_string(),
72
- //! path: "/".to_string(),
73
- //! };
74
- //!
75
- //! let resolved = container
76
- //! .resolve_for_handler(&["db_pool".to_string()], &request, &request_data)
77
- //! .await
78
- //! .unwrap();
79
- //!
80
- //! let pool: Option<Arc<String>> = resolved.get("db_pool");
81
- //! assert!(pool.is_some());
82
- //! # });
83
- //! ```
84
- //!
85
- //! ## Request-Scoped Dependencies
86
- //!
87
- //! ```ignore
88
- //! use spikard_core::di::{DependencyContainer, FactoryDependency};
89
- //! use std::sync::Arc;
90
- //!
91
- //! # tokio_test::block_on(async {
92
- //! let mut container = DependencyContainer::new();
93
- //!
94
- //! // Create a request-scoped dependency (e.g., request ID)
95
- //! let request_id = FactoryDependency::builder("request_id")
96
- //! .factory(|_req, _data, _resolved| {
97
- //! Box::pin(async {
98
- //! let id = uuid::Uuid::new_v4().to_string();
99
- //! Ok(Arc::new(id) as Arc<dyn std::any::Any + Send + Sync>)
100
- //! })
101
- //! })
102
- //! .cacheable(true) // Same ID throughout the request
103
- //! .build();
104
- //!
105
- //! container.register("request_id".to_string(), Arc::new(request_id)).unwrap();
106
- //! # });
107
- //! ```
108
- //!
109
- //! ## Accessing Request Data
110
- //!
111
- //! ```ignore
112
- //! use spikard_core::di::{DependencyContainer, FactoryDependency};
113
- //! use std::sync::Arc;
114
- //!
115
- //! # tokio_test::block_on(async {
116
- //! let mut container = DependencyContainer::new();
117
- //!
118
- //! // Access headers, query params, etc.
119
- //! let user_agent = FactoryDependency::builder("user_agent")
120
- //! .factory(|_req, request_data, _resolved| {
121
- //! let ua = request_data.headers
122
- //! .get("user-agent")
123
- //! .cloned()
124
- //! .unwrap_or_else(|| "unknown".to_string());
125
- //!
126
- //! Box::pin(async move {
127
- //! Ok(Arc::new(ua) as Arc<dyn std::any::Any + Send + Sync>)
128
- //! })
129
- //! })
130
- //! .build();
131
- //!
132
- //! container.register("user_agent".to_string(), Arc::new(user_agent)).unwrap();
133
- //! # });
134
- //! ```
135
- //!
136
- //! ## Cleanup Tasks
137
- //!
138
- //! ```ignore
139
- //! use spikard_core::di::ResolvedDependencies;
140
- //! use std::sync::Arc;
141
- //!
142
- //! # tokio_test::block_on(async {
143
- //! let mut resolved = ResolvedDependencies::new();
144
- //!
145
- //! // Add a dependency with cleanup
146
- //! resolved.insert("connection".to_string(), Arc::new("DB Connection"));
147
- //!
148
- //! // Register cleanup task
149
- //! resolved.add_cleanup_task(Box::new(|| {
150
- //! Box::pin(async {
151
- //! println!("Closing database connection");
152
- //! })
153
- //! }));
154
- //!
155
- //! // Cleanup runs when resolved is dropped (or explicitly)
156
- //! resolved.cleanup().await;
157
- //! # });
158
- //! ```
159
- //!
160
- //! # Performance
161
- //!
162
- //! The DI system is designed for high performance:
163
- //!
164
- //! - **Parallel resolution**: Independent dependencies are resolved concurrently
165
- //! - **Efficient caching**: Singleton and per-request caching minimize redundant work
166
- //! - **Arc-based sharing**: Values are reference-counted, not cloned
167
- //! - **Zero-cost abstractions**: Type erasure has minimal overhead
168
- //!
169
- //! # Thread Safety
170
- //!
171
- //! All components are thread-safe:
172
- //!
173
- //! - `DependencyContainer` can be shared with `Arc<DependencyContainer>`
174
- //! - Singleton cache uses `RwLock` for concurrent access
175
- //! - All dependencies must be `Send + Sync`
176
-
177
- mod container;
178
- mod dependency;
179
- mod error;
180
- mod factory;
181
- mod graph;
182
- mod resolved;
183
- mod value;
184
-
185
- // Public exports
186
- pub use container::DependencyContainer;
187
- pub use dependency::Dependency;
188
- pub use error::DependencyError;
189
- pub use factory::{FactoryDependency, FactoryDependencyBuilder, FactoryFn};
190
- pub use graph::DependencyGraph;
191
- pub use resolved::ResolvedDependencies;
192
- pub use value::ValueDependency;
1
+ //! Dependency Injection system for Spikard
2
+ //!
3
+ //! This module provides a comprehensive dependency injection system with:
4
+ //!
5
+ //! - **Type-safe dependency resolution**: Dependencies are stored as `Arc<dyn Any>` but
6
+ //! can be retrieved with type safety using `ResolvedDependencies::get<T>()`
7
+ //! - **Async resolution**: All dependencies can perform async operations during resolution
8
+ //! - **Batched parallel resolution**: Dependencies with no interdependencies are resolved
9
+ //! in parallel using topological sorting
10
+ //! - **Multiple caching strategies**:
11
+ //! - Singleton: Resolved once globally, cached forever
12
+ //! - Per-request cacheable: Resolved once per request
13
+ //! - Non-cacheable: Resolved every time
14
+ //! - **Cycle detection**: Circular dependencies are detected at registration time
15
+ //! - **Cleanup support**: Generator-pattern dependencies can register cleanup tasks
16
+ //!
17
+ //! # Architecture
18
+ //!
19
+ //! The DI system is built on several core components:
20
+ //!
21
+ //! - [`Dependency`] trait: The core abstraction that all dependencies implement
22
+ //! - [`DependencyContainer`]: Manages registration and resolution
23
+ //! - [`ResolvedDependencies`]: Stores resolved dependencies with type-safe access
24
+ //! - [`DependencyGraph`]: Handles topological sorting and cycle detection
25
+ //! - [`ValueDependency<T>`]: Simple static value dependencies
26
+ //! - [`FactoryDependency`]: Dynamic factory-based dependencies
27
+ //!
28
+ //! # Examples
29
+ //!
30
+ //! ## Basic Usage
31
+ //!
32
+ //! ```ignore
33
+ //! use spikard_core::di::{DependencyContainer, ValueDependency, FactoryDependency};
34
+ //! use std::sync::Arc;
35
+ //!
36
+ //! # tokio_test::block_on(async {
37
+ //! let mut container = DependencyContainer::new();
38
+ //!
39
+ //! // Register a simple value dependency
40
+ //! let config = ValueDependency::new("database_url", "postgresql://localhost/mydb");
41
+ //! container.register("database_url".to_string(), Arc::new(config)).unwrap();
42
+ //!
43
+ //! // Register a factory dependency that depends on the config
44
+ //! let pool = FactoryDependency::builder("db_pool")
45
+ //! .depends_on(vec!["database_url".to_string()])
46
+ //! .factory(|_req, _data, resolved| {
47
+ //! Box::pin(async move {
48
+ //! let url: Arc<String> = resolved.get("database_url").unwrap();
49
+ //! let pool = format!("Pool connected to {}", *url);
50
+ //! Ok(Arc::new(pool) as Arc<dyn std::any::Any + Send + Sync>)
51
+ //! })
52
+ //! })
53
+ //! .singleton(true) // Share across all requests
54
+ //! .build();
55
+ //! container.register("db_pool".to_string(), Arc::new(pool)).unwrap();
56
+ //!
57
+ //! // Resolve for a handler
58
+ //! use http::Request;
59
+ //! use crate::request_data::RequestData;
60
+ //! use std::collections::HashMap;
61
+ //!
62
+ //! let request = Request::builder().body(()).unwrap();
63
+ //! let request_data = RequestData {
64
+ //! path_params: Arc::new(HashMap::new()),
65
+ //! query_params: serde_json::Value::Null,
66
+ //! validated_params: None,
67
+ //! raw_query_params: Arc::new(HashMap::new()),
68
+ //! body: serde_json::Value::Null,
69
+ //! raw_body: None,
70
+ //! headers: Arc::new(HashMap::new()),
71
+ //! cookies: Arc::new(HashMap::new()),
72
+ //! method: "GET".to_string(),
73
+ //! path: "/".to_string(),
74
+ //! };
75
+ //!
76
+ //! let resolved = container
77
+ //! .resolve_for_handler(&["db_pool".to_string()], &request, &request_data)
78
+ //! .await
79
+ //! .unwrap();
80
+ //!
81
+ //! let pool: Option<Arc<String>> = resolved.get("db_pool");
82
+ //! assert!(pool.is_some());
83
+ //! # });
84
+ //! ```
85
+ //!
86
+ //! ## Request-Scoped Dependencies
87
+ //!
88
+ //! ```ignore
89
+ //! use spikard_core::di::{DependencyContainer, FactoryDependency};
90
+ //! use std::sync::Arc;
91
+ //!
92
+ //! # tokio_test::block_on(async {
93
+ //! let mut container = DependencyContainer::new();
94
+ //!
95
+ //! // Create a request-scoped dependency (e.g., request ID)
96
+ //! let request_id = FactoryDependency::builder("request_id")
97
+ //! .factory(|_req, _data, _resolved| {
98
+ //! Box::pin(async {
99
+ //! let id = uuid::Uuid::new_v4().to_string();
100
+ //! Ok(Arc::new(id) as Arc<dyn std::any::Any + Send + Sync>)
101
+ //! })
102
+ //! })
103
+ //! .cacheable(true) // Same ID throughout the request
104
+ //! .build();
105
+ //!
106
+ //! container.register("request_id".to_string(), Arc::new(request_id)).unwrap();
107
+ //! # });
108
+ //! ```
109
+ //!
110
+ //! ## Accessing Request Data
111
+ //!
112
+ //! ```ignore
113
+ //! use spikard_core::di::{DependencyContainer, FactoryDependency};
114
+ //! use std::sync::Arc;
115
+ //!
116
+ //! # tokio_test::block_on(async {
117
+ //! let mut container = DependencyContainer::new();
118
+ //!
119
+ //! // Access headers, query params, etc.
120
+ //! let user_agent = FactoryDependency::builder("user_agent")
121
+ //! .factory(|_req, request_data, _resolved| {
122
+ //! let ua = request_data.headers
123
+ //! .get("user-agent")
124
+ //! .cloned()
125
+ //! .unwrap_or_else(|| "unknown".to_string());
126
+ //!
127
+ //! Box::pin(async move {
128
+ //! Ok(Arc::new(ua) as Arc<dyn std::any::Any + Send + Sync>)
129
+ //! })
130
+ //! })
131
+ //! .build();
132
+ //!
133
+ //! container.register("user_agent".to_string(), Arc::new(user_agent)).unwrap();
134
+ //! # });
135
+ //! ```
136
+ //!
137
+ //! ## Cleanup Tasks
138
+ //!
139
+ //! ```ignore
140
+ //! use spikard_core::di::ResolvedDependencies;
141
+ //! use std::sync::Arc;
142
+ //!
143
+ //! # tokio_test::block_on(async {
144
+ //! let mut resolved = ResolvedDependencies::new();
145
+ //!
146
+ //! // Add a dependency with cleanup
147
+ //! resolved.insert("connection".to_string(), Arc::new("DB Connection"));
148
+ //!
149
+ //! // Register cleanup task
150
+ //! resolved.add_cleanup_task(Box::new(|| {
151
+ //! Box::pin(async {
152
+ //! println!("Closing database connection");
153
+ //! })
154
+ //! }));
155
+ //!
156
+ //! // Cleanup runs when resolved is dropped (or explicitly)
157
+ //! resolved.cleanup().await;
158
+ //! # });
159
+ //! ```
160
+ //!
161
+ //! # Performance
162
+ //!
163
+ //! The DI system is designed for high performance:
164
+ //!
165
+ //! - **Parallel resolution**: Independent dependencies are resolved concurrently
166
+ //! - **Efficient caching**: Singleton and per-request caching minimize redundant work
167
+ //! - **Arc-based sharing**: Values are reference-counted, not cloned
168
+ //! - **Zero-cost abstractions**: Type erasure has minimal overhead
169
+ //!
170
+ //! # Thread Safety
171
+ //!
172
+ //! All components are thread-safe:
173
+ //!
174
+ //! - `DependencyContainer` can be shared with `Arc<DependencyContainer>`
175
+ //! - Singleton cache uses `RwLock` for concurrent access
176
+ //! - All dependencies must be `Send + Sync`
177
+
178
+ mod container;
179
+ mod dependency;
180
+ mod error;
181
+ mod factory;
182
+ mod graph;
183
+ mod resolved;
184
+ mod value;
185
+
186
+ pub use container::DependencyContainer;
187
+ pub use dependency::Dependency;
188
+ pub use error::DependencyError;
189
+ pub use factory::{FactoryDependency, FactoryDependencyBuilder, FactoryFn};
190
+ pub use graph::DependencyGraph;
191
+ pub use resolved::ResolvedDependencies;
192
+ pub use value::ValueDependency;