use bytes::Bytes;
use futures_lite::{future::block_on, io::{AsyncRead, AsyncReadExt}};
use std::{
fmt,
io::{self, Cursor, Read},
pin::Pin,
str,
task::{Context, Poll},
};
macro_rules! match_type {
{
$(
<$name:ident as $T:ty> => $branch:expr,
)*
$defaultName:ident => $defaultBranch:expr,
} => {{
match () {
$(
_ if ::std::any::Any::type_id(&$name) == ::std::any::TypeId::of::<$T>() => {
#[allow(unsafe_code)]
let $name: $T = unsafe {
::std::mem::transmute_copy::<_, $T>(&::std::mem::ManuallyDrop::new($name))
};
$branch
}
)*
_ => $defaultBranch,
}
}};
}
pub struct Body(Inner);
enum Inner {
Empty,
Bytes(Cursor<Bytes>),
AsyncRead(Pin<Box<dyn AsyncRead + Send + Sync>>, Option<u64>),
}
impl Body {
pub const fn empty() -> Self {
Body(Inner::Empty)
}
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
bytes.as_ref().to_vec().into()
}
pub fn from_maybe_shared(bytes: impl AsRef<[u8]> + 'static) -> Self {
Body(Inner::Bytes(Cursor::new(match_type! {
<bytes as Bytes> => bytes,
<bytes as &'static [u8]> => Bytes::from_static(bytes),
bytes => bytes.as_ref().to_vec().into(),
})))
}
pub fn from_reader(read: impl AsyncRead + Send + Sync + 'static) -> Self {
Body(Inner::AsyncRead(Box::pin(read), None))
}
pub fn from_reader_sized(read: impl AsyncRead + Send + Sync + 'static, length: u64) -> Self {
Body(Inner::AsyncRead(Box::pin(read), Some(length)))
}
pub fn is_empty(&self) -> bool {
match self.0 {
Inner::Empty => true,
_ => false,
}
}
pub fn len(&self) -> Option<u64> {
match &self.0 {
Inner::Empty => Some(0),
Inner::Bytes(bytes) => Some(bytes.get_ref().len() as u64),
Inner::AsyncRead(_, len) => *len,
}
}
pub fn reset(&mut self) -> bool {
match &mut self.0 {
Inner::Empty => true,
Inner::Bytes(cursor) => {
cursor.set_position(0);
true
}
Inner::AsyncRead(_, _) => false,
}
}
}
impl Read for Body {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match &mut self.0 {
Inner::Empty => Ok(0),
Inner::Bytes(cursor) => cursor.read(buf),
Inner::AsyncRead(reader, _) => block_on(reader.read(buf)),
}
}
}
impl AsyncRead for Body {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
match &mut self.0 {
Inner::Empty => Poll::Ready(Ok(0)),
Inner::Bytes(cursor) => Poll::Ready(cursor.read(buf)),
Inner::AsyncRead(read, _) => AsyncRead::poll_read(read.as_mut(), cx, buf),
}
}
}
impl Default for Body {
fn default() -> Self {
Self::empty()
}
}
impl From<()> for Body {
fn from(_: ()) -> Self {
Self::empty()
}
}
impl From<Vec<u8>> for Body {
fn from(body: Vec<u8>) -> Self {
Self::from_maybe_shared(Bytes::from(body))
}
}
impl From<&'static [u8]> for Body {
fn from(body: &'static [u8]) -> Self {
Self::from_maybe_shared(Bytes::from(body))
}
}
impl From<String> for Body {
fn from(body: String) -> Self {
body.into_bytes().into()
}
}
impl From<&'static str> for Body {
fn from(body: &'static str) -> Self {
body.as_bytes().into()
}
}
impl<T: Into<Body>> From<Option<T>> for Body {
fn from(body: Option<T>) -> Self {
match body {
Some(body) => body.into(),
None => Self::empty(),
}
}
}
impl fmt::Debug for Body {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.len() {
Some(len) => write!(f, "Body({})", len),
None => write!(f, "Body(?)"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::assert_impl_all!(Body: Send, Sync);
#[test]
fn empty_body() {
let body = Body::empty();
assert!(body.is_empty());
assert_eq!(body.len(), Some(0));
}
#[test]
fn zero_length_body() {
let body = Body::from_bytes(vec![]);
assert!(!body.is_empty());
assert_eq!(body.len(), Some(0));
}
}