{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{- |
Module : Servant.Scotty
Copyright : (c) Zalora SEA 2014
License : BSD3
Maintainer : Alp Mestanogullari <alp@zalora.com>
Stability : experimental
Module for defining a <http://hackage.haskell.org/package/scotty scotty>
webservice from 'Resource's.
> EXAMPLE HERE
-}
module Servant.Scotty
( -- * Setting up handlers for a 'Resource'
Runnable(runResource)
-- * Defining handlers for an operation
, ScottyOp(..)
, Index(..)
, js
, safely
, Response(..)
, respond
-- * Utilities
, Suitables
) where
import Control.Applicative
import Control.Monad.IO.Class
import Data.Aeson hiding (json)
import GHC.Exts
import Servant.Resource
import Servant.Scotty.Arguments
import Servant.Scotty.Op
import Servant.Scotty.Response
import Web.Scotty.Trans
-- | Internal class used to drive the recursion
-- on the list of operations when generating all
-- the endpoints and handlers.
--
-- This lets everyone care only about the specific
-- behavior of their operations, this class will make
-- sure all the operations of your 'Resource' have an
-- implementation.
--
-- Regardless of what's
-- specific about each operation, we just recursively
-- go through all the operations and call 'runOperation'
-- for each of them.
class Runnable ops where
-- | Call this function to setup a 'Resource' in your
-- scotty application.
runResource :: (Functor m, MonadIO m, ScottyError e, Suitables ops a i r)
=> Resource c a i r e ops
-> ScottyT e m ()
-- | No operation means we don't setup any handler.
instance Runnable '[] where
-- runResource :: (MonadIO m, ScottyError e)
-- => Resource c a i r e '[]
-- -> ScottyT e m ()
-- no operation supported
-- (or "no more", if there was any and we're ending the recursion)
runResource _ = return ()
-- | Given some already runnable operation list @ops@,
-- and an operation that we can run in scotty
--
-- (that's the @'ScottyOp' o@ constraint),
--
-- we can run the @(o ': ops)@ operation list.
instance (ScottyOp o, Runnable ops) => Runnable (o ': ops) where
-- runResource :: (MonadIO m, ScottyError e)
-- => Resource c a i r e (o ': ops)
-- -> ScottyT e m ()
runResource r = do
withHeadOperation r runOperation
runResource (dropHeadOperation r)
type family Suitables (ops :: [*]) a i (r :: * -> *) :: Constraint
type instance Suitables '[] a i r = ()
type instance Suitables (o ': ops) a i r = (Suitable o a i r, Suitables ops a i r)