1use crate::error::{Error, Result};
5use bytes::{Bytes, BytesMut};
6use futures_util::stream::{Stream, StreamExt};
7use std::pin::Pin;
8
9pub enum Body {
16 Bytes(Bytes),
18 Stream(Pin<Box<dyn Stream<Item = Result<Bytes>> + Send + 'static>>),
20}
21
22impl Body {
23 pub fn empty() -> Self {
25 Body::Bytes(Bytes::new())
26 }
27
28 pub fn from_stream<S, E>(stream: S) -> Self
31 where
32 S: Stream<Item = std::result::Result<Bytes, E>> + Send + 'static,
33 E: std::fmt::Display,
34 {
35 let mapped =
36 stream.map(|chunk| chunk.map_err(|e| Error::internal(format!("body stream: {e}"))));
37 Body::Stream(Box::pin(mapped))
38 }
39
40 pub fn as_bytes(&self) -> Option<&Bytes> {
43 match self {
44 Body::Bytes(b) => Some(b),
45 Body::Stream(_) => None,
46 }
47 }
48
49 pub fn as_slice(&self) -> Option<&[u8]> {
51 self.as_bytes().map(|b| b.as_ref())
52 }
53
54 pub fn is_empty(&self) -> bool {
57 matches!(self, Body::Bytes(b) if b.is_empty())
58 }
59
60 pub async fn into_bytes(self) -> Result<Bytes> {
63 match self {
64 Body::Bytes(b) => Ok(b),
65 Body::Stream(mut s) => {
66 let mut buf = BytesMut::new();
67 while let Some(chunk) = s.next().await {
68 buf.extend_from_slice(&chunk?);
69 }
70 Ok(buf.freeze())
71 }
72 }
73 }
74}
75
76impl Default for Body {
77 fn default() -> Self {
78 Body::empty()
79 }
80}
81
82impl From<Bytes> for Body {
83 fn from(b: Bytes) -> Self {
84 Body::Bytes(b)
85 }
86}
87impl From<Vec<u8>> for Body {
88 fn from(b: Vec<u8>) -> Self {
89 Body::Bytes(Bytes::from(b))
90 }
91}
92impl From<String> for Body {
93 fn from(s: String) -> Self {
94 Body::Bytes(Bytes::from(s))
95 }
96}
97impl From<&'static str> for Body {
98 fn from(s: &'static str) -> Self {
99 Body::Bytes(Bytes::from_static(s.as_bytes()))
100 }
101}
102
103impl PartialEq<Bytes> for Body {
106 fn eq(&self, other: &Bytes) -> bool {
107 matches!(self, Body::Bytes(b) if b == other)
108 }
109}
110
111impl std::fmt::Debug for Body {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 Body::Bytes(b) => f.debug_tuple("Body::Bytes").field(b).finish(),
115 Body::Stream(_) => f.write_str("Body::Stream(..)"),
116 }
117 }
118}
119
120impl http_body::Body for Body {
124 type Data = Bytes;
125 type Error = Error;
126
127 fn poll_frame(
128 mut self: Pin<&mut Self>,
129 cx: &mut std::task::Context<'_>,
130 ) -> std::task::Poll<Option<std::result::Result<http_body::Frame<Bytes>, Error>>> {
131 match &mut *self {
132 Body::Bytes(bytes) => {
133 if bytes.is_empty() {
134 std::task::Poll::Ready(None)
135 } else {
136 let out = std::mem::take(bytes);
139 std::task::Poll::Ready(Some(Ok(http_body::Frame::data(out))))
140 }
141 }
142 Body::Stream(stream) => match stream.as_mut().poll_next(cx) {
143 std::task::Poll::Ready(Some(Ok(chunk))) => {
144 std::task::Poll::Ready(Some(Ok(http_body::Frame::data(chunk))))
145 }
146 std::task::Poll::Ready(Some(Err(e))) => std::task::Poll::Ready(Some(Err(e))),
147 std::task::Poll::Ready(None) => std::task::Poll::Ready(None),
148 std::task::Poll::Pending => std::task::Poll::Pending,
149 },
150 }
151 }
152
153 fn size_hint(&self) -> http_body::SizeHint {
154 match self {
155 Body::Bytes(b) => http_body::SizeHint::with_exact(b.len() as u64),
157 Body::Stream(_) => http_body::SizeHint::default(),
159 }
160 }
161
162 fn is_end_stream(&self) -> bool {
163 match self {
164 Body::Bytes(b) => b.is_empty(),
165 Body::Stream(_) => false,
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[tokio::test]
175 async fn buffered_round_trips() {
176 let body = Body::from(Bytes::from("hello"));
177 assert_eq!(body.as_slice(), Some(&b"hello"[..]));
178 assert!(!body.is_empty());
179 assert_eq!(body.into_bytes().await.unwrap(), Bytes::from("hello"));
180 }
181
182 #[tokio::test]
183 async fn empty_is_empty() {
184 assert!(Body::empty().is_empty());
185 assert_eq!(Body::empty().into_bytes().await.unwrap(), Bytes::new());
186 }
187
188 #[tokio::test]
189 async fn stream_collects_and_has_no_bytes_view() {
190 let chunks = futures_util::stream::iter(vec![
191 Ok::<_, std::io::Error>(Bytes::from("ab")),
192 Ok(Bytes::from("cd")),
193 ]);
194 let body = Body::from_stream(chunks);
195 assert!(body.as_bytes().is_none()); assert!(!body.is_empty());
197 assert_eq!(body.into_bytes().await.unwrap(), Bytes::from("abcd"));
198 }
199
200 #[test]
201 #[allow(clippy::cmp_owned)] fn partial_eq_bytes() {
203 assert!(Body::from("x".to_string()) == Bytes::from("x"));
204 let s = Body::from_stream(futures_util::stream::iter(vec![Ok::<_, std::io::Error>(
205 Bytes::new(),
206 )]));
207 assert!(!(s == Bytes::new())); }
209}