spikard 0.3.2 → 0.3.3

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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +659 -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 +386 -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 +221 -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 +360 -360
  23. data/vendor/crates/spikard-core/Cargo.toml +40 -40
  24. data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -3
  25. data/vendor/crates/spikard-core/src/bindings/response.rs +133 -133
  26. data/vendor/crates/spikard-core/src/debug.rs +63 -63
  27. data/vendor/crates/spikard-core/src/di/container.rs +726 -726
  28. data/vendor/crates/spikard-core/src/di/dependency.rs +273 -273
  29. data/vendor/crates/spikard-core/src/di/error.rs +118 -118
  30. data/vendor/crates/spikard-core/src/di/factory.rs +538 -538
  31. data/vendor/crates/spikard-core/src/di/graph.rs +545 -545
  32. data/vendor/crates/spikard-core/src/di/mod.rs +192 -192
  33. data/vendor/crates/spikard-core/src/di/resolved.rs +411 -411
  34. data/vendor/crates/spikard-core/src/di/value.rs +283 -283
  35. data/vendor/crates/spikard-core/src/errors.rs +39 -39
  36. data/vendor/crates/spikard-core/src/http.rs +153 -153
  37. data/vendor/crates/spikard-core/src/lib.rs +29 -29
  38. data/vendor/crates/spikard-core/src/lifecycle.rs +422 -422
  39. data/vendor/crates/spikard-core/src/parameters.rs +722 -722
  40. data/vendor/crates/spikard-core/src/problem.rs +310 -310
  41. data/vendor/crates/spikard-core/src/request_data.rs +189 -189
  42. data/vendor/crates/spikard-core/src/router.rs +249 -249
  43. data/vendor/crates/spikard-core/src/schema_registry.rs +183 -183
  44. data/vendor/crates/spikard-core/src/type_hints.rs +304 -304
  45. data/vendor/crates/spikard-core/src/validation.rs +699 -699
  46. data/vendor/crates/spikard-http/Cargo.toml +58 -58
  47. data/vendor/crates/spikard-http/src/auth.rs +247 -247
  48. data/vendor/crates/spikard-http/src/background.rs +249 -249
  49. data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -3
  50. data/vendor/crates/spikard-http/src/bindings/response.rs +1 -1
  51. data/vendor/crates/spikard-http/src/body_metadata.rs +8 -8
  52. data/vendor/crates/spikard-http/src/cors.rs +490 -490
  53. data/vendor/crates/spikard-http/src/debug.rs +63 -63
  54. data/vendor/crates/spikard-http/src/di_handler.rs +423 -423
  55. data/vendor/crates/spikard-http/src/handler_response.rs +190 -190
  56. data/vendor/crates/spikard-http/src/handler_trait.rs +228 -228
  57. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +284 -284
  58. data/vendor/crates/spikard-http/src/lib.rs +529 -529
  59. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +149 -149
  60. data/vendor/crates/spikard-http/src/lifecycle.rs +428 -428
  61. data/vendor/crates/spikard-http/src/middleware/mod.rs +285 -285
  62. data/vendor/crates/spikard-http/src/middleware/multipart.rs +86 -86
  63. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +147 -147
  64. data/vendor/crates/spikard-http/src/middleware/validation.rs +287 -287
  65. data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -309
  66. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +190 -190
  67. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +308 -308
  68. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +195 -195
  69. data/vendor/crates/spikard-http/src/parameters.rs +1 -1
  70. data/vendor/crates/spikard-http/src/problem.rs +1 -1
  71. data/vendor/crates/spikard-http/src/query_parser.rs +369 -369
  72. data/vendor/crates/spikard-http/src/response.rs +399 -399
  73. data/vendor/crates/spikard-http/src/router.rs +1 -1
  74. data/vendor/crates/spikard-http/src/schema_registry.rs +1 -1
  75. data/vendor/crates/spikard-http/src/server/handler.rs +87 -87
  76. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +98 -98
  77. data/vendor/crates/spikard-http/src/server/mod.rs +805 -805
  78. data/vendor/crates/spikard-http/src/server/request_extraction.rs +119 -119
  79. data/vendor/crates/spikard-http/src/sse.rs +447 -447
  80. data/vendor/crates/spikard-http/src/testing/form.rs +14 -14
  81. data/vendor/crates/spikard-http/src/testing/multipart.rs +60 -60
  82. data/vendor/crates/spikard-http/src/testing/test_client.rs +285 -285
  83. data/vendor/crates/spikard-http/src/testing.rs +377 -377
  84. data/vendor/crates/spikard-http/src/type_hints.rs +1 -1
  85. data/vendor/crates/spikard-http/src/validation.rs +1 -1
  86. data/vendor/crates/spikard-http/src/websocket.rs +324 -324
  87. data/vendor/crates/spikard-rb/Cargo.toml +42 -42
  88. data/vendor/crates/spikard-rb/build.rs +8 -8
  89. data/vendor/crates/spikard-rb/src/background.rs +63 -63
  90. data/vendor/crates/spikard-rb/src/config.rs +294 -294
  91. data/vendor/crates/spikard-rb/src/conversion.rs +453 -453
  92. data/vendor/crates/spikard-rb/src/di.rs +409 -409
  93. data/vendor/crates/spikard-rb/src/handler.rs +625 -625
  94. data/vendor/crates/spikard-rb/src/lib.rs +2771 -2771
  95. data/vendor/crates/spikard-rb/src/lifecycle.rs +274 -274
  96. data/vendor/crates/spikard-rb/src/server.rs +283 -283
  97. data/vendor/crates/spikard-rb/src/sse.rs +231 -231
  98. data/vendor/crates/spikard-rb/src/test_client.rs +404 -404
  99. data/vendor/crates/spikard-rb/src/test_sse.rs +143 -143
  100. data/vendor/crates/spikard-rb/src/test_websocket.rs +221 -221
  101. data/vendor/crates/spikard-rb/src/websocket.rs +233 -233
  102. data/vendor/spikard-core/Cargo.toml +40 -40
  103. data/vendor/spikard-core/src/bindings/mod.rs +3 -3
  104. data/vendor/spikard-core/src/bindings/response.rs +133 -133
  105. data/vendor/spikard-core/src/debug.rs +63 -63
  106. data/vendor/spikard-core/src/di/container.rs +726 -726
  107. data/vendor/spikard-core/src/di/dependency.rs +273 -273
  108. data/vendor/spikard-core/src/di/error.rs +118 -118
  109. data/vendor/spikard-core/src/di/factory.rs +538 -538
  110. data/vendor/spikard-core/src/di/graph.rs +545 -545
  111. data/vendor/spikard-core/src/di/mod.rs +192 -192
  112. data/vendor/spikard-core/src/di/resolved.rs +411 -411
  113. data/vendor/spikard-core/src/di/value.rs +283 -283
  114. data/vendor/spikard-core/src/http.rs +153 -153
  115. data/vendor/spikard-core/src/lib.rs +28 -28
  116. data/vendor/spikard-core/src/lifecycle.rs +422 -422
  117. data/vendor/spikard-core/src/parameters.rs +719 -719
  118. data/vendor/spikard-core/src/problem.rs +310 -310
  119. data/vendor/spikard-core/src/request_data.rs +189 -189
  120. data/vendor/spikard-core/src/router.rs +249 -249
  121. data/vendor/spikard-core/src/schema_registry.rs +183 -183
  122. data/vendor/spikard-core/src/type_hints.rs +304 -304
  123. data/vendor/spikard-core/src/validation.rs +699 -699
  124. data/vendor/spikard-http/Cargo.toml +58 -58
  125. data/vendor/spikard-http/src/auth.rs +247 -247
  126. data/vendor/spikard-http/src/background.rs +249 -249
  127. data/vendor/spikard-http/src/bindings/mod.rs +3 -3
  128. data/vendor/spikard-http/src/bindings/response.rs +1 -1
  129. data/vendor/spikard-http/src/body_metadata.rs +8 -8
  130. data/vendor/spikard-http/src/cors.rs +490 -490
  131. data/vendor/spikard-http/src/debug.rs +63 -63
  132. data/vendor/spikard-http/src/di_handler.rs +423 -423
  133. data/vendor/spikard-http/src/handler_response.rs +190 -190
  134. data/vendor/spikard-http/src/handler_trait.rs +228 -228
  135. data/vendor/spikard-http/src/handler_trait_tests.rs +284 -284
  136. data/vendor/spikard-http/src/lib.rs +529 -529
  137. data/vendor/spikard-http/src/lifecycle/adapter.rs +149 -149
  138. data/vendor/spikard-http/src/lifecycle.rs +428 -428
  139. data/vendor/spikard-http/src/middleware/mod.rs +285 -285
  140. data/vendor/spikard-http/src/middleware/multipart.rs +86 -86
  141. data/vendor/spikard-http/src/middleware/urlencoded.rs +147 -147
  142. data/vendor/spikard-http/src/middleware/validation.rs +287 -287
  143. data/vendor/spikard-http/src/openapi/mod.rs +309 -309
  144. data/vendor/spikard-http/src/openapi/parameter_extraction.rs +190 -190
  145. data/vendor/spikard-http/src/openapi/schema_conversion.rs +308 -308
  146. data/vendor/spikard-http/src/openapi/spec_generation.rs +195 -195
  147. data/vendor/spikard-http/src/parameters.rs +1 -1
  148. data/vendor/spikard-http/src/problem.rs +1 -1
  149. data/vendor/spikard-http/src/query_parser.rs +369 -369
  150. data/vendor/spikard-http/src/response.rs +399 -399
  151. data/vendor/spikard-http/src/router.rs +1 -1
  152. data/vendor/spikard-http/src/schema_registry.rs +1 -1
  153. data/vendor/spikard-http/src/server/handler.rs +80 -80
  154. data/vendor/spikard-http/src/server/lifecycle_execution.rs +98 -98
  155. data/vendor/spikard-http/src/server/mod.rs +805 -805
  156. data/vendor/spikard-http/src/server/request_extraction.rs +119 -119
  157. data/vendor/spikard-http/src/sse.rs +447 -447
  158. data/vendor/spikard-http/src/testing/form.rs +14 -14
  159. data/vendor/spikard-http/src/testing/multipart.rs +60 -60
  160. data/vendor/spikard-http/src/testing/test_client.rs +285 -285
  161. data/vendor/spikard-http/src/testing.rs +377 -377
  162. data/vendor/spikard-http/src/type_hints.rs +1 -1
  163. data/vendor/spikard-http/src/validation.rs +1 -1
  164. data/vendor/spikard-http/src/websocket.rs +324 -324
  165. data/vendor/spikard-rb/Cargo.toml +42 -42
  166. data/vendor/spikard-rb/build.rs +8 -8
  167. data/vendor/spikard-rb/src/background.rs +63 -63
  168. data/vendor/spikard-rb/src/config.rs +294 -294
  169. data/vendor/spikard-rb/src/conversion.rs +392 -392
  170. data/vendor/spikard-rb/src/di.rs +409 -409
  171. data/vendor/spikard-rb/src/handler.rs +534 -534
  172. data/vendor/spikard-rb/src/lib.rs +2020 -2020
  173. data/vendor/spikard-rb/src/lifecycle.rs +267 -267
  174. data/vendor/spikard-rb/src/server.rs +283 -283
  175. data/vendor/spikard-rb/src/sse.rs +231 -231
  176. data/vendor/spikard-rb/src/test_client.rs +404 -404
  177. data/vendor/spikard-rb/src/test_sse.rs +143 -143
  178. data/vendor/spikard-rb/src/test_websocket.rs +221 -221
  179. data/vendor/spikard-rb/src/websocket.rs +233 -233
  180. metadata +1 -1
@@ -1,190 +1,190 @@
1
- use axum::{
2
- BoxError,
3
- body::Body,
4
- http::{HeaderMap, HeaderName, HeaderValue, StatusCode},
5
- response::Response as AxumResponse,
6
- };
7
- use bytes::Bytes;
8
- use futures::{Stream, StreamExt};
9
- use std::pin::Pin;
10
-
11
- /// Unified response type that can represent either a ready response or a streaming body.
12
- ///
13
- /// This enum allows handlers to return either:
14
- /// - A complete response that's ready to send (`Response` variant)
15
- /// - A streaming response with potentially unbounded data (`Stream` variant)
16
- ///
17
- /// # Variants
18
- ///
19
- /// * `Response` - A complete Axum response ready to send to the client. Use this for
20
- /// responses where you have all the data ready (files, JSON bodies, HTML, etc.)
21
- ///
22
- /// * `Stream` - A streaming response that produces data chunks over time. Use this for:
23
- /// - Large files (avoid loading entire file in memory)
24
- /// - Server-Sent Events (SSE)
25
- /// - Long-polling responses
26
- /// - Real-time data feeds
27
- /// - Any unbounded or very large responses
28
- ///
29
- /// # Examples
30
- ///
31
- /// ```ignore
32
- /// // Regular response
33
- /// let response = AxumResponse::builder()
34
- /// .status(StatusCode::OK)
35
- /// .body(Body::from("Hello"))
36
- /// .unwrap();
37
- /// let handler_response = HandlerResponse::from(response);
38
- ///
39
- /// // Streaming response
40
- /// let stream = futures::stream::iter(vec![
41
- /// Ok::<_, Box<dyn std::error::Error>>(Bytes::from("chunk1")),
42
- /// Ok(Bytes::from("chunk2")),
43
- /// ]);
44
- /// let response = HandlerResponse::stream(stream)
45
- /// .with_status(StatusCode::OK);
46
- /// ```
47
- pub enum HandlerResponse {
48
- /// A complete response ready to send
49
- Response(AxumResponse<Body>),
50
- /// A streaming response with custom status and headers
51
- Stream {
52
- /// The byte stream that will be sent to the client
53
- stream: Pin<Box<dyn Stream<Item = Result<Bytes, BoxError>> + Send + 'static>>,
54
- /// HTTP status code for the response
55
- status: StatusCode,
56
- /// Response headers to send
57
- headers: HeaderMap,
58
- },
59
- }
60
-
61
- impl From<AxumResponse<Body>> for HandlerResponse {
62
- fn from(response: AxumResponse<Body>) -> Self {
63
- HandlerResponse::Response(response)
64
- }
65
- }
66
-
67
- impl HandlerResponse {
68
- /// Convert the handler response into an Axum response.
69
- ///
70
- /// Consumes the `HandlerResponse` and produces an `AxumResponse<Body>` ready
71
- /// to be sent to the client. For streaming responses, wraps the stream in an
72
- /// Axum Body.
73
- ///
74
- /// # Returns
75
- /// An `AxumResponse<Body>` ready to be returned from an Axum handler
76
- pub fn into_response(self) -> AxumResponse<Body> {
77
- match self {
78
- HandlerResponse::Response(response) => response,
79
- HandlerResponse::Stream {
80
- stream,
81
- status,
82
- mut headers,
83
- } => {
84
- let body = Body::from_stream(stream);
85
- let mut response = AxumResponse::new(body);
86
- *response.status_mut() = status;
87
- response.headers_mut().extend(headers.drain());
88
- response
89
- }
90
- }
91
- }
92
-
93
- /// Create a streaming response from any async stream of byte chunks.
94
- ///
95
- /// Wraps an async stream of byte chunks into a `HandlerResponse::Stream`.
96
- /// This is useful for large files, real-time data, or any unbounded response.
97
- ///
98
- /// # Type Parameters
99
- /// * `S` - The stream type implementing `Stream<Item = Result<Bytes, E>>`
100
- /// * `E` - The error type that can be converted to `BoxError`
101
- ///
102
- /// # Arguments
103
- /// * `stream` - An async stream that yields byte chunks or errors
104
- ///
105
- /// # Returns
106
- /// A `HandlerResponse` with 200 OK status and empty headers (customize with
107
- /// `with_status()` and `with_header()`)
108
- ///
109
- /// # Example
110
- ///
111
- /// ```ignore
112
- /// use futures::stream;
113
- /// use spikard_http::HandlerResponse;
114
- /// use bytes::Bytes;
115
- ///
116
- /// let stream = stream::iter(vec![
117
- /// Ok::<_, Box<dyn std::error::Error>>(Bytes::from("Hello ")),
118
- /// Ok(Bytes::from("World")),
119
- /// ]);
120
- /// let response = HandlerResponse::stream(stream)
121
- /// .with_status(StatusCode::OK);
122
- /// ```
123
- pub fn stream<S, E>(stream: S) -> Self
124
- where
125
- S: Stream<Item = Result<Bytes, E>> + Send + 'static,
126
- E: Into<BoxError>,
127
- {
128
- let mapped = stream.map(|chunk| chunk.map_err(Into::into));
129
- HandlerResponse::Stream {
130
- stream: Box::pin(mapped),
131
- status: StatusCode::OK,
132
- headers: HeaderMap::new(),
133
- }
134
- }
135
-
136
- /// Override the HTTP status code for the streaming response.
137
- ///
138
- /// Sets the HTTP status code to be used in the response. This only affects
139
- /// `Stream` variants; regular responses already have their status set.
140
- ///
141
- /// # Arguments
142
- /// * `status` - The HTTP status code to use (e.g., `StatusCode::OK`)
143
- ///
144
- /// # Returns
145
- /// Self for method chaining
146
- ///
147
- /// # Example
148
- ///
149
- /// ```ignore
150
- /// let response = HandlerResponse::stream(my_stream)
151
- /// .with_status(StatusCode::PARTIAL_CONTENT);
152
- /// ```
153
- pub fn with_status(mut self, status: StatusCode) -> Self {
154
- if let HandlerResponse::Stream { status: s, .. } = &mut self {
155
- *s = status;
156
- }
157
- self
158
- }
159
-
160
- /// Insert or replace a header on the streaming response.
161
- ///
162
- /// Adds an HTTP header to the response. This only affects `Stream` variants;
163
- /// regular responses already have their headers set. If a header with the same
164
- /// name already exists, it will be replaced.
165
- ///
166
- /// # Arguments
167
- /// * `name` - The header name (e.g., `HeaderName::from_static("content-type")`)
168
- /// * `value` - The header value
169
- ///
170
- /// # Returns
171
- /// Self for method chaining
172
- ///
173
- /// # Example
174
- ///
175
- /// ```ignore
176
- /// use axum::http::{HeaderName, HeaderValue};
177
- ///
178
- /// let response = HandlerResponse::stream(my_stream)
179
- /// .with_header(
180
- /// HeaderName::from_static("content-type"),
181
- /// HeaderValue::from_static("application/octet-stream")
182
- /// );
183
- /// ```
184
- pub fn with_header(mut self, name: HeaderName, value: HeaderValue) -> Self {
185
- if let HandlerResponse::Stream { headers, .. } = &mut self {
186
- headers.insert(name, value);
187
- }
188
- self
189
- }
190
- }
1
+ use axum::{
2
+ BoxError,
3
+ body::Body,
4
+ http::{HeaderMap, HeaderName, HeaderValue, StatusCode},
5
+ response::Response as AxumResponse,
6
+ };
7
+ use bytes::Bytes;
8
+ use futures::{Stream, StreamExt};
9
+ use std::pin::Pin;
10
+
11
+ /// Unified response type that can represent either a ready response or a streaming body.
12
+ ///
13
+ /// This enum allows handlers to return either:
14
+ /// - A complete response that's ready to send (`Response` variant)
15
+ /// - A streaming response with potentially unbounded data (`Stream` variant)
16
+ ///
17
+ /// # Variants
18
+ ///
19
+ /// * `Response` - A complete Axum response ready to send to the client. Use this for
20
+ /// responses where you have all the data ready (files, JSON bodies, HTML, etc.)
21
+ ///
22
+ /// * `Stream` - A streaming response that produces data chunks over time. Use this for:
23
+ /// - Large files (avoid loading entire file in memory)
24
+ /// - Server-Sent Events (SSE)
25
+ /// - Long-polling responses
26
+ /// - Real-time data feeds
27
+ /// - Any unbounded or very large responses
28
+ ///
29
+ /// # Examples
30
+ ///
31
+ /// ```ignore
32
+ /// // Regular response
33
+ /// let response = AxumResponse::builder()
34
+ /// .status(StatusCode::OK)
35
+ /// .body(Body::from("Hello"))
36
+ /// .unwrap();
37
+ /// let handler_response = HandlerResponse::from(response);
38
+ ///
39
+ /// // Streaming response
40
+ /// let stream = futures::stream::iter(vec![
41
+ /// Ok::<_, Box<dyn std::error::Error>>(Bytes::from("chunk1")),
42
+ /// Ok(Bytes::from("chunk2")),
43
+ /// ]);
44
+ /// let response = HandlerResponse::stream(stream)
45
+ /// .with_status(StatusCode::OK);
46
+ /// ```
47
+ pub enum HandlerResponse {
48
+ /// A complete response ready to send
49
+ Response(AxumResponse<Body>),
50
+ /// A streaming response with custom status and headers
51
+ Stream {
52
+ /// The byte stream that will be sent to the client
53
+ stream: Pin<Box<dyn Stream<Item = Result<Bytes, BoxError>> + Send + 'static>>,
54
+ /// HTTP status code for the response
55
+ status: StatusCode,
56
+ /// Response headers to send
57
+ headers: HeaderMap,
58
+ },
59
+ }
60
+
61
+ impl From<AxumResponse<Body>> for HandlerResponse {
62
+ fn from(response: AxumResponse<Body>) -> Self {
63
+ HandlerResponse::Response(response)
64
+ }
65
+ }
66
+
67
+ impl HandlerResponse {
68
+ /// Convert the handler response into an Axum response.
69
+ ///
70
+ /// Consumes the `HandlerResponse` and produces an `AxumResponse<Body>` ready
71
+ /// to be sent to the client. For streaming responses, wraps the stream in an
72
+ /// Axum Body.
73
+ ///
74
+ /// # Returns
75
+ /// An `AxumResponse<Body>` ready to be returned from an Axum handler
76
+ pub fn into_response(self) -> AxumResponse<Body> {
77
+ match self {
78
+ HandlerResponse::Response(response) => response,
79
+ HandlerResponse::Stream {
80
+ stream,
81
+ status,
82
+ mut headers,
83
+ } => {
84
+ let body = Body::from_stream(stream);
85
+ let mut response = AxumResponse::new(body);
86
+ *response.status_mut() = status;
87
+ response.headers_mut().extend(headers.drain());
88
+ response
89
+ }
90
+ }
91
+ }
92
+
93
+ /// Create a streaming response from any async stream of byte chunks.
94
+ ///
95
+ /// Wraps an async stream of byte chunks into a `HandlerResponse::Stream`.
96
+ /// This is useful for large files, real-time data, or any unbounded response.
97
+ ///
98
+ /// # Type Parameters
99
+ /// * `S` - The stream type implementing `Stream<Item = Result<Bytes, E>>`
100
+ /// * `E` - The error type that can be converted to `BoxError`
101
+ ///
102
+ /// # Arguments
103
+ /// * `stream` - An async stream that yields byte chunks or errors
104
+ ///
105
+ /// # Returns
106
+ /// A `HandlerResponse` with 200 OK status and empty headers (customize with
107
+ /// `with_status()` and `with_header()`)
108
+ ///
109
+ /// # Example
110
+ ///
111
+ /// ```ignore
112
+ /// use futures::stream;
113
+ /// use spikard_http::HandlerResponse;
114
+ /// use bytes::Bytes;
115
+ ///
116
+ /// let stream = stream::iter(vec![
117
+ /// Ok::<_, Box<dyn std::error::Error>>(Bytes::from("Hello ")),
118
+ /// Ok(Bytes::from("World")),
119
+ /// ]);
120
+ /// let response = HandlerResponse::stream(stream)
121
+ /// .with_status(StatusCode::OK);
122
+ /// ```
123
+ pub fn stream<S, E>(stream: S) -> Self
124
+ where
125
+ S: Stream<Item = Result<Bytes, E>> + Send + 'static,
126
+ E: Into<BoxError>,
127
+ {
128
+ let mapped = stream.map(|chunk| chunk.map_err(Into::into));
129
+ HandlerResponse::Stream {
130
+ stream: Box::pin(mapped),
131
+ status: StatusCode::OK,
132
+ headers: HeaderMap::new(),
133
+ }
134
+ }
135
+
136
+ /// Override the HTTP status code for the streaming response.
137
+ ///
138
+ /// Sets the HTTP status code to be used in the response. This only affects
139
+ /// `Stream` variants; regular responses already have their status set.
140
+ ///
141
+ /// # Arguments
142
+ /// * `status` - The HTTP status code to use (e.g., `StatusCode::OK`)
143
+ ///
144
+ /// # Returns
145
+ /// Self for method chaining
146
+ ///
147
+ /// # Example
148
+ ///
149
+ /// ```ignore
150
+ /// let response = HandlerResponse::stream(my_stream)
151
+ /// .with_status(StatusCode::PARTIAL_CONTENT);
152
+ /// ```
153
+ pub fn with_status(mut self, status: StatusCode) -> Self {
154
+ if let HandlerResponse::Stream { status: s, .. } = &mut self {
155
+ *s = status;
156
+ }
157
+ self
158
+ }
159
+
160
+ /// Insert or replace a header on the streaming response.
161
+ ///
162
+ /// Adds an HTTP header to the response. This only affects `Stream` variants;
163
+ /// regular responses already have their headers set. If a header with the same
164
+ /// name already exists, it will be replaced.
165
+ ///
166
+ /// # Arguments
167
+ /// * `name` - The header name (e.g., `HeaderName::from_static("content-type")`)
168
+ /// * `value` - The header value
169
+ ///
170
+ /// # Returns
171
+ /// Self for method chaining
172
+ ///
173
+ /// # Example
174
+ ///
175
+ /// ```ignore
176
+ /// use axum::http::{HeaderName, HeaderValue};
177
+ ///
178
+ /// let response = HandlerResponse::stream(my_stream)
179
+ /// .with_header(
180
+ /// HeaderName::from_static("content-type"),
181
+ /// HeaderValue::from_static("application/octet-stream")
182
+ /// );
183
+ /// ```
184
+ pub fn with_header(mut self, name: HeaderName, value: HeaderValue) -> Self {
185
+ if let HandlerResponse::Stream { headers, .. } = &mut self {
186
+ headers.insert(name, value);
187
+ }
188
+ self
189
+ }
190
+ }