#![warn(missing_docs)]
use comprehensive::v1::{AssemblyRuntime, Resource, resource};
use comprehensive_traits::http_diag::{Body, DiagRequest, HttpDiagHandler, HttpDiagHandlerInstaller};
use pin_project_lite::pin_project;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tower::util::BoxCloneSyncService;
use tower_service::Service;
use warm_channels::ChannelDiagService;
pub use warm_channels;
#[doc(hidden)]
#[derive(clap::Args, Debug)]
#[group(skip)]
pub struct WarmChannelsDiagArgs {
#[arg(long, default_value = "/debug/channels")]
warm_channels_diag_path: String,
}
pub struct WarmChannelsDiag(WarmChannelsDiagArgs);
#[resource]
#[export(dyn HttpDiagHandler)]
impl Resource for WarmChannelsDiag {
fn new(
_: comprehensive::NoDependencies,
a: WarmChannelsDiagArgs,
_: &mut AssemblyRuntime<'_>,
) -> Result<Arc<Self>, std::convert::Infallible> {
Ok(Arc::new(Self(a)))
}
}
pin_project! {
struct MapResponseBodyFuture<F> {
#[pin] inner: F
}
}
impl<F, B, E> Future for MapResponseBodyFuture<F>
where
F: Future<Output = Result<http::Response<B>, E>>,
B: Into<Body>,
{
type Output = Result<http::Response<Body>, E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.project().inner.poll(cx) {
Poll::Ready(Ok(r)) => {
let (parts, body) = r.into_parts();
Poll::Ready(Ok(http::Response::from_parts(parts, body.into())))
}
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => Poll::Pending,
}
}
}
#[derive(Clone)]
struct WarmChannelsDiagService<T>(T);
impl<T, B> Service<DiagRequest> for WarmChannelsDiagService<T>
where
T: Service<http::Request<Body>, Response = http::Response<B>>,
B: Into<Body>,
{
type Response = http::Response<Body>;
type Error = <T as Service<http::Request<Body>>>::Error;
type Future = MapResponseBodyFuture<<T as Service<http::Request<Body>>>::Future>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Service::poll_ready(&mut self.0, cx)
}
fn call(&mut self, request: DiagRequest) -> Self::Future {
let original_uri = request.get_original_uri().cloned();
let mut req = request.into_inner();
if let Some(uri) = original_uri {
req.extensions_mut().insert(warm_channels::OriginalUri(uri));
}
MapResponseBodyFuture {
inner: Service::call(&mut self.0, req),
}
}
}
impl HttpDiagHandler for WarmChannelsDiag {
fn install_handlers(self: Arc<Self>, installer: &mut dyn HttpDiagHandlerInstaller) {
if !self.0.warm_channels_diag_path.is_empty() {
installer.nest_diag_service(
&self.0.warm_channels_diag_path,
BoxCloneSyncService::new(WarmChannelsDiagService(ChannelDiagService::default())),
);
}
}
}