spikard 0.3.2 → 0.3.4
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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +659 -659
- data/ext/spikard_rb/Cargo.toml +17 -17
- data/ext/spikard_rb/extconf.rb +10 -10
- data/ext/spikard_rb/src/lib.rs +6 -6
- data/lib/spikard/app.rb +386 -386
- data/lib/spikard/background.rb +27 -27
- data/lib/spikard/config.rb +396 -396
- data/lib/spikard/converters.rb +13 -13
- data/lib/spikard/handler_wrapper.rb +113 -113
- data/lib/spikard/provide.rb +214 -214
- data/lib/spikard/response.rb +173 -173
- data/lib/spikard/schema.rb +243 -243
- data/lib/spikard/sse.rb +111 -111
- data/lib/spikard/streaming_response.rb +44 -44
- data/lib/spikard/testing.rb +221 -221
- data/lib/spikard/upload_file.rb +131 -131
- data/lib/spikard/version.rb +5 -5
- data/lib/spikard/websocket.rb +59 -59
- data/lib/spikard.rb +43 -43
- data/sig/spikard.rbs +360 -360
- data/vendor/crates/spikard-core/Cargo.toml +40 -40
- data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -3
- data/vendor/crates/spikard-core/src/bindings/response.rs +133 -133
- data/vendor/crates/spikard-core/src/debug.rs +63 -63
- data/vendor/crates/spikard-core/src/di/container.rs +726 -726
- data/vendor/crates/spikard-core/src/di/dependency.rs +273 -273
- data/vendor/crates/spikard-core/src/di/error.rs +118 -118
- data/vendor/crates/spikard-core/src/di/factory.rs +538 -538
- data/vendor/crates/spikard-core/src/di/graph.rs +545 -545
- data/vendor/crates/spikard-core/src/di/mod.rs +192 -192
- data/vendor/crates/spikard-core/src/di/resolved.rs +411 -411
- data/vendor/crates/spikard-core/src/di/value.rs +283 -283
- data/vendor/crates/spikard-core/src/errors.rs +39 -39
- data/vendor/crates/spikard-core/src/http.rs +153 -153
- data/vendor/crates/spikard-core/src/lib.rs +29 -29
- data/vendor/crates/spikard-core/src/lifecycle.rs +422 -422
- data/vendor/crates/spikard-core/src/parameters.rs +722 -722
- data/vendor/crates/spikard-core/src/problem.rs +310 -310
- data/vendor/crates/spikard-core/src/request_data.rs +189 -189
- data/vendor/crates/spikard-core/src/router.rs +249 -249
- data/vendor/crates/spikard-core/src/schema_registry.rs +183 -183
- data/vendor/crates/spikard-core/src/type_hints.rs +304 -304
- data/vendor/crates/spikard-core/src/validation.rs +699 -699
- data/vendor/crates/spikard-http/Cargo.toml +58 -58
- data/vendor/crates/spikard-http/src/auth.rs +247 -247
- data/vendor/crates/spikard-http/src/background.rs +249 -249
- data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -3
- data/vendor/crates/spikard-http/src/bindings/response.rs +1 -1
- data/vendor/crates/spikard-http/src/body_metadata.rs +8 -8
- data/vendor/crates/spikard-http/src/cors.rs +490 -490
- data/vendor/crates/spikard-http/src/debug.rs +63 -63
- data/vendor/crates/spikard-http/src/di_handler.rs +423 -423
- data/vendor/crates/spikard-http/src/handler_response.rs +190 -190
- data/vendor/crates/spikard-http/src/handler_trait.rs +228 -228
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +284 -284
- data/vendor/crates/spikard-http/src/lib.rs +529 -529
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +149 -149
- data/vendor/crates/spikard-http/src/lifecycle.rs +428 -428
- data/vendor/crates/spikard-http/src/middleware/mod.rs +285 -285
- data/vendor/crates/spikard-http/src/middleware/multipart.rs +86 -86
- data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +147 -147
- data/vendor/crates/spikard-http/src/middleware/validation.rs +287 -287
- data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -309
- data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +190 -190
- data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +308 -308
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +195 -195
- data/vendor/crates/spikard-http/src/parameters.rs +1 -1
- data/vendor/crates/spikard-http/src/problem.rs +1 -1
- data/vendor/crates/spikard-http/src/query_parser.rs +369 -369
- data/vendor/crates/spikard-http/src/response.rs +399 -399
- data/vendor/crates/spikard-http/src/router.rs +1 -1
- data/vendor/crates/spikard-http/src/schema_registry.rs +1 -1
- data/vendor/crates/spikard-http/src/server/handler.rs +87 -87
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +98 -98
- data/vendor/crates/spikard-http/src/server/mod.rs +805 -805
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +119 -119
- data/vendor/crates/spikard-http/src/sse.rs +447 -447
- data/vendor/crates/spikard-http/src/testing/form.rs +14 -14
- data/vendor/crates/spikard-http/src/testing/multipart.rs +60 -60
- data/vendor/crates/spikard-http/src/testing/test_client.rs +285 -285
- data/vendor/crates/spikard-http/src/testing.rs +377 -377
- data/vendor/crates/spikard-http/src/type_hints.rs +1 -1
- data/vendor/crates/spikard-http/src/validation.rs +1 -1
- data/vendor/crates/spikard-http/src/websocket.rs +324 -324
- data/vendor/crates/spikard-rb/Cargo.toml +42 -42
- data/vendor/crates/spikard-rb/build.rs +8 -8
- data/vendor/crates/spikard-rb/src/background.rs +63 -63
- data/vendor/crates/spikard-rb/src/config.rs +294 -294
- data/vendor/crates/spikard-rb/src/conversion.rs +453 -453
- data/vendor/crates/spikard-rb/src/di.rs +409 -409
- data/vendor/crates/spikard-rb/src/handler.rs +625 -625
- data/vendor/crates/spikard-rb/src/lib.rs +2771 -2771
- data/vendor/crates/spikard-rb/src/lifecycle.rs +274 -274
- data/vendor/crates/spikard-rb/src/server.rs +283 -283
- data/vendor/crates/spikard-rb/src/sse.rs +231 -231
- data/vendor/crates/spikard-rb/src/test_client.rs +404 -404
- data/vendor/crates/spikard-rb/src/test_sse.rs +143 -143
- data/vendor/crates/spikard-rb/src/test_websocket.rs +221 -221
- data/vendor/crates/spikard-rb/src/websocket.rs +233 -233
- data/vendor/spikard-core/Cargo.toml +40 -40
- data/vendor/spikard-core/src/bindings/mod.rs +3 -3
- data/vendor/spikard-core/src/bindings/response.rs +133 -133
- data/vendor/spikard-core/src/debug.rs +63 -63
- data/vendor/spikard-core/src/di/container.rs +726 -726
- data/vendor/spikard-core/src/di/dependency.rs +273 -273
- data/vendor/spikard-core/src/di/error.rs +118 -118
- data/vendor/spikard-core/src/di/factory.rs +538 -538
- data/vendor/spikard-core/src/di/graph.rs +545 -545
- data/vendor/spikard-core/src/di/mod.rs +192 -192
- data/vendor/spikard-core/src/di/resolved.rs +411 -411
- data/vendor/spikard-core/src/di/value.rs +283 -283
- data/vendor/spikard-core/src/http.rs +153 -153
- data/vendor/spikard-core/src/lib.rs +28 -28
- data/vendor/spikard-core/src/lifecycle.rs +422 -422
- data/vendor/spikard-core/src/parameters.rs +719 -719
- data/vendor/spikard-core/src/problem.rs +310 -310
- data/vendor/spikard-core/src/request_data.rs +189 -189
- data/vendor/spikard-core/src/router.rs +249 -249
- data/vendor/spikard-core/src/schema_registry.rs +183 -183
- data/vendor/spikard-core/src/type_hints.rs +304 -304
- data/vendor/spikard-core/src/validation.rs +699 -699
- data/vendor/spikard-http/Cargo.toml +58 -58
- data/vendor/spikard-http/src/auth.rs +247 -247
- data/vendor/spikard-http/src/background.rs +249 -249
- data/vendor/spikard-http/src/bindings/mod.rs +3 -3
- data/vendor/spikard-http/src/bindings/response.rs +1 -1
- data/vendor/spikard-http/src/body_metadata.rs +8 -8
- data/vendor/spikard-http/src/cors.rs +490 -490
- data/vendor/spikard-http/src/debug.rs +63 -63
- data/vendor/spikard-http/src/di_handler.rs +423 -423
- data/vendor/spikard-http/src/handler_response.rs +190 -190
- data/vendor/spikard-http/src/handler_trait.rs +228 -228
- data/vendor/spikard-http/src/handler_trait_tests.rs +284 -284
- data/vendor/spikard-http/src/lib.rs +529 -529
- data/vendor/spikard-http/src/lifecycle/adapter.rs +149 -149
- data/vendor/spikard-http/src/lifecycle.rs +428 -428
- data/vendor/spikard-http/src/middleware/mod.rs +285 -285
- data/vendor/spikard-http/src/middleware/multipart.rs +86 -86
- data/vendor/spikard-http/src/middleware/urlencoded.rs +147 -147
- data/vendor/spikard-http/src/middleware/validation.rs +287 -287
- data/vendor/spikard-http/src/openapi/mod.rs +309 -309
- data/vendor/spikard-http/src/openapi/parameter_extraction.rs +190 -190
- data/vendor/spikard-http/src/openapi/schema_conversion.rs +308 -308
- data/vendor/spikard-http/src/openapi/spec_generation.rs +195 -195
- data/vendor/spikard-http/src/parameters.rs +1 -1
- data/vendor/spikard-http/src/problem.rs +1 -1
- data/vendor/spikard-http/src/query_parser.rs +369 -369
- data/vendor/spikard-http/src/response.rs +399 -399
- data/vendor/spikard-http/src/router.rs +1 -1
- data/vendor/spikard-http/src/schema_registry.rs +1 -1
- data/vendor/spikard-http/src/server/handler.rs +80 -80
- data/vendor/spikard-http/src/server/lifecycle_execution.rs +98 -98
- data/vendor/spikard-http/src/server/mod.rs +805 -805
- data/vendor/spikard-http/src/server/request_extraction.rs +119 -119
- data/vendor/spikard-http/src/sse.rs +447 -447
- data/vendor/spikard-http/src/testing/form.rs +14 -14
- data/vendor/spikard-http/src/testing/multipart.rs +60 -60
- data/vendor/spikard-http/src/testing/test_client.rs +285 -285
- data/vendor/spikard-http/src/testing.rs +377 -377
- data/vendor/spikard-http/src/type_hints.rs +1 -1
- data/vendor/spikard-http/src/validation.rs +1 -1
- data/vendor/spikard-http/src/websocket.rs +324 -324
- data/vendor/spikard-rb/Cargo.toml +42 -42
- data/vendor/spikard-rb/build.rs +8 -8
- data/vendor/spikard-rb/src/background.rs +63 -63
- data/vendor/spikard-rb/src/config.rs +294 -294
- data/vendor/spikard-rb/src/conversion.rs +392 -392
- data/vendor/spikard-rb/src/di.rs +409 -409
- data/vendor/spikard-rb/src/handler.rs +534 -534
- data/vendor/spikard-rb/src/lib.rs +2020 -2020
- data/vendor/spikard-rb/src/lifecycle.rs +267 -267
- data/vendor/spikard-rb/src/server.rs +283 -283
- data/vendor/spikard-rb/src/sse.rs +231 -231
- data/vendor/spikard-rb/src/test_client.rs +404 -404
- data/vendor/spikard-rb/src/test_sse.rs +143 -143
- data/vendor/spikard-rb/src/test_websocket.rs +221 -221
- data/vendor/spikard-rb/src/websocket.rs +233 -233
- metadata +1 -1
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
//! Shared utilities for lifecycle hook implementations across language bindings.
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides common error messages, hook registration patterns, and
|
|
4
|
-
//! serialization utilities to eliminate duplication across Python, Node.js,
|
|
5
|
-
//! Ruby, and WASM bindings.
|
|
6
|
-
|
|
7
|
-
use crate::lifecycle::LifecycleHook;
|
|
8
|
-
use axum::body::Body;
|
|
9
|
-
use axum::http::{Request, Response};
|
|
10
|
-
use std::sync::Arc;
|
|
11
|
-
|
|
12
|
-
/// Standard error message formatters for lifecycle hooks.
|
|
13
|
-
/// These are used consistently across all language bindings.
|
|
14
|
-
pub mod error {
|
|
15
|
-
use std::fmt::Display;
|
|
16
|
-
|
|
17
|
-
/// Format error when a hook invocation fails
|
|
18
|
-
pub fn call_failed(hook_name: &str, reason: impl Display) -> String {
|
|
19
|
-
format!("Hook '{}' call failed: {}", hook_name, reason)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/// Format error when a task execution fails (tokio/threading)
|
|
23
|
-
pub fn task_error(hook_name: &str, reason: impl Display) -> String {
|
|
24
|
-
format!("Hook '{}' task error: {}", hook_name, reason)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/// Format error when a promise/future fails
|
|
28
|
-
pub fn promise_failed(hook_name: &str, reason: impl Display) -> String {
|
|
29
|
-
format!("Hook '{}' promise failed: {}", hook_name, reason)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/// Format error for Python-specific failures
|
|
33
|
-
pub fn python_error(hook_name: &str, reason: impl Display) -> String {
|
|
34
|
-
format!("Hook '{}' Python error: {}", hook_name, reason)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/// Format error when body reading fails
|
|
38
|
-
pub fn body_read_failed(direction: &str, reason: impl Display) -> String {
|
|
39
|
-
format!("Failed to read {} body: {}", direction, reason)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/// Format error when body writing fails
|
|
43
|
-
pub fn body_write_failed(reason: impl Display) -> String {
|
|
44
|
-
format!("Failed to write body: {}", reason)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/// Format error for serialization failures
|
|
48
|
-
pub fn serialize_failed(context: &str, reason: impl Display) -> String {
|
|
49
|
-
format!("Failed to serialize {}: {}", context, reason)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/// Format error for deserialization failures
|
|
53
|
-
pub fn deserialize_failed(context: &str, reason: impl Display) -> String {
|
|
54
|
-
format!("Failed to deserialize {}: {}", context, reason)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/// Format error when building HTTP objects fails
|
|
58
|
-
pub fn build_failed(what: &str, reason: impl Display) -> String {
|
|
59
|
-
format!("Failed to build {}: {}", what, reason)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/// Utilities for serializing/deserializing request and response bodies
|
|
64
|
-
pub mod serial {
|
|
65
|
-
use super::*;
|
|
66
|
-
|
|
67
|
-
/// Extract body bytes from an axum Body
|
|
68
|
-
pub async fn extract_body(body: Body) -> Result<bytes::Bytes, String> {
|
|
69
|
-
use axum::body::to_bytes;
|
|
70
|
-
to_bytes(body, usize::MAX)
|
|
71
|
-
.await
|
|
72
|
-
.map_err(|e| error::body_read_failed("request/response", e))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/// Create a JSON-formatted response body
|
|
76
|
-
pub fn json_response_body(json: &serde_json::Value) -> Result<Body, String> {
|
|
77
|
-
serde_json::to_string(json)
|
|
78
|
-
.map(Body::from)
|
|
79
|
-
.map_err(|e| error::serialize_failed("response JSON", e))
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/// Parse a JSON value from bytes
|
|
83
|
-
pub fn parse_json(bytes: &[u8]) -> Result<serde_json::Value, String> {
|
|
84
|
-
if bytes.is_empty() {
|
|
85
|
-
return Ok(serde_json::Value::Null);
|
|
86
|
-
}
|
|
87
|
-
serde_json::from_slice(bytes)
|
|
88
|
-
.or_else(|_| Ok(serde_json::Value::String(String::from_utf8_lossy(bytes).to_string())))
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/// Re-export of the HTTP-specific lifecycle hooks type alias
|
|
93
|
-
pub use super::LifecycleHooks as HttpLifecycleHooks;
|
|
94
|
-
|
|
95
|
-
/// Helper for registering hooks with standard naming conventions
|
|
96
|
-
pub struct HookRegistry;
|
|
97
|
-
|
|
98
|
-
impl HookRegistry {
|
|
99
|
-
/// Extract hooks from a configuration and register them with a naming pattern
|
|
100
|
-
/// Used by bindings to standardize hook naming (e.g., "on_request_hook_0")
|
|
101
|
-
pub fn register_from_list<F>(
|
|
102
|
-
hooks: &mut HttpLifecycleHooks,
|
|
103
|
-
hook_list: Vec<Arc<dyn LifecycleHook<Request<Body>, Response<Body>>>>,
|
|
104
|
-
_hook_type: &str,
|
|
105
|
-
register_fn: F,
|
|
106
|
-
) where
|
|
107
|
-
F: Fn(&mut HttpLifecycleHooks, Arc<dyn LifecycleHook<Request<Body>, Response<Body>>>),
|
|
108
|
-
{
|
|
109
|
-
for hook in hook_list {
|
|
110
|
-
register_fn(hooks, hook);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
#[cfg(test)]
|
|
116
|
-
mod tests {
|
|
117
|
-
use super::*;
|
|
118
|
-
|
|
119
|
-
#[test]
|
|
120
|
-
fn test_error_messages() {
|
|
121
|
-
let call_err = error::call_failed("test_hook", "test reason");
|
|
122
|
-
assert!(call_err.contains("test_hook"));
|
|
123
|
-
assert!(call_err.contains("test reason"));
|
|
124
|
-
|
|
125
|
-
let task_err = error::task_error("task_hook", "spawn failed");
|
|
126
|
-
assert!(task_err.contains("task_hook"));
|
|
127
|
-
|
|
128
|
-
let promise_err = error::promise_failed("promise_hook", "rejected");
|
|
129
|
-
assert!(promise_err.contains("promise_hook"));
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
#[test]
|
|
133
|
-
fn test_body_error_messages() {
|
|
134
|
-
let read_err = error::body_read_failed("request", "stream closed");
|
|
135
|
-
assert!(read_err.contains("request"));
|
|
136
|
-
|
|
137
|
-
let write_err = error::body_write_failed("allocation failed");
|
|
138
|
-
assert!(write_err.contains("allocation"));
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
#[test]
|
|
142
|
-
fn test_json_error_messages() {
|
|
143
|
-
let ser_err = error::serialize_failed("request body", "invalid type");
|
|
144
|
-
assert!(ser_err.contains("request body"));
|
|
145
|
-
|
|
146
|
-
let deser_err = error::deserialize_failed("response", "malformed");
|
|
147
|
-
assert!(deser_err.contains("response"));
|
|
148
|
-
}
|
|
149
|
-
}
|
|
1
|
+
//! Shared utilities for lifecycle hook implementations across language bindings.
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides common error messages, hook registration patterns, and
|
|
4
|
+
//! serialization utilities to eliminate duplication across Python, Node.js,
|
|
5
|
+
//! Ruby, and WASM bindings.
|
|
6
|
+
|
|
7
|
+
use crate::lifecycle::LifecycleHook;
|
|
8
|
+
use axum::body::Body;
|
|
9
|
+
use axum::http::{Request, Response};
|
|
10
|
+
use std::sync::Arc;
|
|
11
|
+
|
|
12
|
+
/// Standard error message formatters for lifecycle hooks.
|
|
13
|
+
/// These are used consistently across all language bindings.
|
|
14
|
+
pub mod error {
|
|
15
|
+
use std::fmt::Display;
|
|
16
|
+
|
|
17
|
+
/// Format error when a hook invocation fails
|
|
18
|
+
pub fn call_failed(hook_name: &str, reason: impl Display) -> String {
|
|
19
|
+
format!("Hook '{}' call failed: {}", hook_name, reason)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Format error when a task execution fails (tokio/threading)
|
|
23
|
+
pub fn task_error(hook_name: &str, reason: impl Display) -> String {
|
|
24
|
+
format!("Hook '{}' task error: {}", hook_name, reason)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Format error when a promise/future fails
|
|
28
|
+
pub fn promise_failed(hook_name: &str, reason: impl Display) -> String {
|
|
29
|
+
format!("Hook '{}' promise failed: {}", hook_name, reason)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Format error for Python-specific failures
|
|
33
|
+
pub fn python_error(hook_name: &str, reason: impl Display) -> String {
|
|
34
|
+
format!("Hook '{}' Python error: {}", hook_name, reason)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Format error when body reading fails
|
|
38
|
+
pub fn body_read_failed(direction: &str, reason: impl Display) -> String {
|
|
39
|
+
format!("Failed to read {} body: {}", direction, reason)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Format error when body writing fails
|
|
43
|
+
pub fn body_write_failed(reason: impl Display) -> String {
|
|
44
|
+
format!("Failed to write body: {}", reason)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Format error for serialization failures
|
|
48
|
+
pub fn serialize_failed(context: &str, reason: impl Display) -> String {
|
|
49
|
+
format!("Failed to serialize {}: {}", context, reason)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Format error for deserialization failures
|
|
53
|
+
pub fn deserialize_failed(context: &str, reason: impl Display) -> String {
|
|
54
|
+
format!("Failed to deserialize {}: {}", context, reason)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Format error when building HTTP objects fails
|
|
58
|
+
pub fn build_failed(what: &str, reason: impl Display) -> String {
|
|
59
|
+
format!("Failed to build {}: {}", what, reason)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Utilities for serializing/deserializing request and response bodies
|
|
64
|
+
pub mod serial {
|
|
65
|
+
use super::*;
|
|
66
|
+
|
|
67
|
+
/// Extract body bytes from an axum Body
|
|
68
|
+
pub async fn extract_body(body: Body) -> Result<bytes::Bytes, String> {
|
|
69
|
+
use axum::body::to_bytes;
|
|
70
|
+
to_bytes(body, usize::MAX)
|
|
71
|
+
.await
|
|
72
|
+
.map_err(|e| error::body_read_failed("request/response", e))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Create a JSON-formatted response body
|
|
76
|
+
pub fn json_response_body(json: &serde_json::Value) -> Result<Body, String> {
|
|
77
|
+
serde_json::to_string(json)
|
|
78
|
+
.map(Body::from)
|
|
79
|
+
.map_err(|e| error::serialize_failed("response JSON", e))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Parse a JSON value from bytes
|
|
83
|
+
pub fn parse_json(bytes: &[u8]) -> Result<serde_json::Value, String> {
|
|
84
|
+
if bytes.is_empty() {
|
|
85
|
+
return Ok(serde_json::Value::Null);
|
|
86
|
+
}
|
|
87
|
+
serde_json::from_slice(bytes)
|
|
88
|
+
.or_else(|_| Ok(serde_json::Value::String(String::from_utf8_lossy(bytes).to_string())))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Re-export of the HTTP-specific lifecycle hooks type alias
|
|
93
|
+
pub use super::LifecycleHooks as HttpLifecycleHooks;
|
|
94
|
+
|
|
95
|
+
/// Helper for registering hooks with standard naming conventions
|
|
96
|
+
pub struct HookRegistry;
|
|
97
|
+
|
|
98
|
+
impl HookRegistry {
|
|
99
|
+
/// Extract hooks from a configuration and register them with a naming pattern
|
|
100
|
+
/// Used by bindings to standardize hook naming (e.g., "on_request_hook_0")
|
|
101
|
+
pub fn register_from_list<F>(
|
|
102
|
+
hooks: &mut HttpLifecycleHooks,
|
|
103
|
+
hook_list: Vec<Arc<dyn LifecycleHook<Request<Body>, Response<Body>>>>,
|
|
104
|
+
_hook_type: &str,
|
|
105
|
+
register_fn: F,
|
|
106
|
+
) where
|
|
107
|
+
F: Fn(&mut HttpLifecycleHooks, Arc<dyn LifecycleHook<Request<Body>, Response<Body>>>),
|
|
108
|
+
{
|
|
109
|
+
for hook in hook_list {
|
|
110
|
+
register_fn(hooks, hook);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#[cfg(test)]
|
|
116
|
+
mod tests {
|
|
117
|
+
use super::*;
|
|
118
|
+
|
|
119
|
+
#[test]
|
|
120
|
+
fn test_error_messages() {
|
|
121
|
+
let call_err = error::call_failed("test_hook", "test reason");
|
|
122
|
+
assert!(call_err.contains("test_hook"));
|
|
123
|
+
assert!(call_err.contains("test reason"));
|
|
124
|
+
|
|
125
|
+
let task_err = error::task_error("task_hook", "spawn failed");
|
|
126
|
+
assert!(task_err.contains("task_hook"));
|
|
127
|
+
|
|
128
|
+
let promise_err = error::promise_failed("promise_hook", "rejected");
|
|
129
|
+
assert!(promise_err.contains("promise_hook"));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#[test]
|
|
133
|
+
fn test_body_error_messages() {
|
|
134
|
+
let read_err = error::body_read_failed("request", "stream closed");
|
|
135
|
+
assert!(read_err.contains("request"));
|
|
136
|
+
|
|
137
|
+
let write_err = error::body_write_failed("allocation failed");
|
|
138
|
+
assert!(write_err.contains("allocation"));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#[test]
|
|
142
|
+
fn test_json_error_messages() {
|
|
143
|
+
let ser_err = error::serialize_failed("request body", "invalid type");
|
|
144
|
+
assert!(ser_err.contains("request body"));
|
|
145
|
+
|
|
146
|
+
let deser_err = error::deserialize_failed("response", "malformed");
|
|
147
|
+
assert!(deser_err.contains("response"));
|
|
148
|
+
}
|
|
149
|
+
}
|