content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE PolyKinds #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeOperators #-}
  
  {- |
  Module      :  Servant.Checked.Exceptions.Internal.Servant.API
  
  Copyright   :  Dennis Gosnell 2017
  License     :  BSD3
  
  Maintainer  :  Dennis Gosnell (cdep.illabout@gmail.com)
  Stability   :  experimental
  Portability :  unknown
  
  This module defines the 'Throws' and 'Throwing' types.
  -}
  
  module Servant.Checked.Exceptions.Internal.Servant.API where
  
  import GHC.Exts (Constraint)
  import Network.HTTP.Types (Status)
  import Servant.API ((:>))
  
  import Servant.Checked.Exceptions.Internal.Util (Snoc)
  
  -- | 'Throws' is used in Servant API definitions and signifies that an API will
  -- throw the given error.
  --
  -- Here is an example of how to create an API that potentially returns a
  -- 'String' as an error, or an 'Int' on success:
  --
  -- >>> import Servant.API (Get, JSON, (:>))
  -- >>> type API = Throws String :> Get '[JSON] Int
  data Throws (e :: *)
  
  -- | 'NoThrow' is used to indicate that an API will not throw an error, but
  -- that it will still return a response wrapped in a
  -- 'Servant.Checked.Exceptions.Internal.Envelope.Envelope'.
  --
  -- ==== __Examples__
  --
  -- Create an API using 'NoThrow':
  --
  -- >>> import Servant.API (Get, JSON, (:>))
  -- >>> type API = NoThrow :> Get '[JSON] Int
  --
  -- A servant-server handler for this type would look like the following:
  --
  -- @
  --   apiHandler :: 'Servant.Handler' ('Servant.Checked.Exceptions.Internal.Envelope.Envelope' \'[] Int)
  --   apiHandler = 'Servant.Checked.Exceptions.Internal.Envelope.pureSuccEnvelope' 3
  -- @
  data NoThrow
  
  -- | This is used internally and should not be used by end-users.
  data Throwing (e :: [*])
  
  -- | Used by the 'HasServer' and 'HasClient' instances for
  -- @'Throwing' es ':>' api ':>' apis@ to detect @'Throwing' es@ followed
  -- immediately by @'Throws' e@.
  type family ThrowingNonterminal api where
    ThrowingNonterminal (Throwing es :> Throws e :> api) =
      Throwing (Snoc es e) :> api
    ThrowingNonterminal (Throwing es :> c :> api) =
      c :> Throwing es :> api
  
  
  -- | Note that clients generated by @servant-client@ currently don't handle
  -- status codes outside the @2XX@ range correctly.
  -- Take a look at the README for more details.
  class ErrStatus e where
    toErrStatus :: e -> Status
  
  type family AllErrStatus (es :: [k]) :: Constraint where
    AllErrStatus '[] = ()
    AllErrStatus (a ': as) = (ErrStatus a, AllErrStatus as)
  
  -- $setup
  -- >>> :set -XDataKinds
  -- >>> :set -XTypeOperators
  