-- | -- | This module just exports orphan instances to make named-servant
-- work with servers
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Servant.Server.Record () where
import Servant.API
import Data.Proxy
import GHC.TypeLits
import GHC.Generics
import Servant.Server
import Servant.Server.Internal
import Servant.Record

class GHasServer (a :: * -> *) context api where
  gRoute :: Proxy api -> Context context -> Delayed env (a () -> Server api)
        -> Router env
  gHoistServerWithContext :: Proxy api -> Proxy context
                          -> (forall x. m x -> n x)
                          -> (a () -> ServerT api m)
                          -> (a () -> ServerT api n)

data GParam a

instance ( Generic a
         , GHasServer (Rep a) context api
         ) =>
         HasServer (RecordParam a :> api) context where
  type ServerT (RecordParam a :> api) m = a -> ServerT api m
  route _ context env = gRoute (Proxy :: Proxy api) context $
                        (\f (x :: Rep a ()) -> f (to x)) <$> env
  {-# INLINE route #-}
  hoistServerWithContext _ pc nt s x =
    gHoistServerWithContext (Proxy :: Proxy api) pc nt (s . to)
    (from x :: Rep a ())
  {-# INLINE hoistServerWithContext #-}

instance GHasServer a context api =>
         HasServer (GParam (a ()) :> api) context where
  type ServerT (GParam (a ()) :> api) m = a () -> ServerT api m
  route _ = gRoute (Proxy :: Proxy api)
  {-# INLINE route #-}
  hoistServerWithContext _ = gHoistServerWithContext (Proxy :: Proxy api)
  {-# INLINE hoistServerWithContext #-}

instance GHasServer c context api =>
         GHasServer (D1 m3 c) context api where
  gRoute _ context env = gRoute (Proxy :: Proxy api) context $
                         (\f x -> f (M1 x)) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (M1 x) =
    gHoistServerWithContext (Proxy :: Proxy api) pc nt (s . M1) x
  {-# INLINE gHoistServerWithContext #-}

instance GHasServer a context (GParam (b ()) :> api) =>
         GHasServer ((a :*: b)) context api where
  gRoute _ context env = gRoute (Proxy :: Proxy (GParam (b ()) :> api))
                         context $ (\f x y -> f (x :*: y)) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (x :*: y) =
    gHoistServerWithContext (Proxy :: Proxy (GParam (b ()) :> api))
    pc nt (\x' y' -> s (x' :*: y')) x y
  {-# INLINE gHoistServerWithContext #-}

instance GHasServer a context api =>
         GHasServer (C1 n a) context api where
  gRoute _ context env = gRoute (Proxy :: Proxy api) context $
                         (\f x -> f (M1 x)) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (M1 x) =
    gHoistServerWithContext (Proxy :: Proxy api) pc nt (s . M1) x
  {-# INLINE gHoistServerWithContext #-}

instance {-# OVERLAPPING #-}
  ( HasServer api context
  , KnownSymbol sym
  ) =>
  GHasServer (S1 ('MetaSel ('Just sym) d1 d2 d3) (Rec0 Bool))
             context
             api where
  gRoute _ context env = route (Proxy :: Proxy (QueryFlag sym :> api))
                         context $ (\f x -> f (M1 (K1 x))) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (M1 (K1 x)) =
    hoistServerWithContext (Proxy :: Proxy (QueryFlag sym :> api)) pc nt
    (s . M1 . K1) x
  {-# INLINE gHoistServerWithContext #-}

instance {-# OVERLAPPING #-}
  ( HasServer api context
  , FromHttpApiData a
  , KnownSymbol sym
  ) =>
  GHasServer (S1 ('MetaSel ('Just sym) d1 d2 d3) (Rec0 [a]))
             context
             api where
  gRoute _ context env = route (Proxy :: Proxy (QueryParams sym a :> api))
                         context $ (\f x -> f (M1 (K1 x))) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (M1 (K1 x)) =
    hoistServerWithContext (Proxy :: Proxy (QueryParams sym a :> api)) pc nt
    (s . M1 . K1) x
  {-# INLINE gHoistServerWithContext #-}

instance {-# OVERLAPPING #-}
  ( HasServer api context
  , FromHttpApiData a
  , KnownSymbol sym
  ) =>
  GHasServer (S1 ('MetaSel ('Just sym) d1 d2 d3) (Rec0 (Maybe a)))
             context
             api where
  gRoute _ context env =
    route (Proxy :: Proxy (QueryParam sym a :> api))
    context $ (\f x -> f (M1 (K1 x))) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (M1 (K1 x)) =
    hoistServerWithContext (Proxy :: Proxy (QueryParam sym a :> api))
    pc nt (s . M1 . K1) x
  {-# INLINE gHoistServerWithContext #-}

instance {-# OVERLAPPABLE #-}
  ( HasServer api context
  , FromHttpApiData a
  , KnownSymbol sym
  ) =>
  GHasServer (S1 ('MetaSel ('Just sym) d1 d2 d3) (Rec0 a))
             context
             api where
  gRoute _ context env =
    route (Proxy :: Proxy (QueryParam' '[Required, Strict] sym a :> api))
    context $ (\f x -> f (M1 (K1 x))) <$> env
  {-# INLINE gRoute #-}
  gHoistServerWithContext _ pc nt s (M1 (K1 x)) =
    hoistServerWithContext
    (Proxy :: Proxy (QueryParam' '[Required, Strict] sym a :> api))
    pc nt (s . M1 . K1) x
  {-# INLINE gHoistServerWithContext #-}