//! Trait aliases for [`Future`] and [`Service`].

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

use futures_core::TryFuture;
use http::{Request, Response};
use http_body::Body;
use tower_service::Service;

#[cfg_attr(not(feature = "__docs"), allow(intra_doc_link_resolution_failure))]
/// An HTTP client (like [`hyper::Client`]).
///
/// This is just an alias for [`tower_service::Service`] introduced to reduce the number of type
/// parameters.
pub trait HttpService<B>: service_private::Sealed<B> {
    /// Body of the responses given by the service.
    type ResponseBody: Body;
    type Error;
    type Future: Future<Output = Result<Response<Self::ResponseBody>, Self::Error>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;

    fn call(&mut self, request: Request<B>) -> Self::Future;
}

/// An HTTP response future.
///
/// This is just an alias for [`Future`] introduced to reduce the number of type parameters.
pub trait HttpTryFuture: future_private::Sealed {
    type Body: Body;
    type Error;

    fn try_poll(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Result<Response<Self::Body>, Self::Error>>;
}

impl<F, B> HttpTryFuture for F
where
    F: TryFuture<Ok = Response<B>>,
    B: Body,
{
    type Body = B;
    type Error = F::Error;

    fn try_poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<Response<B>, F::Error>> {
        self.try_poll(cx)
    }
}

impl<F, B> future_private::Sealed for F
where
    F: TryFuture<Ok = Response<B>>,
    B: Body,
{
}

impl<S, ReqB, ResB> HttpService<ReqB> for S
where
    S: Service<Request<ReqB>, Response = Response<ResB>> + ?Sized,
    ResB: Body,
{
    type ResponseBody = ResB;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Service::poll_ready(self, cx)
    }

    fn call(&mut self, request: Request<ReqB>) -> S::Future {
        Service::call(self, request)
    }
}

impl<S, ReqB, ResB> service_private::Sealed<ReqB> for S
where
    S: Service<Request<ReqB>, Response = Response<ResB>> + ?Sized,
    ResB: Body,
{
}

mod future_private {
    pub trait Sealed {}
}

mod service_private {
    pub trait Sealed<B> {}
}