packages feed

servant-hateoas-0.3.4: src/Servant/Hateoas/Layer/Build.hs

{-# LANGUAGE UndecidableInstances #-}

module Servant.Hateoas.Layer.Build
(
  -- * Type family
  ReplaceHandler,

  -- * Class
  BuildLayerLinks(..),
)
where

import Servant
import Servant.API.ContentTypes
import Servant.Hateoas.RelationLink
import Servant.Hateoas.Layer.Type
import Servant.Hateoas.Internal.Sym
import Servant.Hateoas.Internal.Polyvariadic
import Data.Kind
import Control.Monad.IO.Class
import GHC.TypeLits

-- | Replace the result 'Type' of a function with a new 'Type'.
type family ReplaceHandler server replacement where
  ReplaceHandler (a :<|> b)  replacement = ReplaceHandler a replacement :<|> ReplaceHandler b replacement
  ReplaceHandler (a -> b)    replacement = a -> ReplaceHandler b replacement
  ReplaceHandler _           replacement = replacement

-- | Create all 'RelationLink's to a 'Layer's 'RelativeChildren'.
type BuildLayerLinks :: Layer -> (Type -> Type) -> Constraint
class BuildLayerLinks l m where
  buildLayerLinks :: MonadIO m => Proxy l -> Proxy m -> ReplaceHandler (ServerT l m) [(String, RelationLink)]

instance
  ( api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , mkSelf ~ MkLink api RelationLink
  , PolyvariadicComp mkSelf (IsFun mkSelf)
  , Return mkSelf (IsFun mkSelf) ~ RelationLink
  , Replace mkSelf [(String, RelationLink)] (IsFun mkSelf) ~ ReplaceHandler (ServerT api m) [(String, RelationLink)]
  ) => BuildLayerLinks ('Layer apiCs '[] verb) m where
  buildLayerLinks _ _ = (pure @[] . ("self", )) ... mkSelf
    where
      mkSelf = toRelationLink (Proxy @api)

instance
  ( c ~ MkPrefix (apiCs ++ '[Sym sym]) verb
  , HasRelationLink c
  , mkLink ~ MkLink c RelationLink
  , KnownSymbol sym
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT (MkPrefix apiCs verb) m) [(String, RelationLink)])
  , PolyvariadicComp2 mkLink buildLinksFun (IsFun mkLink)
  , Return2 mkLink buildLinksFun (IsFun mkLink) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 mkLink buildLinksFun [(String, RelationLink)] (IsFun mkLink) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (Sym sym ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(l, ls) -> (symbolVal (Proxy @sym), l) : ls) mkLink mkLinks
    where
      mkLink = toRelationLink (Proxy @c)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m

instance
  ( verb ~ Verb method status cts a
  , AllMime cts, ReflectMethod method
  , api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , KnownSymbol sym
  , HasTemplatedLink (MkPrefix '[Capture' mods sym x] verb)
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT api m) [(String, RelationLink)])
  , PolyvariadicComp2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun)
  , Return2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 (MkLink api RelationLink) buildLinksFun [(String, RelationLink)] (IsFun buildLinksFun) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (Capture' mods sym x ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(self, ls) -> (relName, self <<< child) : ls) mkSelf mkLinks
    where
      relName = symbolVal (Proxy @sym)
      mkSelf = toRelationLink (Proxy @api)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m
      child = toTemplatedLink (Proxy @(MkPrefix '[Capture' mods sym x] verb))

instance
  ( verb ~ Verb method status cts a
  , AllMime cts, ReflectMethod method
  , api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , KnownSymbol sym
  , HasTemplatedLink (MkPrefix '[CaptureAll sym x] verb)
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT api m) [(String, RelationLink)])
  , PolyvariadicComp2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun)
  , Return2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 (MkLink api RelationLink) buildLinksFun [(String, RelationLink)] (IsFun buildLinksFun) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (CaptureAll sym x ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(self, ls) -> (relName, self <<< child) : ls) mkSelf mkLinks
    where
      relName = symbolVal (Proxy @sym)
      mkSelf = toRelationLink (Proxy @api)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m
      child = toTemplatedLink (Proxy @(MkPrefix '[CaptureAll sym x] verb))

instance
  ( verb ~ Verb method status cts a
  , AllMime cts, ReflectMethod method
  , api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , KnownSymbol sym
  , HasTemplatedLink (MkPrefix '[QueryParam' mods sym x] verb)
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT api m) [(String, RelationLink)])
  , PolyvariadicComp2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun)
  , Return2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 (MkLink api RelationLink) buildLinksFun [(String, RelationLink)] (IsFun buildLinksFun) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (QueryParam' mods sym x ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(self, ls) -> (relName, self <<< child) : ls) mkSelf mkLinks
    where
      relName = symbolVal (Proxy @sym)
      mkSelf = toRelationLink (Proxy @api)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m
      child = toTemplatedLink (Proxy @(MkPrefix '[QueryParam' mods sym x] verb))

instance
  ( verb ~ Verb method status cts a
  , AllMime cts, ReflectMethod method
  , api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , KnownSymbol sym
  , HasTemplatedLink (MkPrefix '[QueryParams sym x] verb)
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT api m) [(String, RelationLink)])
  , PolyvariadicComp2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun)
  , Return2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 (MkLink api RelationLink) buildLinksFun [(String, RelationLink)] (IsFun buildLinksFun) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (QueryParams sym x ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(self, ls) -> (relName, self <<< child) : ls) mkSelf mkLinks
    where
      relName = symbolVal (Proxy @sym)
      mkSelf = toRelationLink (Proxy @api)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m
      child = toTemplatedLink (Proxy @(MkPrefix '[QueryParams sym x] verb))

instance
  ( verb ~ Verb method status cts a
  , AllMime cts, ReflectMethod method
  , api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , KnownSymbol sym
  , HasTemplatedLink (MkPrefix '[DeepQuery sym x] verb)
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT api m) [(String, RelationLink)])
  , PolyvariadicComp2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun)
  , Return2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 (MkLink api RelationLink) buildLinksFun [(String, RelationLink)] (IsFun buildLinksFun) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (DeepQuery sym x ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(self, ls) -> (relName, self <<< child) : ls) mkSelf mkLinks
    where
      relName = symbolVal (Proxy @sym)
      mkSelf = toRelationLink (Proxy @api)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m
      child = toTemplatedLink (Proxy @(MkPrefix '[DeepQuery sym x] verb))

instance
  ( verb ~ Verb method status cts a
  , AllMime cts, ReflectMethod method
  , api ~ MkPrefix apiCs verb
  , HasRelationLink api
  , KnownSymbol sym
  , HasTemplatedLink (MkPrefix '[QueryFlag sym] verb)
  , BuildLayerLinks ('Layer apiCs cs verb) m
  , buildLinksFun ~ (ReplaceHandler (ServerT api m) [(String, RelationLink)])
  , PolyvariadicComp2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun)
  , Return2 (MkLink api RelationLink) buildLinksFun (IsFun buildLinksFun) ~ (RelationLink, [(String, RelationLink)])
  , Replace2 (MkLink api RelationLink) buildLinksFun [(String, RelationLink)] (IsFun buildLinksFun) ~ buildLinksFun
  ) => BuildLayerLinks ('Layer apiCs (QueryFlag sym ': cs) verb) m where
  buildLayerLinks _ m = pcomp2 (\(self, ls) -> (relName, self <<< child) : ls) mkSelf mkLinks
    where
      relName = symbolVal (Proxy @sym)
      mkSelf = toRelationLink (Proxy @api)
      mkLinks = buildLayerLinks (Proxy @('Layer apiCs cs verb)) m
      child = toTemplatedLink (Proxy @(MkPrefix '[QueryFlag sym] verb))