{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# OPTIONS_HADDOCK ignore-exports #-}
-- |
-- Copyright        : (c) Raghu Kaippully, 2020
-- License          : MPL-2.0
-- Maintainer       : rkaippully@gmail.com
--
-- WebGear helps to build composable, type-safe HTTP API servers.
--
-- The documentation below gives an overview of WebGear. Example
-- programs built using WebGear are available at
-- https://github.com/rkaippully/webgear/tree/master/webgear-examples.
--
module WebGear
  ( -- * Serving HTTP APIs
    -- $serving

    -- * Traits and Linking
    -- $traits

    -- * Handlers
    -- $handlers

    -- * Middlewares
    -- $middlewares

    -- * Routing
    -- $routing

    -- * Running the Server
    -- $running

    -- * Servers with other monads
    -- $otherMonads

    module Control.Applicative
  , module Control.Arrow
  , module Data.ByteString.Lazy
  , module Data.ByteString.Conversion.To
  , module Data.Proxy
  , module Data.Text
  , module Web.HttpApiData
  , module WebGear.Middlewares
  , module WebGear.Trait
  , module WebGear.Types
  ) where

import Control.Applicative (Alternative ((<|>)))
import Control.Arrow (Kleisli (..))
import Data.ByteString.Conversion.To
import Data.ByteString.Lazy (ByteString)
import Data.Proxy (Proxy (..))
import Data.Text
import Web.HttpApiData (FromHttpApiData (..))

import qualified Network.Wai as Wai

import WebGear.Middlewares
import WebGear.Trait
import WebGear.Types


--
-- $serving
--
-- An HTTP API server handler can be thought of as a function that
-- takes a request as input and produces a response as output in a
-- monadic context.
--
-- > handler :: Monad m => Request -> m Response
--
-- For reasons that will be explained later, WebGear uses the 'Router'
-- monad for running handlers. Thus the above type signature changes
-- to:
--
-- > handler :: Request -> Router Response
--
-- Most APIs will require extracting some information from the
-- request, processing it and then producing a response. For example,
-- the server might require access to some HTTP header values, query
-- parameters, or the request body. WebGear allows to access such
-- information using traits.
--
--
-- $traits
--
-- A trait is an attribute associated with a value. For example, a
-- @Request@ might have a header that we are interested in, which is
-- represented by the 'Header' trait. All traits have instances of the
-- 'Trait' typeclass. The 'toAttribute' function helps to check
-- presence of the trait. It also has two associated types -
-- 'Attribute' and 'Absence' - to represent the result of the
-- extraction.
--
-- For example, the 'Header' trait has an instance of the 'Trait'
-- typeclass. The 'toAttribute' function evaluates to a 'Found' or
-- 'NotFound' value depending on whether we can successfully retrieve
-- the header value.
--
-- WebGear provides type-safety by linking traits to the request at
-- type level. The 'Linked' data type associates a 'Request' with a
-- list of traits. This linking guarantees that the Request has the
-- specified trait.
--
-- These functions work with traits and linked values:
--
--   * 'link': Establish a link between a value and an empty list of
--     traits. This always succeeds.
--
--   * 'unlink': Convert a linked value to a regular value without any
--     type-level traits.
--
--   * 'probe': Attempts to establish a link between a linked value
--     with an additional trait using 'toAttribute'.
--
--   * 'remove': Removes a trait from the list of linked traits.
--
--   * 'get': Extract an 'Attribute' associated with a trait from a
--     linked value.
--
-- For example, we make use of the @'Method' \@GET@ trait to ensure
-- that our handler is called only for GET requests. We can link a
-- request value with this trait using:
--
-- @
-- linkedRequest :: Monad m => 'Request' -> 'Router' (Either 'MethodMismatch' ('Linked' '['Method' GET] 'Request'))
-- linkedRequest = 'probe' @('Method' GET) . 'link'
-- @
--
-- Let us modify the type signature of our handler to use linked
-- values instead of regular values:
--
-- > handler :: Linked req Request -> Router Response
--
-- Here, @req@ is a type-level list of traits associated with the
-- @Request@ that this handler requires. This ensures that this
-- handler can only be called with a request possessing certain
-- traits thus providing type-safety to our handlers.
--
--
-- $handlers
--
-- Handlers in WebGear are defined with a type very similar to the
-- above.
--
-- @
-- type 'Handler'' m req a = 'Kleisli' m ('Linked' req 'Request') ('Response' a)
--
-- type 'Handler' req a = 'Handler'' 'Router' req a
-- @
--
-- It is a 'Kleisli' arrow as described in the above section with
-- type-level trait lists. However, the response is parameterized by
-- the type variable @a@, which represents the type of the response
-- body.
--
-- 'Handler'' can work with any monad while 'Handler' works with
-- 'Router'.
--
-- A handler can extract some trait attribute of a request with the
-- 'get' function.
--
--
-- $middlewares
--
-- A middleware is a higher-order function that takes a handler as
-- input and produces another handler with potentially different
-- request and response types. Thus middlewares can augment the
-- functionality of another handler.
--
-- For example, here is the definition of the 'method' middleware:
--
-- @
-- method :: ('IsStdMethod' t, 'MonadRouter' m) => 'Handler'' m ('Method' t:req) a -> 'Handler'' m req a
-- method handler = 'Kleisli' $ 'probe' \@('Method' t) >=> 'either' ('const' 'rejectRoute') ('runKleisli' handler)
-- @
--
-- The @probe \@(Method t)@ function is used to ensure that the
-- request has method @t@ before invoking the @handler@. In case of a
-- mismatch, this route is rejected by calling 'rejectRoute'.
--
-- Many middlewares can be composed to form complex request handling
-- logic. For example:
--
-- @
-- putUser = 'method' \@PUT
--           $ 'requestContentTypeHeader' \@"application/json"
--           $ 'jsonRequestBody' \@User
--           $ 'jsonResponseBody' \@User
--           $ putUserHandler
-- @
--
--
-- $routing
--
-- A typical server will have many routes and we would like to pick
-- one based on the URL path, HTTP method etc. We need a couple of
-- things to achieve this.
--
-- First, we need a way to indicate that a handler cannot handle a
-- request, possibly because the path or method did not match with
-- what was expected. This is achieved by the 'rejectRoute' function:
--
-- @
-- class (Alternative m, MonadPlus m) => 'MonadRouter' m where
--   'rejectRoute' :: m a
--   'errorResponse' :: 'Response' 'ByteString' -> m a
--   'catchErrorResponse' :: m a -> ('Response' 'ByteString' -> m a) -> m a
-- @
--
-- The 'errorResponse' can be used in cases where we find a matching
-- route but the request handling is aborted for some reason. For
-- example, if a route requires the request Content-type header to
-- have a particular value but the actual request had a different
-- Content-type, 'errorResponse' can be used to abort and return an
-- error response.
--
-- Second, we need a mechanism to try an alternate route when one
-- route is rejected. Since 'MonadRouter' is an 'Alternative', we can
-- use '<|>' to combine many routes. When a request arrives, a match
-- will be attempted against each route sequentially and the first
-- matching route handler will process the request. Here is an
-- example:
--
-- @
-- allRoutes :: 'Handler' '[] 'ByteString'
-- allRoutes = ['match'| /v1\/users\/userId:Int |]    -- non-TH version: 'path' \@"/v1/users" . 'pathVar' \@"userId" \@Int
--             $ getUser \<|\> putUser \<|\> deleteUser
--
-- type IntUserId = 'PathVar' "userId" Int
--
-- getUser :: 'Has' IntUserId req => 'Handler' req 'ByteString'
-- getUser = 'method' \@GET getUserHandler
--
-- putUser :: 'Has' IntUserId req => 'Handler' req 'ByteString'
-- putUser = 'method' \@PUT
--           $ 'requestContentTypeHeader' \@"application/json"
--           $ 'jsonRequestBody' \@User
--           $ putUserHandler
--
-- deleteUser :: 'Has' IntUserId req => 'Handler' req 'ByteString'
-- deleteUser = 'method' \@DELETE deleteUserHandler
-- @
--
--
-- $running
--
-- Routable handlers can be converted to a Wai 'Wai.Application' using
-- 'toApplication':
--
-- @
-- toApplication :: 'ToByteString' a => 'Handler' '[] a -> 'Wai.Application'
-- @
--
-- This Wai application can then be run as a Warp web server.
--
-- @
-- main :: IO ()
-- main = Warp.run 3000 $ 'toApplication' allRoutes
-- @
--
--
-- $otherMonads
--
-- It may not be practical to use 'Router' monad for your handlers. In
-- most cases, you would need your own monad transformer stack or
-- algebraic effect runners. WebGear supports that easily.
--
-- Let us say, the @putUserHandler@ from the above example runs on
-- some monad other than 'Router'. You can still use it as a handler thus:
--
-- @
-- putUser = 'method' \@PUT
--           $ 'requestContentTypeHeader' \@"application/json"
--           $ 'jsonRequestBody' \@User
--           $ 'jsonResponseBody' \@User
--           $ 'transform' customMonadToRouter putUserHandler
--
-- putUserHandler :: 'Handler'' MyCustomMonad req User
-- putUserHandler = ....
--
-- customMonadToRouter :: MyCustomMonad a -> Router a
-- customMonadToRouter = ...
-- @
--
-- As long as you have a way of transforming values in your custom
-- monad to a 'Router' monadic value, you can use 'transform' to
-- convert the handlers in that custom monad to handlers running in
-- 'Router' monad.
--