spikard 0.4.0-arm64-darwin-23

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 (138) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +659 -0
  4. data/ext/spikard_rb/Cargo.toml +17 -0
  5. data/ext/spikard_rb/extconf.rb +10 -0
  6. data/ext/spikard_rb/src/lib.rs +6 -0
  7. data/lib/spikard/app.rb +405 -0
  8. data/lib/spikard/background.rb +27 -0
  9. data/lib/spikard/config.rb +396 -0
  10. data/lib/spikard/converters.rb +13 -0
  11. data/lib/spikard/handler_wrapper.rb +113 -0
  12. data/lib/spikard/provide.rb +214 -0
  13. data/lib/spikard/response.rb +173 -0
  14. data/lib/spikard/schema.rb +243 -0
  15. data/lib/spikard/sse.rb +111 -0
  16. data/lib/spikard/streaming_response.rb +44 -0
  17. data/lib/spikard/testing.rb +221 -0
  18. data/lib/spikard/upload_file.rb +131 -0
  19. data/lib/spikard/version.rb +5 -0
  20. data/lib/spikard/websocket.rb +59 -0
  21. data/lib/spikard.rb +43 -0
  22. data/sig/spikard.rbs +366 -0
  23. data/vendor/bundle/ruby/3.4.0/gems/diff-lcs-1.6.2/mise.toml +5 -0
  24. data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.10.0/build/buildkitd.toml +2 -0
  25. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
  26. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +139 -0
  27. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +561 -0
  28. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
  29. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
  30. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +403 -0
  31. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +274 -0
  32. data/vendor/crates/spikard-bindings-shared/src/lib.rs +25 -0
  33. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +298 -0
  34. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +637 -0
  35. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +309 -0
  36. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
  37. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +355 -0
  38. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +502 -0
  39. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +389 -0
  40. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +413 -0
  41. data/vendor/crates/spikard-core/Cargo.toml +40 -0
  42. data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -0
  43. data/vendor/crates/spikard-core/src/bindings/response.rs +133 -0
  44. data/vendor/crates/spikard-core/src/debug.rs +63 -0
  45. data/vendor/crates/spikard-core/src/di/container.rs +726 -0
  46. data/vendor/crates/spikard-core/src/di/dependency.rs +273 -0
  47. data/vendor/crates/spikard-core/src/di/error.rs +118 -0
  48. data/vendor/crates/spikard-core/src/di/factory.rs +538 -0
  49. data/vendor/crates/spikard-core/src/di/graph.rs +545 -0
  50. data/vendor/crates/spikard-core/src/di/mod.rs +192 -0
  51. data/vendor/crates/spikard-core/src/di/resolved.rs +411 -0
  52. data/vendor/crates/spikard-core/src/di/value.rs +283 -0
  53. data/vendor/crates/spikard-core/src/errors.rs +39 -0
  54. data/vendor/crates/spikard-core/src/http.rs +153 -0
  55. data/vendor/crates/spikard-core/src/lib.rs +29 -0
  56. data/vendor/crates/spikard-core/src/lifecycle.rs +422 -0
  57. data/vendor/crates/spikard-core/src/metadata.rs +397 -0
  58. data/vendor/crates/spikard-core/src/parameters.rs +723 -0
  59. data/vendor/crates/spikard-core/src/problem.rs +310 -0
  60. data/vendor/crates/spikard-core/src/request_data.rs +189 -0
  61. data/vendor/crates/spikard-core/src/router.rs +249 -0
  62. data/vendor/crates/spikard-core/src/schema_registry.rs +183 -0
  63. data/vendor/crates/spikard-core/src/type_hints.rs +304 -0
  64. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +689 -0
  65. data/vendor/crates/spikard-core/src/validation/mod.rs +459 -0
  66. data/vendor/crates/spikard-http/Cargo.toml +58 -0
  67. data/vendor/crates/spikard-http/examples/sse-notifications.rs +147 -0
  68. data/vendor/crates/spikard-http/examples/websocket-chat.rs +91 -0
  69. data/vendor/crates/spikard-http/src/auth.rs +247 -0
  70. data/vendor/crates/spikard-http/src/background.rs +1562 -0
  71. data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -0
  72. data/vendor/crates/spikard-http/src/bindings/response.rs +1 -0
  73. data/vendor/crates/spikard-http/src/body_metadata.rs +8 -0
  74. data/vendor/crates/spikard-http/src/cors.rs +490 -0
  75. data/vendor/crates/spikard-http/src/debug.rs +63 -0
  76. data/vendor/crates/spikard-http/src/di_handler.rs +1878 -0
  77. data/vendor/crates/spikard-http/src/handler_response.rs +532 -0
  78. data/vendor/crates/spikard-http/src/handler_trait.rs +861 -0
  79. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +284 -0
  80. data/vendor/crates/spikard-http/src/lib.rs +524 -0
  81. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +149 -0
  82. data/vendor/crates/spikard-http/src/lifecycle.rs +428 -0
  83. data/vendor/crates/spikard-http/src/middleware/mod.rs +285 -0
  84. data/vendor/crates/spikard-http/src/middleware/multipart.rs +930 -0
  85. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +541 -0
  86. data/vendor/crates/spikard-http/src/middleware/validation.rs +287 -0
  87. data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -0
  88. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +535 -0
  89. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +867 -0
  90. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +678 -0
  91. data/vendor/crates/spikard-http/src/query_parser.rs +369 -0
  92. data/vendor/crates/spikard-http/src/response.rs +399 -0
  93. data/vendor/crates/spikard-http/src/server/handler.rs +1557 -0
  94. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +98 -0
  95. data/vendor/crates/spikard-http/src/server/mod.rs +806 -0
  96. data/vendor/crates/spikard-http/src/server/request_extraction.rs +630 -0
  97. data/vendor/crates/spikard-http/src/server/routing_factory.rs +497 -0
  98. data/vendor/crates/spikard-http/src/sse.rs +961 -0
  99. data/vendor/crates/spikard-http/src/testing/form.rs +14 -0
  100. data/vendor/crates/spikard-http/src/testing/multipart.rs +60 -0
  101. data/vendor/crates/spikard-http/src/testing/test_client.rs +285 -0
  102. data/vendor/crates/spikard-http/src/testing.rs +377 -0
  103. data/vendor/crates/spikard-http/src/websocket.rs +831 -0
  104. data/vendor/crates/spikard-http/tests/background_behavior.rs +918 -0
  105. data/vendor/crates/spikard-http/tests/common/handlers.rs +308 -0
  106. data/vendor/crates/spikard-http/tests/common/mod.rs +21 -0
  107. data/vendor/crates/spikard-http/tests/di_integration.rs +202 -0
  108. data/vendor/crates/spikard-http/tests/doc_snippets.rs +4 -0
  109. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1135 -0
  110. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +688 -0
  111. data/vendor/crates/spikard-http/tests/server_config_builder.rs +324 -0
  112. data/vendor/crates/spikard-http/tests/sse_behavior.rs +728 -0
  113. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +724 -0
  114. data/vendor/crates/spikard-rb/Cargo.toml +43 -0
  115. data/vendor/crates/spikard-rb/build.rs +199 -0
  116. data/vendor/crates/spikard-rb/src/background.rs +63 -0
  117. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
  118. data/vendor/crates/spikard-rb/src/config/server_config.rs +283 -0
  119. data/vendor/crates/spikard-rb/src/conversion.rs +459 -0
  120. data/vendor/crates/spikard-rb/src/di/builder.rs +105 -0
  121. data/vendor/crates/spikard-rb/src/di/mod.rs +413 -0
  122. data/vendor/crates/spikard-rb/src/handler.rs +612 -0
  123. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
  124. data/vendor/crates/spikard-rb/src/lib.rs +1857 -0
  125. data/vendor/crates/spikard-rb/src/lifecycle.rs +275 -0
  126. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
  127. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +427 -0
  128. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
  129. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +326 -0
  130. data/vendor/crates/spikard-rb/src/server.rs +283 -0
  131. data/vendor/crates/spikard-rb/src/sse.rs +231 -0
  132. data/vendor/crates/spikard-rb/src/testing/client.rs +404 -0
  133. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
  134. data/vendor/crates/spikard-rb/src/testing/sse.rs +143 -0
  135. data/vendor/crates/spikard-rb/src/testing/websocket.rs +221 -0
  136. data/vendor/crates/spikard-rb/src/websocket.rs +233 -0
  137. data/vendor/crates/spikard-rb/tests/magnus_ffi_tests.rs +14 -0
  138. metadata +213 -0
@@ -0,0 +1,194 @@
1
+ //! Language-agnostic conversion interfaces
2
+
3
+ use std::any::Any;
4
+
5
+ /// Trait for converting from language-specific types to Rust types
6
+ pub trait FromLanguage: Sized {
7
+ /// The error type for conversion failures
8
+ type Error: std::fmt::Display;
9
+
10
+ /// Convert from a language-specific value
11
+ fn from_any(value: &(dyn Any + Send + Sync)) -> Result<Self, Self::Error>;
12
+ }
13
+
14
+ /// Trait for converting from Rust types to language-specific types
15
+ pub trait ToLanguage {
16
+ /// The error type for conversion failures
17
+ type Error: std::fmt::Display;
18
+
19
+ /// Convert to a language-specific value
20
+ fn to_any(&self) -> Result<Box<dyn Any + Send + Sync>, Self::Error>;
21
+ }
22
+
23
+ /// Trait for converting to/from JSON values
24
+ pub trait JsonConvertible: Sized {
25
+ /// The error type for conversion failures
26
+ type Error: std::fmt::Display;
27
+
28
+ /// Convert from a JSON value
29
+ fn from_json(value: serde_json::Value) -> Result<Self, Self::Error>;
30
+
31
+ /// Convert to a JSON value
32
+ fn to_json(&self) -> Result<serde_json::Value, Self::Error>;
33
+ }
34
+
35
+ /// Default JSON conversion error
36
+ #[derive(Debug)]
37
+ pub struct JsonConversionError(pub String);
38
+
39
+ impl std::fmt::Display for JsonConversionError {
40
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41
+ write!(f, "JSON conversion error: {}", self.0)
42
+ }
43
+ }
44
+
45
+ /// Default implementation for JSON values
46
+ impl JsonConvertible for serde_json::Value {
47
+ type Error = JsonConversionError;
48
+
49
+ fn from_json(value: serde_json::Value) -> Result<Self, Self::Error> {
50
+ Ok(value)
51
+ }
52
+
53
+ fn to_json(&self) -> Result<serde_json::Value, Self::Error> {
54
+ Ok(self.clone())
55
+ }
56
+ }
57
+
58
+ #[cfg(test)]
59
+ mod tests {
60
+ use super::*;
61
+ use serde_json::json;
62
+
63
+ #[derive(Debug)]
64
+ struct TestType {
65
+ value: i32,
66
+ }
67
+
68
+ impl FromLanguage for TestType {
69
+ type Error = String;
70
+
71
+ fn from_any(value: &(dyn Any + Send + Sync)) -> Result<Self, Self::Error> {
72
+ value
73
+ .downcast_ref::<i32>()
74
+ .map(|&v| TestType { value: v })
75
+ .ok_or_else(|| "Invalid type".to_string())
76
+ }
77
+ }
78
+
79
+ impl ToLanguage for TestType {
80
+ type Error = String;
81
+
82
+ fn to_any(&self) -> Result<Box<dyn Any + Send + Sync>, Self::Error> {
83
+ Ok(Box::new(self.value))
84
+ }
85
+ }
86
+
87
+ #[test]
88
+ fn test_json_conversion_error_display() {
89
+ let err = JsonConversionError("test error".to_string());
90
+ assert_eq!(err.to_string(), "JSON conversion error: test error");
91
+ }
92
+
93
+ #[test]
94
+ fn test_json_value_from_json() {
95
+ let input = json!({ "key": "value" });
96
+ let result = serde_json::Value::from_json(input.clone());
97
+ assert!(result.is_ok());
98
+ assert_eq!(result.unwrap(), input);
99
+ }
100
+
101
+ #[test]
102
+ fn test_json_value_to_json() {
103
+ let input = json!({ "key": "value" });
104
+ let result = serde_json::Value::to_json(&input);
105
+ assert!(result.is_ok());
106
+ assert_eq!(result.unwrap(), input);
107
+ }
108
+
109
+ #[test]
110
+ fn test_json_value_roundtrip() {
111
+ let original = json!({
112
+ "string": "test",
113
+ "number": 42,
114
+ "float": 3.14,
115
+ "bool": true,
116
+ "null": null,
117
+ "array": [1, 2, 3],
118
+ "object": { "nested": "value" }
119
+ });
120
+
121
+ let converted = serde_json::Value::from_json(original.clone()).unwrap();
122
+ let back = converted.to_json().unwrap();
123
+ assert_eq!(original, back);
124
+ }
125
+
126
+ #[test]
127
+ fn test_from_language_trait() {
128
+ let any_value: Box<dyn Any + Send + Sync> = Box::new(42i32);
129
+ let result = TestType::from_any(&*any_value);
130
+ assert!(result.is_ok());
131
+ assert_eq!(result.unwrap().value, 42);
132
+ }
133
+
134
+ #[test]
135
+ fn test_from_language_wrong_type() {
136
+ let any_value: Box<dyn Any + Send + Sync> = Box::new("string");
137
+ let result = TestType::from_any(&*any_value);
138
+ assert!(result.is_err());
139
+ assert_eq!(result.unwrap_err(), "Invalid type");
140
+ }
141
+
142
+ #[test]
143
+ fn test_to_language_trait() {
144
+ let test_obj = TestType { value: 123 };
145
+ let result = test_obj.to_any();
146
+ assert!(result.is_ok());
147
+
148
+ let any_box = result.unwrap();
149
+ let downcast = any_box.downcast_ref::<i32>();
150
+ assert_eq!(*downcast.unwrap(), 123);
151
+ }
152
+
153
+ #[test]
154
+ fn test_json_null_conversion() {
155
+ let null_value = serde_json::Value::Null;
156
+ let result = serde_json::Value::from_json(null_value.clone());
157
+ assert!(result.is_ok());
158
+ assert!(result.unwrap().is_null());
159
+ }
160
+
161
+ #[test]
162
+ fn test_json_array_conversion() {
163
+ let array = json!([1, 2, 3, 4, 5]);
164
+ let result = serde_json::Value::from_json(array.clone());
165
+ assert!(result.is_ok());
166
+ let converted = result.unwrap();
167
+ assert!(converted.is_array());
168
+ assert_eq!(converted.as_array().unwrap().len(), 5);
169
+ }
170
+
171
+ #[test]
172
+ fn test_json_nested_object_conversion() {
173
+ let nested = json!({
174
+ "level1": {
175
+ "level2": {
176
+ "level3": {
177
+ "value": "deep"
178
+ }
179
+ }
180
+ }
181
+ });
182
+
183
+ let result = serde_json::Value::from_json(nested.clone());
184
+ assert!(result.is_ok());
185
+ let converted = result.unwrap();
186
+ assert_eq!(converted["level1"]["level2"]["level3"]["value"], "deep");
187
+ }
188
+
189
+ #[test]
190
+ fn test_json_conversion_error_description() {
191
+ let err = JsonConversionError("Custom message".to_string());
192
+ assert!(err.to_string().contains("Custom message"));
193
+ }
194
+ }
@@ -0,0 +1,246 @@
1
+ //! Dependency injection base traits
2
+ //!
3
+ //! This module provides language-agnostic DI abstractions that eliminate
4
+ //! duplicate value/factory patterns across Node, Ruby, and PHP bindings.
5
+
6
+ use std::any::Any;
7
+ use std::future::Future;
8
+ use std::pin::Pin;
9
+ use std::sync::Arc;
10
+
11
+ use http::Request;
12
+ use spikard_core::RequestData;
13
+ use spikard_core::di::{Dependency, DependencyError, ResolvedDependencies};
14
+
15
+ /// Type alias for the common dependency resolution future type
16
+ type DependencyFuture<'a> =
17
+ Pin<Box<dyn Future<Output = Result<Arc<dyn Any + Send + Sync>, DependencyError>> + Send + 'a>>;
18
+
19
+ /// Adapter trait for value dependencies across language bindings
20
+ ///
21
+ /// Language bindings should implement this trait to wrap their
22
+ /// language-specific value storage (e.g., Py<PyAny>, Opaque<Value>, etc.)
23
+ pub trait ValueDependencyAdapter: Send + Sync {
24
+ /// Get the dependency key
25
+ fn key(&self) -> &str;
26
+
27
+ /// Resolve the stored value
28
+ ///
29
+ /// Returns an Arc<dyn Any> that can be downcast to the concrete type
30
+ fn resolve_value(&self) -> DependencyFuture<'_>;
31
+ }
32
+
33
+ /// Adapter trait for factory dependencies across language bindings
34
+ ///
35
+ /// Language bindings should implement this trait to wrap their
36
+ /// language-specific callable storage (e.g., Py<PyAny>, ThreadsafeFunction, etc.)
37
+ pub trait FactoryDependencyAdapter: Send + Sync {
38
+ /// Get the dependency key
39
+ fn key(&self) -> &str;
40
+
41
+ /// Invoke the factory with resolved dependencies
42
+ ///
43
+ /// The factory receives already-resolved dependencies and returns
44
+ /// a new instance wrapped in Arc<dyn Any>
45
+ fn invoke_factory(
46
+ &self,
47
+ request: &Request<()>,
48
+ request_data: &RequestData,
49
+ resolved: &ResolvedDependencies,
50
+ ) -> DependencyFuture<'_>;
51
+ }
52
+
53
+ /// Bridge between language-specific adapters and core DI system
54
+ ///
55
+ /// This struct implements the core `Dependency` trait while delegating
56
+ /// to the language-specific adapter implementation
57
+ pub struct ValueDependencyBridge<T: ValueDependencyAdapter> {
58
+ adapter: Arc<T>,
59
+ }
60
+
61
+ impl<T: ValueDependencyAdapter + 'static> ValueDependencyBridge<T> {
62
+ /// Create a new value dependency bridge
63
+ pub fn new(adapter: T) -> Self {
64
+ Self {
65
+ adapter: Arc::new(adapter),
66
+ }
67
+ }
68
+ }
69
+
70
+ impl<T: ValueDependencyAdapter + 'static> Dependency for ValueDependencyBridge<T> {
71
+ fn resolve(
72
+ &self,
73
+ _request: &Request<()>,
74
+ _request_data: &RequestData,
75
+ _resolved: &ResolvedDependencies,
76
+ ) -> Pin<Box<dyn Future<Output = Result<Arc<dyn Any + Send + Sync>, DependencyError>> + Send + '_>> {
77
+ self.adapter.resolve_value()
78
+ }
79
+
80
+ fn key(&self) -> &str {
81
+ self.adapter.key()
82
+ }
83
+
84
+ fn depends_on(&self) -> Vec<String> {
85
+ vec![]
86
+ }
87
+ }
88
+
89
+ /// Bridge between language-specific factory adapters and core DI system
90
+ pub struct FactoryDependencyBridge<T: FactoryDependencyAdapter> {
91
+ adapter: Arc<T>,
92
+ }
93
+
94
+ impl<T: FactoryDependencyAdapter + 'static> FactoryDependencyBridge<T> {
95
+ /// Create a new factory dependency bridge
96
+ pub fn new(adapter: T) -> Self {
97
+ Self {
98
+ adapter: Arc::new(adapter),
99
+ }
100
+ }
101
+ }
102
+
103
+ impl<T: FactoryDependencyAdapter + 'static> Dependency for FactoryDependencyBridge<T> {
104
+ fn resolve(
105
+ &self,
106
+ request: &Request<()>,
107
+ request_data: &RequestData,
108
+ resolved: &ResolvedDependencies,
109
+ ) -> Pin<Box<dyn Future<Output = Result<Arc<dyn Any + Send + Sync>, DependencyError>> + Send + '_>> {
110
+ self.adapter.invoke_factory(request, request_data, resolved)
111
+ }
112
+
113
+ fn key(&self) -> &str {
114
+ self.adapter.key()
115
+ }
116
+
117
+ fn depends_on(&self) -> Vec<String> {
118
+ vec![]
119
+ }
120
+ }
121
+
122
+ #[cfg(test)]
123
+ mod tests {
124
+ use super::*;
125
+ use std::collections::HashMap;
126
+ use std::sync::atomic::{AtomicUsize, Ordering};
127
+
128
+ struct MockValueAdapter {
129
+ key: String,
130
+ value: i32,
131
+ }
132
+
133
+ impl ValueDependencyAdapter for MockValueAdapter {
134
+ fn key(&self) -> &str {
135
+ &self.key
136
+ }
137
+
138
+ fn resolve_value(
139
+ &self,
140
+ ) -> Pin<Box<dyn Future<Output = Result<Arc<dyn Any + Send + Sync>, DependencyError>> + Send + '_>> {
141
+ let value = self.value;
142
+ Box::pin(async move { Ok(Arc::new(value) as Arc<dyn Any + Send + Sync>) })
143
+ }
144
+ }
145
+
146
+ struct MockFactoryAdapter {
147
+ key: String,
148
+ call_count: Arc<AtomicUsize>,
149
+ }
150
+
151
+ impl FactoryDependencyAdapter for MockFactoryAdapter {
152
+ fn key(&self) -> &str {
153
+ &self.key
154
+ }
155
+
156
+ fn invoke_factory(
157
+ &self,
158
+ _request: &Request<()>,
159
+ _request_data: &RequestData,
160
+ _resolved: &ResolvedDependencies,
161
+ ) -> Pin<Box<dyn Future<Output = Result<Arc<dyn Any + Send + Sync>, DependencyError>> + Send + '_>> {
162
+ let count = self.call_count.clone();
163
+ count.fetch_add(1, Ordering::SeqCst);
164
+ Box::pin(async move {
165
+ let value = count.load(Ordering::SeqCst);
166
+ Ok(Arc::new(value) as Arc<dyn Any + Send + Sync>)
167
+ })
168
+ }
169
+ }
170
+
171
+ #[tokio::test]
172
+ async fn test_value_dependency_bridge() {
173
+ let adapter = MockValueAdapter {
174
+ key: "test_key".to_string(),
175
+ value: 42,
176
+ };
177
+ let bridge = ValueDependencyBridge::new(adapter);
178
+
179
+ assert_eq!(bridge.key(), "test_key");
180
+ assert_eq!(bridge.depends_on(), Vec::<String>::new());
181
+
182
+ let request = Request::builder().body(()).unwrap();
183
+ let request_data = RequestData {
184
+ path_params: Arc::new(HashMap::new()),
185
+ query_params: serde_json::Value::Null,
186
+ raw_query_params: Arc::new(HashMap::new()),
187
+ body: serde_json::Value::Null,
188
+ raw_body: None,
189
+ headers: Arc::new(HashMap::new()),
190
+ cookies: Arc::new(HashMap::new()),
191
+ method: "GET".to_string(),
192
+ path: "/".to_string(),
193
+ dependencies: None,
194
+ };
195
+ let resolved = ResolvedDependencies::new();
196
+
197
+ let result = bridge.resolve(&request, &request_data, &resolved).await;
198
+ assert!(result.is_ok());
199
+
200
+ let value = result.unwrap();
201
+ let downcast = value.downcast_ref::<i32>();
202
+ assert_eq!(*downcast.unwrap(), 42);
203
+ }
204
+
205
+ #[tokio::test]
206
+ async fn test_factory_dependency_bridge() {
207
+ let call_count = Arc::new(AtomicUsize::new(0));
208
+ let adapter = MockFactoryAdapter {
209
+ key: "factory_key".to_string(),
210
+ call_count: call_count.clone(),
211
+ };
212
+ let bridge = FactoryDependencyBridge::new(adapter);
213
+
214
+ assert_eq!(bridge.key(), "factory_key");
215
+ assert_eq!(bridge.depends_on(), Vec::<String>::new());
216
+
217
+ let request = Request::builder().body(()).unwrap();
218
+ let request_data = RequestData {
219
+ path_params: Arc::new(HashMap::new()),
220
+ query_params: serde_json::Value::Null,
221
+ raw_query_params: Arc::new(HashMap::new()),
222
+ body: serde_json::Value::Null,
223
+ raw_body: None,
224
+ headers: Arc::new(HashMap::new()),
225
+ cookies: Arc::new(HashMap::new()),
226
+ method: "GET".to_string(),
227
+ path: "/".to_string(),
228
+ dependencies: None,
229
+ };
230
+ let resolved = ResolvedDependencies::new();
231
+
232
+ // First call
233
+ let result1 = bridge.resolve(&request, &request_data, &resolved).await;
234
+ assert!(result1.is_ok());
235
+ let value1 = result1.unwrap();
236
+ let count1 = *value1.downcast_ref::<usize>().unwrap();
237
+ assert_eq!(count1, 1);
238
+
239
+ // Second call - factory is invoked again
240
+ let result2 = bridge.resolve(&request, &request_data, &resolved).await;
241
+ assert!(result2.is_ok());
242
+ let value2 = result2.unwrap();
243
+ let count2 = *value2.downcast_ref::<usize>().unwrap();
244
+ assert_eq!(count2, 2);
245
+ }
246
+ }