use std::convert::Infallible;
use axum::error_handling::HandleError;
use bytes::Bytes;
use http::{Request, Response, StatusCode};
use tower::util::BoxCloneService;
use tower::{Service, ServiceExt};
use tower_http::map_response_body::MapResponseBody;
use crate::config::TargetConfig;
use crate::layer::layers_config;
use crate::service::service_config;
pub type ReqBody = axum::body::BoxBody;
pub type ResBody = axum::body::BoxBody;
pub type Target<A = ReqBody, B = ResBody> = BoxCloneService<Request<A>, Response<B>, Infallible>;
async fn handle_error(err: impl Into<anyhow::Error>) -> (StatusCode, String) {
let err = err.into();
log::warn!("unhandled error: {:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong"),
)
}
pub trait IntoTarget {
fn into_target(self) -> Target;
}
impl<S, B> IntoTarget for S
where
S: Service<Request<ReqBody>, Response = Response<B>> + Clone + Sized + Send + 'static,
B: http_body::Body<Data = Bytes> + Send + 'static,
B::Error: Into<axum::BoxError>,
S::Error: Into<anyhow::Error> + Send,
S::Future: Send + 'static,
{
fn into_target(self) -> Target {
let s = self;
let s = MapResponseBody::new(s, axum::body::boxed);
let s = HandleError::<_, _, ()>::new(s, handle_error);
ServiceExt::<Request<ReqBody>>::boxed_clone(s)
}
}
pub fn target_config(cfg: TargetConfig) -> anyhow::Result<Target> {
let mut service = service_config(cfg.service)?;
service = layers_config(service, cfg.layers)?;
Ok(service)
}