use bytes::Bytes;
use futures::{Stream, StreamExt, TryStreamExt};
use std::pin::Pin;
use super::error::FetchError;
pub type ByteStream = Pin<Box<dyn Stream<Item = Result<Bytes, FetchError>> + Send>>;
#[must_use]
pub fn channel_stream(rx: tokio::sync::mpsc::Receiver<Result<Vec<u8>, String>>) -> ByteStream {
futures::stream::unfold(rx, |mut rx| async move {
let chunk = rx.recv().await?;
let item = match chunk {
Ok(bytes) => Ok(Bytes::from(bytes)),
Err(message) => Err(FetchError::Body(message)),
};
Some((item, rx))
})
.boxed()
}
pub(crate) enum RequestPayload {
Empty,
Bytes(Bytes),
Stream(ByteStream),
Invalid,
}
pub struct Body(Inner);
enum Inner {
Empty,
Bytes(Bytes),
Stream(ByteStream),
Response(Box<reqwest::Response>),
}
impl Body {
#[must_use]
pub fn empty() -> Self {
Self(Inner::Empty)
}
#[must_use]
pub fn from_bytes(bytes: impl Into<Bytes>) -> Self {
Self(Inner::Bytes(bytes.into()))
}
#[must_use]
pub fn from_stream(stream: ByteStream) -> Self {
Self(Inner::Stream(stream))
}
pub(crate) fn from_response(response: reqwest::Response) -> Self {
Self(Inner::Response(Box::new(response)))
}
pub(crate) fn into_request_payload(self) -> RequestPayload {
match self.0 {
Inner::Empty => RequestPayload::Empty,
Inner::Bytes(b) => RequestPayload::Bytes(b),
Inner::Stream(s) => RequestPayload::Stream(s),
Inner::Response(_) => RequestPayload::Invalid,
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
matches!(self.0, Inner::Empty)
}
pub async fn collect(self) -> Result<Bytes, FetchError> {
match self.0 {
Inner::Empty => Ok(Bytes::new()),
Inner::Bytes(b) => Ok(b),
Inner::Response(resp) => resp
.bytes()
.await
.map_err(|e| FetchError::Body(format!("read response body: {e}"))),
Inner::Stream(mut s) => {
let mut buf = Vec::new();
while let Some(chunk) = s.next().await {
buf.extend_from_slice(&chunk?);
}
Ok(Bytes::from(buf))
},
}
}
#[must_use]
pub fn into_stream(self) -> ByteStream {
match self.0 {
Inner::Empty => futures::stream::empty().boxed(),
Inner::Bytes(b) => futures::stream::once(async move { Ok(b) }).boxed(),
Inner::Stream(s) => s,
Inner::Response(resp) => resp
.bytes_stream()
.map_err(|e| FetchError::Body(format!("read response body: {e}")))
.boxed(),
}
}
}
impl std::fmt::Debug for Body {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
Inner::Empty => f.write_str("Body::Empty"),
Inner::Bytes(b) => write!(f, "Body::Bytes({} bytes)", b.len()),
Inner::Stream(_) => f.write_str("Body::Stream"),
Inner::Response(_) => f.write_str("Body::Response"),
}
}
}