# LANGUAGE PolyKinds #
# LANGUAGE TypeFamilies #

-- | Extras for working with servant
module Hercules.API.Servant
  ( noContent,

    -- * 'Generic'
    useApi,
    useApiE,
    enterApi,
    enterApiE,

    -- * Substitution
    Substitute,
    Placeholder,
  )
where

import Control.Monad (void)
import Data.Kind (Type)
import Servant.API
import Servant.API.Generic
import Prelude

-- | Postcomposes 'Servant.API.Generic.fromServant' to an accessor,
-- preserving the mode parameter, because otherwise the mode parameter
-- can not be inferred.
--
-- Ideally, this functionality would be built into a new combinator.
useApi ::
  forall subapi api mode.
  (GenericServant subapi mode) =>
  (api mode -> ToServant subapi mode) ->
  api mode ->
  subapi mode
useApi = (Servant.API.Generic.fromServant .)

-- | Like 'useApi' but constrains the @auth@ type variable that's passed to
-- subapis.
useApiE ::
  forall subapi api mode a.
  (GenericServant (subapi a) mode) =>
  (api a mode -> ToServant (subapi a) mode) ->
  api a mode ->
  subapi a mode
useApiE = useApi

-- | @flip 'useApi'
enterApi ::
  forall subapi api mode.
  (GenericServant subapi mode) =>
  api mode ->
  (api mode -> ToServant subapi mode) ->
  subapi mode
enterApi = flip useApi

  | @flip ' useApiE '
enterApiE ::
  forall subapi api mode a.
  (GenericServant (subapi a) mode) =>
  api a mode ->
  (api a mode -> ToServant (subapi a) mode) ->
  subapi a mode
enterApiE = flip useApi

  | ' Control.Monad.void ' specialised to ' NoContent ' to soothe the
-- compiler that rightfully warns about throwing away a do notation
-- result. By specialising, we make sure that we still get warnings
-- if the result type changes in the future. (We'll get an error)
noContent :: Functor m => m Servant.API.NoContent -> m ()
noContent = void

-- | A reference to the @subapi@ parameter in 'Substitute'
data Placeholder

-- | Replaces 'Placeholder' by @subapi@ in the API @target@.
type family Substitute (target :: k) subapi :: Type

type instance Substitute Placeholder subapi = subapi

type instance Substitute (a :> b) subapi = a :> Substitute b subapi

type instance Substitute (a :<|> b) subapi = Substitute a subapi :<|> Substitute b subapi

 | Extras for working with servant
 * 'Generic'
 * Substitution
 | Postcomposes 'Servant.API.Generic.fromServant' to an accessor,
 preserving the mode parameter, because otherwise the mode parameter
 can not be inferred.

 Ideally, this functionality would be built into a new combinator.
 | Like 'useApi' but constrains the @auth@ type variable that's passed to
 subapis.
 | @flip 'useApi'
 compiler that rightfully warns about throwing away a do notation
 result. By specialising, we make sure that we still get warnings
 if the result type changes in the future. (We'll get an error)
 | A reference to the @subapi@ parameter in 'Substitute'
 | Replaces 'Placeholder' by @subapi@ in the API @target@.
# LANGUAGE PolyKinds #
# LANGUAGE TypeFamilies #

module Hercules.API.Servant
  ( noContent,

    useApi,
    useApiE,
    enterApi,
    enterApiE,

    Substitute,
    Placeholder,
  )
where

import Control.Monad (void)
import Data.Kind (Type)
import Servant.API
import Servant.API.Generic
import Prelude

useApi ::
  forall subapi api mode.
  (GenericServant subapi mode) =>
  (api mode -> ToServant subapi mode) ->
  api mode ->
  subapi mode
useApi = (Servant.API.Generic.fromServant .)

useApiE ::
  forall subapi api mode a.
  (GenericServant (subapi a) mode) =>
  (api a mode -> ToServant (subapi a) mode) ->
  api a mode ->
  subapi a mode
useApiE = useApi

enterApi ::
  forall subapi api mode.
  (GenericServant subapi mode) =>
  api mode ->
  (api mode -> ToServant subapi mode) ->
  subapi mode
enterApi = flip useApi

  | @flip ' useApiE '
enterApiE ::
  forall subapi api mode a.
  (GenericServant (subapi a) mode) =>
  api a mode ->
  (api a mode -> ToServant (subapi a) mode) ->
  subapi a mode
enterApiE = flip useApi

  | ' Control.Monad.void ' specialised to ' NoContent ' to soothe the
noContent :: Functor m => m Servant.API.NoContent -> m ()
noContent = void

data Placeholder

type family Substitute (target :: k) subapi :: Type

type instance Substitute Placeholder subapi = subapi

type instance Substitute (a :> b) subapi = a :> Substitute b subapi

type instance Substitute (a :<|> b) subapi = Substitute a subapi :<|> Substitute b subapi
