//! Contains [`MakeService`] which is a trait alias for a [`Service`] of [`Service`]s.

use crate::sealed::Sealed;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::task::{Context, Poll};
use tower_service::Service;

pub(crate) mod shared;

/// Creates new [`Service`] values.
///
/// Acts as a service factory. This is useful for cases where new [`Service`]
/// values must be produced. One case is a TCP server listener. The listener
/// accepts new TCP streams, obtains a new [`Service`] value using the
/// [`MakeService`] trait, and uses that new [`Service`] value to process inbound
/// requests on that new TCP stream.
///
/// This is essentially a trait alias for a [`Service`] of [`Service`]s.
pub trait MakeService<Target, Request>: Sealed<(Target, Request)> {
    /// Responses given by the service
    type Response;

    /// Errors produced by the service
    type Error;

    /// The [`Service`] value created by this factory
    type Service: Service<Request, Response = Self::Response, Error = Self::Error>;

    /// Errors produced while building a service.
    type MakeError;

    /// The future of the [`Service`] instance.
    type Future: Future<Output = Result<Self::Service, Self::MakeError>>;

    /// Returns [`Poll::Ready`] when the factory is able to create more services.
    ///
    /// If the service is at capacity, then [`Poll::Pending`] is returned and the task
    /// is notified when the service becomes ready again. This function is
    /// expected to be called while on a task.
    ///
    /// [`Poll::Ready`]: std::task::Poll::Ready
    /// [`Poll::Pending`]: std::task::Poll::Pending
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::MakeError>>;

    /// Create and return a new service value asynchronously.
    fn make_service(&mut self, target: Target) -> Self::Future;

    /// Consume this [`MakeService`] and convert it into a [`Service`].
    ///
    /// # Example
    /// ```
    /// use std::convert::Infallible;
    /// use tower::Service;
    /// use tower::make::MakeService;
    /// use tower::service_fn;
    ///
    /// # fn main() {
    /// # async {
    /// // A `MakeService`
    /// let make_service = service_fn(|make_req: ()| async {
    ///     Ok::<_, Infallible>(service_fn(|req: String| async {
    ///         Ok::<_, Infallible>(req)
    ///     }))
    /// });
    ///
    /// // Convert the `MakeService` into a `Service`
    /// let mut svc = make_service.into_service();
    ///
    /// // Make a new service
    /// let mut new_svc = svc.call(()).await.unwrap();
    ///
    /// // Call the service
    /// let res = new_svc.call("foo".to_string()).await.unwrap();
    /// # };
    /// # }
    /// ```
    fn into_service(self) -> IntoService<Self, Request>
    where
        Self: Sized,
    {
        IntoService {
            make: self,
            _marker: PhantomData,
        }
    }

    /// Convert this [`MakeService`] into a [`Service`] without consuming the original [`MakeService`].
    ///
    /// # Example
    /// ```
    /// use std::convert::Infallible;
    /// use tower::Service;
    /// use tower::make::MakeService;
    /// use tower::service_fn;
    ///
    /// # fn main() {
    /// # async {
    /// // A `MakeService`
    /// let mut make_service = service_fn(|make_req: ()| async {
    ///     Ok::<_, Infallible>(service_fn(|req: String| async {
    ///         Ok::<_, Infallible>(req)
    ///     }))
    /// });
    ///
    /// // Convert the `MakeService` into a `Service`
    /// let mut svc = make_service.as_service();
    ///
    /// // Make a new service
    /// let mut new_svc = svc.call(()).await.unwrap();
    ///
    /// // Call the service
    /// let res = new_svc.call("foo".to_string()).await.unwrap();
    ///
    /// // The original `MakeService` is still accessible
    /// let new_svc = make_service.make_service(()).await.unwrap();
    /// # };
    /// # }
    /// ```
    fn as_service(&mut self) -> AsService<'_, Self, Request>
    where
        Self: Sized,
    {
        AsService {
            make: self,
            _marker: PhantomData,
        }
    }
}

impl<M, S, Target, Request> Sealed<(Target, Request)> for M
where
    M: Service<Target, Response = S>,
    S: Service<Request>,
{
}

impl<M, S, Target, Request> MakeService<Target, Request> for M
where
    M: Service<Target, Response = S>,
    S: Service<Request>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Service = S;
    type MakeError = M::Error;
    type Future = M::Future;

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

    fn make_service(&mut self, target: Target) -> Self::Future {
        Service::call(self, target)
    }
}

/// Service returned by [`MakeService::into_service`][into].
///
/// See the documentation on [`into_service`][into] for details.
///
/// [into]: MakeService::into_service
pub struct IntoService<M, Request> {
    make: M,
    _marker: PhantomData<Request>,
}

impl<M, Request> Clone for IntoService<M, Request>
where
    M: Clone,
{
    fn clone(&self) -> Self {
        Self {
            make: self.make.clone(),
            _marker: PhantomData,
        }
    }
}

impl<M, Request> fmt::Debug for IntoService<M, Request>
where
    M: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("IntoService")
            .field("make", &self.make)
            .finish()
    }
}

impl<M, S, Target, Request> Service<Target> for IntoService<M, Request>
where
    M: Service<Target, Response = S>,
    S: Service<Request>,
{
    type Response = M::Response;
    type Error = M::Error;
    type Future = M::Future;

    #[inline]
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.make.poll_ready(cx)
    }

    #[inline]
    fn call(&mut self, target: Target) -> Self::Future {
        self.make.make_service(target)
    }
}

/// Service returned by [`MakeService::as_service`][as].
///
/// See the documentation on [`as_service`][as] for details.
///
/// [as]: MakeService::as_service
pub struct AsService<'a, M, Request> {
    make: &'a mut M,
    _marker: PhantomData<Request>,
}

impl<M, Request> fmt::Debug for AsService<'_, M, Request>
where
    M: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("AsService")
            .field("make", &self.make)
            .finish()
    }
}

impl<M, S, Target, Request> Service<Target> for AsService<'_, M, Request>
where
    M: Service<Target, Response = S>,
    S: Service<Request>,
{
    type Response = M::Response;
    type Error = M::Error;
    type Future = M::Future;

    #[inline]
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.make.poll_ready(cx)
    }

    #[inline]
    fn call(&mut self, target: Target) -> Self::Future {
        self.make.make_service(target)
    }
}
