//! [`MessageBody`] trait and foreign implementations.
use std::{
    convert::Infallible,
    mem,
    pin::Pin,
    task::{Context, Poll},
};
use bytes::{Bytes, BytesMut};
use futures_core::ready;
use pin_project_lite::pin_project;
use super::BodySize;
/// An interface for response bodies.
pub trait MessageBody {
    type Error;
    /// Body size hint.
    fn size(&self) -> BodySize;
    /// Attempt to pull out the next chunk of body bytes.
    fn poll_next(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll>>;
}
impl MessageBody for () {
    type Error = Infallible;
    fn size(&self) -> BodySize {
        BodySize::Sized(0)
    }
    fn poll_next(
        self: Pin<&mut Self>,
        _: &mut Context<'_>,
    ) -> Poll >> {
        Poll::Ready(None)
    }
}
impl MessageBody for Box
where
    B: MessageBody + Unpin,
{
    type Error = B::Error;
    fn size(&self) -> BodySize {
        self.as_ref().size()
    }
    fn poll_next(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll>> {
        Pin::new(self.get_mut().as_mut()).poll_next(cx)
    }
}
impl MessageBody for Pin>
where
    B: MessageBody,
{
    type Error = B::Error;
    fn size(&self) -> BodySize {
        self.as_ref().size()
    }
    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll>> {
        self.as_mut().poll_next(cx)
    }
}
impl MessageBody for Bytes {
    type Error = Infallible;
    fn size(&self) -> BodySize {
        BodySize::Sized(self.len() as u64)
    }
    fn poll_next(
        self: Pin<&mut Self>,
        _: &mut Context<'_>,
    ) -> Poll >> {
        if self.is_empty() {
            Poll::Ready(None)
        } else {
            Poll::Ready(Some(Ok(mem::take(self.get_mut()))))
        }
    }
}
impl MessageBody for BytesMut {
    type Error = Infallible;
    fn size(&self) -> BodySize {
        BodySize::Sized(self.len() as u64)
    }
    fn poll_next(
        self: Pin<&mut Self>,
        _: &mut Context<'_>,
    ) -> Poll >> {
        if self.is_empty() {
            Poll::Ready(None)
        } else {
            Poll::Ready(Some(Ok(mem::take(self.get_mut()).freeze())))
        }
    }
}
impl MessageBody for &'static str {
    type Error = Infallible;
    fn size(&self) -> BodySize {
        BodySize::Sized(self.len() as u64)
    }
    fn poll_next(
        self: Pin<&mut Self>,
        _: &mut Context<'_>,
    ) -> Poll >> {
        if self.is_empty() {
            Poll::Ready(None)
        } else {
            Poll::Ready(Some(Ok(Bytes::from_static(
                mem::take(self.get_mut()).as_ref(),
            ))))
        }
    }
}
impl MessageBody for Vec {
    type Error = Infallible;
    fn size(&self) -> BodySize {
        BodySize::Sized(self.len() as u64)
    }
    fn poll_next(
        self: Pin<&mut Self>,
        _: &mut Context<'_>,
    ) -> Poll>> {
        if self.is_empty() {
            Poll::Ready(None)
        } else {
            Poll::Ready(Some(Ok(Bytes::from(mem::take(self.get_mut())))))
        }
    }
}
impl MessageBody for String {
    type Error = Infallible;
    fn size(&self) -> BodySize {
        BodySize::Sized(self.len() as u64)
    }
    fn poll_next(
        self: Pin<&mut Self>,
        _: &mut Context<'_>,
    ) -> Poll >> {
        if self.is_empty() {
            Poll::Ready(None)
        } else {
            Poll::Ready(Some(Ok(Bytes::from(
                mem::take(self.get_mut()).into_bytes(),
            ))))
        }
    }
}
pin_project! {
    pub(crate) struct MessageBodyMapErr {
        #[pin]
        body: B,
        mapper: Option,
    }
}
impl MessageBodyMapErr
where
    B: MessageBody,
    F: FnOnce(B::Error) -> E,
{
    pub(crate) fn new(body: B, mapper: F) -> Self {
        Self {
            body,
            mapper: Some(mapper),
        }
    }
}
impl MessageBody for MessageBodyMapErr
where
    B: MessageBody,
    F: FnOnce(B::Error) -> E,
{
    type Error = E;
    fn size(&self) -> BodySize {
        self.body.size()
    }
    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll>> {
        let this = self.as_mut().project();
        match ready!(this.body.poll_next(cx)) {
            Some(Err(err)) => {
                let f = self.as_mut().project().mapper.take().unwrap();
                let mapped_err = (f)(err);
                Poll::Ready(Some(Err(mapped_err)))
            }
            Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
            None => Poll::Ready(None),
        }
    }
}