use std::convert::Infallible;
use std::pin::Pin;
use std::task::{Context, Poll};
use axum::body::BoxBody;
use bytes::Bytes;
use http::{HeaderMap, Request, Response};
use http_body::Body;
use tower::layer::layer_fn;
use tower::{Layer, Service, ServiceExt};
use crate::layer::ApplyLayer;
use crate::target::{IntoTarget, ReqBody, Target};
#[derive(Default)]
pub enum AutomaticBody {
#[default]
Automatic,
Boxed(BoxBody),
}
impl Body for AutomaticBody {
type Data = Bytes;
type Error = axum::Error;
fn poll_data(
self: Pin<&mut Self>,
_: &mut Context,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
panic!()
}
fn poll_trailers(
self: Pin<&mut Self>,
_: &mut Context,
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
panic!()
}
}
pub fn to_automatic(inner: impl IntoTarget) -> Target<ReqBody, AutomaticBody> {
inner
.into_target()
.map_response(|resp| {
let (parts, body) = resp.into_parts();
Response::from_parts(parts, AutomaticBody::Boxed(body))
})
.boxed_clone()
}
pub fn from_automatic(inner: Target<ReqBody, AutomaticBody>) -> impl IntoTarget {
inner.map_response(|resp| {
let (parts, body) = resp.into_parts();
let new_body = match body {
AutomaticBody::Automatic => {
let mut text = parts.status.as_u16().to_string();
if let Some(reason) = parts.status.canonical_reason() {
text = format!("{} {}", text, reason);
}
axum::body::boxed(hyper::Body::from(text))
}
AutomaticBody::Boxed(b) => b,
};
Response::from_parts(parts, new_body)
})
}
pub fn add_automatic_body<L, S>(inner: L) -> impl ApplyLayer
where
L: Layer<Target<ReqBody, AutomaticBody>, Service = S>,
S: Service<Request<ReqBody>, Response = Response<AutomaticBody>, Error = Infallible>
+ Clone
+ Sized
+ Send
+ 'static,
S::Future: Send + 'static,
{
layer_fn(move |s| from_automatic(inner.layer(to_automatic(s)).boxed_clone()))
}