    {-# LANGUAGE DataKinds           #-}
    {-# LANGUAGE DeriveDataTypeable  #-}
    {-# LANGUAGE FlexibleContexts    #-}
    {-# LANGUAGE PolyKinds           #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeFamilies        #-}
    {-# LANGUAGE TypeOperators       #-}
    {-# OPTIONS_HADDOCK not-home    #-}
    module Servant.API.Description (
        -- * Combinators
        Description,
        Summary,
        -- * Used as modifiers
        FoldDescription,
        FoldDescription',
        reflectDescription,
        ) where

    import           Data.Proxy
                     (Proxy (..))
    import           Data.Typeable
                     (Typeable)
    import           GHC.TypeLits
                     (KnownSymbol, Symbol, symbolVal)

    -- | Add a short summary for (part of) API.
    --
    -- Example:
    --
    -- >>> type MyApi = Summary "Get book by ISBN." :> "books" :> Capture "isbn" Text :> Get '[JSON] Book
    data Summary (sym :: Symbol)
        deriving (Typeable)

    -- | Add more verbose description for (part of) API.
    --
    -- Example:
    --
    -- >>> :{
    --type MyApi = Description
    --  "This comment is visible in multiple Servant interpretations \
    --  \and can be really long if necessary. \
    --  \Haskell multiline support is not perfect \
    --  \but it's still very readable."
    -- :> Get '[JSON] Book
    -- :}
    data Description (sym :: Symbol)
        deriving (Typeable)

    -- | Fold modifier list to decide whether argument should be parsed strictly or leniently.
    --
    -- >>> :kind! FoldDescription '[]
    -- FoldDescription '[] :: Symbol
    -- = ""
    --
    -- >>> :kind! FoldDescription '[Required, Description "foobar", Lenient]
    -- FoldDescription '[Required, Description "foobar", Lenient] :: Symbol
    -- = "foobar"
    --
    type FoldDescription mods = FoldDescription' "" mods

    -- | Implementation of 'FoldDescription'.
    type family FoldDescription' (acc :: Symbol) (mods ::  [*]) :: Symbol where
        FoldDescription' acc '[]                        = acc
        FoldDescription' acc (Description desc ': mods) = FoldDescription' desc mods
        FoldDescription' acc (mod     ': mods)          = FoldDescription' acc mods

    -- | Reflect description to the term level.
    --
    -- >>> reflectDescription (Proxy :: Proxy '[Required, Description "foobar", Lenient])
    -- "foobar"
    --
    reflectDescription :: forall mods. KnownSymbol (FoldDescription mods) => Proxy mods -> String
    reflectDescription :: Proxy mods -> String
    reflectDescription _ = Proxy (FoldDescription' "" mods) -> String
    forall (n :: Symbol) (proxy :: Symbol -> *).
    KnownSymbol n =>
    proxy n -> String
    symbolVal (Proxy (FoldDescription' "" mods)
    forall k (t :: k). Proxy t
    Proxy :: Proxy (FoldDescription mods))

    -- $setup
    -- >>> import Servant.API
    -- >>> import Data.Aeson
    -- >>> import Data.Text
    -- >>> data Book
    -- >>> instance ToJSON Book where { toJSON = undefined }
    -- >>> data SourceFile
    -- >>> instance ToJSON SourceFile where { toJSON = undefined }