use std::fmt;
use std::pin::Pin;
use std::task::{Context, Poll};
use pin_project_lite::pin_project;
use rama_http_types::body::http_body as rama_body;
use super::TryIntoRamaHttp as _;
fn size_hint_to_rama(hint: &http_body::SizeHint) -> rama_body::SizeHint {
let mut out = rama_body::SizeHint::new();
out.set_lower(hint.lower());
if let Some(upper) = hint.upper() {
out.set_upper(upper);
}
out
}
pin_project! {
pub struct RamaBody<B> {
#[pin]
inner: B,
}
}
impl<B> RamaBody<B> {
pub const fn new(inner: B) -> Self {
Self { inner }
}
pub fn into_inner(self) -> B {
self.inner
}
}
impl<B: http_body::Body> rama_body::Body for RamaBody<B> {
type Data = B::Data;
type Error = RamaBodyError<B::Error>;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<rama_body::Frame<Self::Data>, Self::Error>>> {
match self.project().inner.poll_frame(cx) {
Poll::Ready(Some(Ok(frame))) => match frame.into_data() {
Ok(data) => Poll::Ready(Some(Ok(rama_body::Frame::data(data)))),
Err(frame) => match frame.into_trailers() {
Ok(trailers) => Poll::Ready(Some(
trailers
.try_into_rama_http()
.map(rama_body::Frame::trailers)
.map_err(RamaBodyError::Trailers),
)),
Err(_) => Poll::Ready(None),
},
},
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(RamaBodyError::Body(err)))),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> rama_body::SizeHint {
size_hint_to_rama(&self.inner.size_hint())
}
}
#[derive(Debug)]
pub enum RamaBodyError<E> {
Body(E),
Trailers(rama_http_types::Error),
}
impl<E: fmt::Display> fmt::Display for RamaBodyError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Body(err) => write!(f, "http body error: {err}"),
Self::Trailers(err) => write!(f, "trailer conversion to rama failed: {err}"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for RamaBodyError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Body(err) => Some(err),
Self::Trailers(err) => Some(err),
}
}
}