http_kit/body/mod.rs
1//! HTTP request/response body handling.
2//!
3//! This module provides a flexible [`Body`] type that can represent HTTP request and response bodies
4//! in various forms while maintaining efficiency and type safety.
5//!
6//! # Body Representation
7//!
8//! The body can hold data in different forms:
9//!
10//! - **Bytes**: For simple in-memory bodies that fit entirely in memory
11//! - **AsyncReader**: For streaming from files or other async sources
12//! - **Stream**: For general async streaming data with backpressure support
13//! - **Frozen**: For consumed bodies that can no longer provide data
14//!
15//! # Format Support
16//!
17//! The body type provides convenient methods for working with common formats:
18//!
19//! - **JSON** (with `json` feature): Serialize/deserialize to/from JSON
20//! - **URL-encoded forms** (with `form` feature): Handle form data
21//! - **Files** (with `fs` feature): Stream file contents with MIME detection
22//! - **Raw bytes**: Direct byte manipulation and string conversion
23//!
24//! # Examples
25//!
26//! ## Basic Usage
27//!
28//! ```rust
29//! use http_kit::Body;
30//!
31//! // Create empty body
32//! let empty = Body::empty();
33//!
34//! // Create from string
35//! let text = Body::from_bytes("Hello world!");
36//!
37//! // Create from bytes
38//! let data = Body::from_bytes(vec![1, 2, 3, 4]);
39//! ```
40//!
41//! ## JSON Handling
42//!
43/// ```rust
44/// # #[cfg(feature = "json")]
45/// # {
46/// use http_kit::Body;
47/// use serde::{Serialize, Deserialize};
48///
49/// #[derive(Serialize, Deserialize)]
50/// struct User { name: String }
51///
52/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
53/// // Create from JSON
54/// let user = User { name: "Alice".to_string() };
55/// let body = Body::from_json(&user)?;
56///
57/// // Parse to JSON
58/// let mut body = Body::from_bytes(r#"{"name":"Bob"}"#);
59/// let user: User = body.into_json().await?;
60/// # Ok(())
61/// # }
62/// # }
63/// ```
64//
65// ## File Streaming
66//
67// ```rust,no_run
68// # #[cfg(feature = "fs")]
69// # {
70// use http_kit::Body;
71//
72// // Stream file contents
73// let body = Body::from_file("large_file.dat").await?;
74// # }
75// # Ok::<(), std::io::Error>(())
76// ```
77mod convert;
78mod error_type;
79mod utils;
80pub use error_type::Error;
81use futures_lite::{ready, Stream, StreamExt};
82
83use self::utils::IntoAsyncRead;
84use bytestr::ByteStr;
85
86use bytes::Bytes;
87use futures_lite::{AsyncBufRead, AsyncBufReadExt};
88
89use alloc::{boxed::Box, vec::Vec};
90use core::fmt::Debug;
91use core::mem::{replace, swap, take};
92use core::pin::Pin;
93use core::task::{Context, Poll};
94type BoxCoreError = Box<dyn core::error::Error + Send + Sync + 'static>;
95
96// A boxed steam object.
97type BoxStream = Pin<Box<dyn Stream<Item = Result<Bytes, BoxCoreError>> + Send + Sync + 'static>>;
98
99// A boxed bufreader object.
100type BoxBufReader = Pin<Box<dyn AsyncBufRead + Send + Sync + 'static>>;
101
102#[cfg(feature = "http_body")]
103pub use http_body::Body as HttpBody;
104
105/// Flexible HTTP body that can represent data in various forms.
106///
107/// `Body` is the core type for representing HTTP request and response bodies.
108/// It can efficiently handle different data sources:
109///
110/// - **In-memory data**: Bytes, strings, vectors
111/// - **Streaming data**: Files, network streams, async readers
112/// - **Structured data**: JSON, form data (with appropriate features)
113///
114/// The body automatically manages the underlying representation and provides
115/// zero-copy conversions where possible.
116///
117/// # Examples
118///
119/// ```rust
120/// use http_kit::Body;
121///
122/// // Create from string
123/// let body = Body::from_bytes("Hello, world!");
124///
125/// // Create empty body
126/// let empty = Body::empty();
127///
128/// // Check if empty (when size is known)
129/// if let Some(true) = body.is_empty() {
130/// println!("Body is empty");
131/// }
132/// ```
133pub struct Body {
134 inner: BodyInner,
135}
136
137impl Debug for Body {
138 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139 f.write_str("Body")
140 }
141}
142
143impl_error!(
144 BodyFrozen,
145 "Body was frozen,it may have been consumed by `take()`"
146);
147
148enum BodyInner {
149 Once(Bytes),
150 Reader {
151 reader: BoxBufReader,
152 length: Option<usize>,
153 },
154 Stream(BoxStream),
155 Freeze,
156}
157
158impl Default for BodyInner {
159 fn default() -> Self {
160 Self::Once(Bytes::new())
161 }
162}
163
164impl Body {
165 /// Creates a new empty body.
166 ///
167 /// This creates a body with zero bytes that can be used as a placeholder
168 /// or default value.
169 ///
170 /// # Examples
171 ///
172 /// ```rust
173 /// use http_kit::Body;
174 ///
175 /// let body = Body::empty();
176 /// assert_eq!(body.len(), Some(0));
177 /// ```
178 pub const fn empty() -> Self {
179 Self {
180 inner: BodyInner::Once(Bytes::new()),
181 }
182 }
183
184 /// Creates a new frozen body that cannot provide data.
185 ///
186 /// A frozen body represents a body that has been consumed and can no longer
187 /// provide data. This is typically used internally after a body has been
188 /// taken or consumed.
189 ///
190 /// # Examples
191 ///
192 /// ```rust
193 /// use http_kit::Body;
194 ///
195 /// let body = Body::frozen();
196 /// assert!(body.is_frozen());
197 /// ```
198 pub const fn frozen() -> Self {
199 Self {
200 inner: BodyInner::Freeze,
201 }
202 }
203
204 /// Creates a body from an async buffered reader.
205 ///
206 /// This method allows streaming data from any source that implements
207 /// `AsyncBufRead`, such as files, network connections, or in-memory buffers.
208 /// The optional length hint can improve performance for operations that
209 /// benefit from knowing the total size.
210 ///
211 /// # Arguments
212 ///
213 /// * `reader` - Any type implementing `AsyncBufRead + Send + Sync + 'static`
214 /// * `length` - Optional hint about the total number of bytes to read
215 ///
216 /// # Examples
217 ///
218 /// ```rust,no_run
219 /// # #[cfg(feature = "fs")]
220 /// # {
221 /// use http_kit::Body;
222 /// use async_fs::File;
223 /// use futures_lite::io::BufReader;
224 ///
225 /// # async fn example() -> Result<(), http_kit::BodyError> {
226 /// let file = File::open("data.txt").await?;
227 /// let metadata = file.metadata().await?;
228 /// let reader = BufReader::new(file);
229 ///
230 /// let body = Body::from_reader(reader, metadata.len() as usize);
231 /// # Ok(())
232 /// # }
233 /// # }
234 /// ```
235 pub fn from_reader(
236 reader: impl AsyncBufRead + Send + Sync + 'static,
237 length: impl Into<Option<usize>>,
238 ) -> Self {
239 Self {
240 inner: BodyInner::Reader {
241 reader: Box::pin(reader),
242 length: length.into(),
243 },
244 }
245 }
246
247 /// Creates a body from an async stream of data chunks.
248 ///
249 /// This method allows creating a body from any stream that yields
250 /// `Result<T, E>` where `T` can be converted to `Bytes`. This is useful
251 /// for handling data from network sources, databases, or custom generators.
252 ///
253 /// # Type Parameters
254 ///
255 /// * `T` - Data type that can be converted to `Bytes`
256 /// * `E` - Error type that can be converted to a boxed error
257 /// * `S` - Stream type yielding `Result<T, E>`
258 ///
259 /// # Examples
260 ///
261 /// ```rust
262 /// use http_kit::Body;
263 /// use futures_lite::stream;
264 ///
265 /// # async fn example() {
266 /// let data_stream = stream::iter(vec![
267 /// Ok::<_, std::io::Error>("Hello, ".as_bytes()),
268 /// Ok("world!".as_bytes()),
269 /// ]);
270 ///
271 /// let body = Body::from_stream(data_stream);
272 /// # }
273 /// ```
274 pub fn from_stream<T, E, S>(stream: S) -> Self
275 where
276 T: Into<Bytes> + Send + 'static,
277 E: Into<BoxCoreError>,
278 S: Stream<Item = Result<T, E>> + Send + Sync + 'static,
279 {
280 Self {
281 inner: BodyInner::Stream(Box::pin(
282 stream.map(|result| result.map(|data| data.into()).map_err(|error| error.into())),
283 )),
284 }
285 }
286 /// Creates a body from bytes or byte-like data.
287 ///
288 /// This method accepts any type that can be converted to `Bytes`,
289 /// including `String`, `Vec<u8>`, `&str`, `&[u8]`, and `Bytes` itself.
290 /// The conversion is zero-copy when possible.
291 ///
292 /// # Examples
293 ///
294 /// ```rust
295 /// use http_kit::Body;
296 ///
297 /// // From string slice
298 /// let body1 = Body::from_bytes("Hello, world!");
299 ///
300 /// // From String
301 /// let body2 = Body::from_bytes("Hello, world!".to_string());
302 ///
303 /// // From byte vector
304 /// let body3 = Body::from_bytes(vec![72, 101, 108, 108, 111]);
305 /// ```
306 pub fn from_bytes(data: impl Into<Bytes>) -> Self {
307 Self {
308 inner: BodyInner::Once(data.into()),
309 }
310 }
311
312 /// Creates a body by streaming the contents of a file.
313 ///
314 /// This method opens a file and creates a streaming body that reads
315 /// the file contents on demand. The file size is determined automatically
316 /// and used as a length hint for optimization.
317 ///
318 /// # Arguments
319 ///
320 /// * `path` - Path to the file to read
321 ///
322 /// # Errors
323 ///
324 /// Returns an `io::Error` if the file cannot be opened or its metadata
325 /// cannot be read.
326 ///
327 /// # Examples
328 ///
329 /// ```rust,no_run
330 /// # #[cfg(feature = "fs")]
331 /// # {
332 /// use http_kit::Body;
333 ///
334 /// # async fn example() -> Result<(), std::io::Error> {
335 /// let body = Body::from_file("large_document.pdf").await?;
336 /// println!("File body created with {} bytes", body.len().unwrap_or(0));
337 /// # Ok(())
338 /// # }
339 /// # }
340 /// ```
341 #[cfg(feature = "fs")]
342 pub async fn from_file(path: impl AsRef<core::path::Path>) -> Result<Self, core::io::Error> {
343 let file = async_fs::File::open(path).await?;
344 let len = file.metadata().await?.len() as usize;
345 Ok(Self::from_reader(
346 futures_lite::io::BufReader::new(file),
347 len,
348 ))
349 }
350
351 /// Creates a body by serializing an object to JSON.
352 ///
353 /// This method serializes any `Serialize` type to JSON and creates
354 /// a body containing the JSON string. The resulting body will have
355 /// UTF-8 encoded JSON content.
356 ///
357 /// # Arguments
358 ///
359 /// * `value` - Any type implementing `serde::Serialize`
360 ///
361 /// # Errors
362 ///
363 /// Returns `serde_json::Error` if serialization fails.
364 ///
365 /// # Examples
366 ///
367 /// ```rust
368 /// # #[cfg(feature = "json")]
369 /// # {
370 /// use http_kit::Body;
371 /// use serde::Serialize;
372 ///
373 /// #[derive(Serialize)]
374 /// struct User {
375 /// name: String,
376 /// age: u32,
377 /// }
378 ///
379 /// let user = User {
380 /// name: "Alice".to_string(),
381 /// age: 30,
382 /// };
383 ///
384 /// let body = Body::from_json(&user)?;
385 /// # }
386 /// # Ok::<(), serde_json::Error>(())
387 /// ```
388 #[cfg(feature = "json")]
389 pub fn from_json<T: serde::Serialize>(value: T) -> Result<Self, serde_json::Error> {
390 Ok(Self::from_bytes(serde_json::to_string(&value)?))
391 }
392
393 /// Creates a body by serializing an object to URL-encoded form data.
394 ///
395 /// This method serializes any `Serialize` type to `application/x-www-form-urlencoded`
396 /// format, commonly used for HTML form submissions.
397 ///
398 /// # Arguments
399 ///
400 /// * `value` - Any type implementing `serde::Serialize`
401 ///
402 /// # Errors
403 ///
404 /// Returns `serde_urlencoded::ser::Error` if serialization fails.
405 ///
406 /// # Examples
407 ///
408 /// ```rust
409 /// # #[cfg(feature = "form")]
410 /// # {
411 /// use http_kit::Body;
412 /// use serde::Serialize;
413 ///
414 /// #[derive(Serialize)]
415 /// struct LoginForm {
416 /// username: String,
417 /// password: String,
418 /// }
419 ///
420 /// let form = LoginForm {
421 /// username: "user".to_string(),
422 /// password: "pass".to_string(),
423 /// };
424 ///
425 /// let body = Body::from_form(&form)?;
426 /// # }
427 /// # Ok::<(), serde_urlencoded::ser::Error>(())
428 /// ```
429 #[cfg(feature = "form")]
430 pub fn from_form<T: serde::Serialize>(value: T) -> Result<Self, serde_urlencoded::ser::Error> {
431 Ok(Self::from_bytes(serde_urlencoded::to_string(value)?))
432 }
433
434 /// Returns the length of the body in bytes, if known.
435 ///
436 /// This method returns `Some(length)` for in-memory bodies where the size
437 /// is immediately available. For streaming bodies (files, readers, streams),
438 /// it returns `None` since the total size may not be known until the entire
439 /// body is consumed.
440 ///
441 /// The returned length is primarily used for optimizations like setting
442 /// `Content-Length` headers, but should be considered a hint rather than
443 /// a guarantee.
444 ///
445 /// # Examples
446 ///
447 /// ```rust
448 /// use http_kit::Body;
449 ///
450 /// let body = Body::from_bytes("Hello, world!");
451 /// assert_eq!(body.len(), Some(13));
452 ///
453 /// let empty = Body::empty();
454 /// assert_eq!(empty.len(), Some(0));
455 /// ```
456 pub const fn len(&self) -> Option<usize> {
457 if let BodyInner::Once(bytes) = &self.inner {
458 Some(bytes.len())
459 } else {
460 None
461 }
462 }
463
464 /// Returns whether the body is empty, if the length is known.
465 ///
466 /// This method returns `Some(true)` if the body is known to be empty,
467 /// `Some(false)` if the body is known to contain data, and `None` if
468 /// the body length cannot be determined without consuming it.
469 ///
470 /// # Examples
471 ///
472 /// ```rust
473 /// use http_kit::Body;
474 ///
475 /// let empty = Body::empty();
476 /// assert_eq!(empty.is_empty(), Some(true));
477 ///
478 /// let body = Body::from_bytes("data");
479 /// assert_eq!(body.is_empty(), Some(false));
480 /// ```
481 pub const fn is_empty(&self) -> Option<bool> {
482 if let Some(len) = self.len() {
483 if len == 0 {
484 Some(true)
485 } else {
486 Some(false)
487 }
488 } else {
489 None
490 }
491 }
492
493 /// Consumes the body and returns all its data as `Bytes`.
494 ///
495 /// This method reads the entire body into memory and returns it as a
496 /// `Bytes` object. For large bodies or streams, this may consume significant
497 /// memory. For streaming bodies, all data will be read and concatenated.
498 ///
499 /// # Errors
500 ///
501 /// Returns an error if:
502 /// - The body is frozen (already consumed)
503 /// - An I/O error occurs while reading streaming data
504 /// - The underlying stream produces an error
505 ///
506 /// # Examples
507 ///
508 /// ```rust
509 /// use http_kit::Body;
510 ///
511 /// # async fn example() -> Result<(), http_kit::BodyError> {
512 /// let body = Body::from_bytes("Hello, world!");
513 /// let bytes = body.into_bytes().await?;
514 /// assert_eq!(bytes, "Hello, world!");
515 /// # Ok(())
516 /// # }
517 /// ```
518 pub async fn into_bytes(self) -> Result<Bytes, Error> {
519 match self.inner {
520 BodyInner::Once(bytes) => Ok(bytes),
521 BodyInner::Reader { mut reader, length } => {
522 let mut vec = Vec::with_capacity(length.unwrap_or_default());
523 loop {
524 let data = reader.fill_buf().await?;
525 if data.is_empty() {
526 break;
527 } else {
528 vec.extend_from_slice(data);
529 }
530 }
531 Ok(vec.into())
532 }
533
534 BodyInner::Stream(mut body) => {
535 let first = body.try_next().await?.unwrap_or_default();
536 let second = body.try_next().await?;
537 if let Some(second) = second {
538 let remain_size_hint = body.size_hint();
539 let mut vec = Vec::with_capacity(
540 first.len()
541 + second.len()
542 + remain_size_hint.1.unwrap_or(remain_size_hint.0),
543 );
544 vec.extend_from_slice(&first);
545 vec.extend_from_slice(&second);
546 while let Some(data) = body.try_next().await? {
547 vec.extend_from_slice(&data);
548 }
549 Ok(vec.into())
550 } else {
551 Ok(first)
552 }
553 }
554 BodyInner::Freeze => Err(Error::BodyFrozen),
555 }
556 }
557
558 /// Consumes the body and returns its data as a UTF-8 string.
559 ///
560 /// This method reads the entire body into memory and converts it to a
561 /// UTF-8 string, returning a `ByteStr` which provides string-like operations
562 /// while maintaining the underlying byte representation.
563 ///
564 /// # Errors
565 ///
566 /// Returns an error if:
567 /// - The body is frozen (already consumed)
568 /// - An I/O error occurs while reading streaming data
569 /// - The body contains invalid UTF-8 sequences
570 ///
571 /// # Examples
572 ///
573 /// ```rust
574 /// use http_kit::Body;
575 ///
576 /// # async fn example() -> Result<(), http_kit::BodyError> {
577 /// let body = Body::from_bytes("Hello, world!");
578 /// let text = body.into_string().await?;
579 /// assert_eq!(text, "Hello, world!");
580 /// # Ok(())
581 /// # }
582 /// ```
583 pub async fn into_string(self) -> Result<ByteStr, Error> {
584 Ok(ByteStr::from_utf8(self.into_bytes().await?)?)
585 }
586
587 /// Converts the body into an async buffered reader.
588 ///
589 /// This method wraps the body in a type that implements `AsyncBufRead`,
590 /// allowing it to be used anywhere that expects an async reader. This is
591 /// useful for streaming the body data to other async I/O operations.
592 ///
593 /// # Examples
594 ///
595 /// ```rust
596 /// use http_kit::Body;
597 /// use futures_lite::AsyncBufReadExt;
598 ///
599 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
600 /// let body = Body::from_bytes("line1\nline2\nline3");
601 /// let mut reader = body.into_reader();
602 /// let mut line = String::new();
603 /// reader.read_line(&mut line).await?;
604 /// assert_eq!(line, "line1\n");
605 /// # Ok(())
606 /// # }
607 /// ```
608 pub fn into_reader(self) -> impl AsyncBufRead + Send + Sync {
609 IntoAsyncRead::new(self)
610 }
611
612 /// Returns a reference to the body data as bytes.
613 ///
614 /// This method ensures the body data is available as a byte slice and returns
615 /// a reference to it. For streaming bodies, this will consume and buffer all
616 /// data in memory. The body is modified to store the buffered data internally.
617 ///
618 /// # Errors
619 ///
620 /// Returns an error if:
621 /// - The body is frozen (already consumed)
622 /// - An I/O error occurs while reading streaming data
623 ///
624 /// # Examples
625 ///
626 /// ```rust
627 /// use http_kit::Body;
628 ///
629 /// # async fn example() -> Result<(), http_kit::BodyError> {
630 /// let mut body = Body::from_bytes("Hello, world!");
631 /// let bytes = body.as_bytes().await?;
632 /// assert_eq!(bytes, b"Hello, world!");
633 /// # Ok(())
634 /// # }
635 /// ```
636 pub async fn as_bytes(&mut self) -> Result<&[u8], Error> {
637 self.inner = BodyInner::Once(self.take()?.into_bytes().await?);
638 match self.inner {
639 BodyInner::Once(ref bytes) => Ok(bytes),
640 _ => unreachable!(),
641 }
642 }
643
644 /// Returns a reference to the body data as a UTF-8 string slice.
645 ///
646 /// This method ensures the body data is available as a string slice and returns
647 /// a reference to it. For streaming bodies, this will consume and buffer all
648 /// data in memory first. The body is modified to store the buffered data internally.
649 ///
650 /// # Errors
651 ///
652 /// Returns an error if:
653 /// - The body is frozen (already consumed)
654 /// - An I/O error occurs while reading streaming data
655 /// - The body contains invalid UTF-8 sequences
656 ///
657 /// # Examples
658 ///
659 /// ```rust
660 /// use http_kit::Body;
661 ///
662 /// # async fn example() -> Result<(), http_kit::BodyError> {
663 /// let mut body = Body::from_bytes("Hello, world!");
664 /// let text = body.as_str().await?;
665 /// assert_eq!(text, "Hello, world!");
666 /// # Ok(())
667 /// # }
668 /// ```
669 pub async fn as_str(&mut self) -> Result<&str, Error> {
670 let data = self.as_bytes().await?;
671 Ok(core::str::from_utf8(data)?)
672 }
673
674 /// Deserializes the body data as JSON into the specified type.
675 ///
676 /// This method reads the body data and attempts to deserialize it as JSON.
677 /// The deserialization is performed with zero-copy when possible by working
678 /// directly with the buffered byte data.
679 ///
680 /// # Warning
681 ///
682 /// This method does not validate the `Content-Type` header. If you need
683 /// MIME type validation, use `Request::into_json()` or `Response::into_json()`
684 /// instead, which check for the `application/json` content type.
685 ///
686 /// # Errors
687 ///
688 /// Returns an error if:
689 /// - The body is frozen (already consumed)
690 /// - An I/O error occurs while reading streaming data
691 /// - The JSON is malformed or doesn't match the target type
692 ///
693 /// # Examples
694 ///
695 /// ```rust
696 /// # #[cfg(feature = "json")]
697 /// # {
698 /// use http_kit::Body;
699 /// use serde::Deserialize;
700 ///
701 /// #[derive(Deserialize, PartialEq, Debug)]
702 /// struct User {
703 /// name: String,
704 /// age: u32,
705 /// }
706 ///
707 /// # async fn example() -> Result<(), http_kit::BodyError> {
708 /// let json_data = r#"{"name": "Alice", "age": 30}"#;
709 /// let mut body = Body::from_bytes(json_data);
710 /// let user: User = body.into_json().await?;
711 /// assert_eq!(user.name, "Alice");
712 /// # Ok(())
713 /// # }
714 /// # }
715 /// ```
716 #[cfg(feature = "json")]
717 pub async fn into_json<'a, T>(&'a mut self) -> Result<T, Error>
718 where
719 T: serde::Deserialize<'a>,
720 {
721 Ok(serde_json::from_slice(self.as_bytes().await?)?)
722 }
723
724 /// Deserializes the body data as URL-encoded form data into the specified type.
725 ///
726 /// This method reads the body data and attempts to deserialize it as
727 /// `application/x-www-form-urlencoded` data. The deserialization is performed
728 /// with zero-copy when possible by working directly with the buffered byte data.
729 ///
730 /// # Warning
731 ///
732 /// This method does not validate the `Content-Type` header. If you need
733 /// MIME type validation, use `Request::into_form()` or `Response::into_form()`
734 /// instead, which check for the `application/x-www-form-urlencoded` content type.
735 ///
736 /// # Errors
737 ///
738 /// Returns an error if:
739 /// - The body is frozen (already consumed)
740 /// - An I/O error occurs while reading streaming data
741 /// - The form data is malformed or doesn't match the target type
742 ///
743 /// # Examples
744 ///
745 /// ```rust
746 /// # #[cfg(feature = "form")]
747 /// # {
748 /// use http_kit::Body;
749 /// use serde::Deserialize;
750 ///
751 /// #[derive(Deserialize, PartialEq, Debug)]
752 /// struct LoginForm {
753 /// username: String,
754 /// password: String,
755 /// }
756 ///
757 /// # async fn example() -> Result<(), http_kit::BodyError> {
758 /// let form_data = "username=alice&password=secret123";
759 /// let mut body = Body::from_bytes(form_data);
760 /// let form: LoginForm = body.into_form().await?;
761 /// assert_eq!(form.username, "alice");
762 /// # Ok(())
763 /// # }
764 /// # }
765 /// ```
766 #[cfg(feature = "form")]
767 pub async fn into_form<'a, T>(&'a mut self) -> Result<T, Error>
768 where
769 T: serde::Deserialize<'a>,
770 {
771 Ok(serde_urlencoded::from_bytes(self.as_bytes().await?)?)
772 }
773
774 /// Replaces this body with a new body and returns the old body.
775 ///
776 /// This method swaps the current body with the provided body, returning
777 /// the original body value. This can be useful for chaining operations
778 /// or temporarily substituting body content.
779 ///
780 /// # Examples
781 ///
782 /// ```rust
783 /// use http_kit::Body;
784 ///
785 /// let mut body = Body::from_bytes("original");
786 /// let old_body = body.replace(Body::from_bytes("replacement"));
787 ///
788 /// // `body` now contains "replacement"
789 /// // `old_body` contains "original"
790 /// ```
791 pub fn replace(&mut self, body: Body) -> Body {
792 replace(self, body)
793 }
794
795 /// Swaps the contents of this body with another body.
796 ///
797 /// This method exchanges the contents of two bodies, provided that this
798 /// body is not frozen. If the body is frozen (already consumed), the
799 /// operation fails and returns an error.
800 ///
801 /// # Errors
802 ///
803 /// Returns `BodyFrozen` if this body has been frozen/consumed.
804 ///
805 /// # Examples
806 ///
807 /// ```rust
808 /// use http_kit::Body;
809 ///
810 /// let mut body1 = Body::from_bytes("first");
811 /// let mut body2 = Body::from_bytes("second");
812 ///
813 /// body1.swap(&mut body2)?;
814 ///
815 /// // Now body1 contains "second" and body2 contains "first"
816 /// # Ok::<(), http_kit::BodyError>(())
817 /// ```
818 pub fn swap(&mut self, body: &mut Body) -> Result<(), BodyFrozen> {
819 if self.is_frozen() {
820 Err(BodyFrozen::new())
821 } else {
822 swap(self, body);
823 Ok(())
824 }
825 }
826
827 /// Consumes and takes the body, leaving a frozen body in its place.
828 ///
829 /// This method extracts the body content and replaces it with a frozen
830 /// (unusable) body. This is useful when you need to move the body to
831 /// another location while ensuring the original cannot be used again.
832 ///
833 /// # Errors
834 ///
835 /// Returns `BodyFrozen` if the body is already frozen.
836 ///
837 /// # Examples
838 ///
839 /// ```rust
840 /// use http_kit::Body;
841 ///
842 /// let mut body = Body::from_bytes("Hello, world!");
843 /// let taken_body = body.take()?;
844 ///
845 /// // `taken_body` contains the original data
846 /// // `body` is now frozen and cannot be used
847 /// assert!(body.is_frozen());
848 /// # Ok::<(), http_kit::BodyError>(())
849 /// ```
850 pub fn take(&mut self) -> Result<Self, BodyFrozen> {
851 if self.is_frozen() {
852 Err(BodyFrozen::new())
853 } else {
854 Ok(self.replace(Self::frozen()))
855 }
856 }
857
858 /// Returns `true` if the body is frozen (consumed), `false` otherwise.
859 ///
860 /// A frozen body is one that has been consumed by operations like `take()`
861 /// and can no longer provide data. This is different from an empty body,
862 /// which still has a valid state but contains no data.
863 ///
864 /// # Examples
865 ///
866 /// ```rust
867 /// use http_kit::Body;
868 ///
869 /// let mut body = Body::from_bytes("data");
870 /// assert!(!body.is_frozen());
871 ///
872 /// let _taken = body.take().unwrap();
873 /// assert!(body.is_frozen());
874 ///
875 /// let frozen = Body::frozen();
876 /// assert!(frozen.is_frozen());
877 /// ```
878 pub const fn is_frozen(&self) -> bool {
879 matches!(self.inner, BodyInner::Freeze)
880 }
881
882 /// Freezes the body, making it unusable and dropping its content.
883 ///
884 /// This method converts the body to a frozen state, discarding any data
885 /// it contained. After freezing, the body cannot be used for any operations
886 /// and will return errors if accessed.
887 ///
888 /// # Examples
889 ///
890 /// ```rust
891 /// use http_kit::Body;
892 ///
893 /// let mut body = Body::from_bytes("Hello, world!");
894 /// body.freeze();
895 ///
896 /// assert!(body.is_frozen());
897 /// // Any further operations on `body` will fail
898 /// ```
899 pub fn freeze(&mut self) {
900 self.replace(Self::frozen());
901 }
902}
903
904impl Default for Body {
905 fn default() -> Self {
906 Self::empty()
907 }
908}
909
910impl Stream for Body {
911 type Item = Result<Bytes, BoxCoreError>;
912
913 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
914 match &mut self.inner {
915 BodyInner::Once(bytes) => {
916 if bytes.is_empty() {
917 Poll::Ready(None)
918 } else {
919 Poll::Ready(Some(Ok(take(bytes))))
920 }
921 }
922 BodyInner::Reader { reader, length } => {
923 let data = ready!(reader.as_mut().poll_fill_buf(cx))?;
924 if data.is_empty() {
925 return Poll::Ready(None);
926 }
927 let data = Bytes::copy_from_slice(data);
928 reader.as_mut().consume(data.len());
929 if let Some(length) = length {
930 *length -= data.len();
931 }
932 Poll::Ready(Some(Ok(data)))
933 }
934 BodyInner::Stream(stream) => stream.as_mut().poll_next(cx),
935 BodyInner::Freeze => Poll::Ready(Some(Err(Error::BodyFrozen.into()))),
936 }
937 }
938
939 fn size_hint(&self) -> (usize, Option<usize>) {
940 match &self.inner {
941 BodyInner::Once(bytes) => (bytes.len(), Some(bytes.len())),
942 BodyInner::Reader { length, .. } => (0, *length),
943 BodyInner::Stream(body) => body.size_hint(),
944 BodyInner::Freeze => (0, None),
945 }
946 }
947}
948
949#[cfg(feature = "http_body")]
950impl HttpBody for Body {
951 type Data = Bytes;
952
953 type Error = BoxCoreError;
954
955 fn poll_frame(
956 self: Pin<&mut Self>,
957 cx: &mut Context<'_>,
958 ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
959 self.poll_next(cx)
960 .map(|opt| opt.map(|result| result.map(http_body::Frame::data)))
961 }
962}