servant-generic-0.1.0.0: src/Servant/Generic.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DataKinds #-}
module Servant.Generic
( (:-)
, AsServerT
, AsServer
, AsApi
, ToServant
, toServant
, GenericProduct
-- * Internals
, GProduct(..)
, Generic(..)
) where
import GHC.Generics
import Servant
-- | A class of generic product types.
class GProduct f where
type GToServant f
gtoServant :: f p -> GToServant f
instance GProduct f => GProduct (M1 i c f) where
type GToServant (M1 i c f) = GToServant f
gtoServant (M1 x) = gtoServant x
instance (GProduct l, GProduct r) => GProduct (l :*: r) where
type GToServant (l :*: r) = GToServant l :<|> GToServant r
gtoServant (l :*: r) = gtoServant l :<|> gtoServant r
instance GProduct (K1 i c) where
type GToServant (K1 i c) = c
gtoServant (K1 x) = x
type GenericProduct a = (Generic a, GProduct (Rep a))
-- | Turns a generic product type into a linear tree of `:<|>` combinators.
-- For example, given
--
-- @
-- data Foo route = Foo
-- { foo :: route :-
-- Get '[PlainText] Text
-- , bar :: route :-
-- Get '[PlainText] Text
-- }
-- @
--
-- @ ToServant (Foo AsApi) ~ Get '[PlainText] Text :\<|\> Get '[PlainText] Text @
type ToServant a = GToServant (Rep a)
-- | See `ToServant`, but at value-level.
toServant :: GenericProduct a => a -> ToServant a
toServant = gtoServant . from
-- | A type family that applies an appropriate type family to the @api@ parameter.
-- For example, passing `AsApi` will leave @api@ untouched, while @`AsServerT` m@ will produce @`ServerT` api m@.
type family mode :- api
infixl 8 :-
-- | A type that specifies that an API record contains an API definition. Only useful at type-level.
data AsApi
type instance AsApi :- api = api
-- | A type that specifies that an API record contains a server implementation.
data AsServerT (m :: * -> *)
type instance AsServerT m :- api = ServerT api m
type AsServer = AsServerT Handler