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}