Documentation
use hyper::body::{Body, Buf, Frame, SizeHint};
use std::error::Error;
use std::pin::Pin;
use std::task::{Context, Poll};

/// 擦除掉具体的body和error
trait ErasedBody: Send + Sync {
    fn poll_frame(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Result<Frame<Box<dyn Buf + Send>>, Box<dyn Error + Send + Sync>>>>;

    fn is_end_stream(&self) -> bool;

    fn size_hint(&self) -> SizeHint;
}

/// 为所有满足条件的 Body 实现 ErasedBody
impl<B> ErasedBody for B
where
    B: Body + Send + Sync + 'static,
    B::Data: Buf + Send,
    B::Error: Into<Box<dyn Error + Send + Sync>>,
{
    fn poll_frame(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Result<Frame<Box<dyn Buf + Send>>, Box<dyn Error + Send + Sync>>>> {
        match <Self as Body>::poll_frame(self, cx) {
            Poll::Pending => Poll::Pending,
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e.into()))),
            Poll::Ready(Some(Ok(frame))) => {
                let mapped = frame.map_data(|d| Box::new(d) as Box<dyn Buf + Send>);
                Poll::Ready(Some(Ok(mapped)))
            }
        }
    }

    fn is_end_stream(&self) -> bool {
        <Self as Body>::is_end_stream(self)
    }

    fn size_hint(&self) -> SizeHint {
        <Self as Body>::size_hint(self)
    }
}

/// 通用型http body
pub struct VariantBody {
    inner: Pin<Box<dyn ErasedBody + 'static>>,
}

impl VariantBody {
    pub fn new<B>(body: B) -> Self
    where
        B: Body + Send + Sync + 'static,
        B::Data: Buf + Send,
        B::Error: Into<Box<dyn Error + Send + Sync>>,
    {
        Self {
            inner: Box::pin(body),
        }
    }
}

/// 为VariantBody实现body trait
impl Body for VariantBody {
    type Data = Box<dyn Buf + Send>;
    type Error = Box<dyn Error + Send + Sync>;

    fn poll_frame(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
        self.get_mut().inner.as_mut().poll_frame(cx)
    }

    fn is_end_stream(&self) -> bool {
        self.inner.is_end_stream()
    }

    fn size_hint(&self) -> SizeHint {
        self.inner.size_hint()
    }
}

pub fn variant_body<B>(body: B) -> VariantBody
where
    B: Body + Send + Sync + 'static,
    B::Data: Buf + Send,
    B::Error: Into<Box<dyn Error + Send + Sync>>,
{
    VariantBody::new(body)
}