packages feed

haskell-admin-core-1.0.0.0: src/Admin/Components/ComponentsClass.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Admin.Components.ComponentsClass
  ( with
  , Components(..)
  ) where

import Admin.Components.Component
import Admin.Components.ComponentDescription
import Admin.Components.ComponentList
import Admin.Components.Internal.TypeLevel
import Data.Version
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import Servant

type ToServantAPI names api = (ManySymbolVal names, HasServer api '[])

class (ToServantAPI names apis) =>
      Components a (names :: [Symbol]) apis
  | a -> names apis
  where
  serveAll' :: a -> Server apis
  describe :: a -> [ComponentDescription]

instance (KnownSymbol name, HasServer api '[]) =>
         Components (Component name api) '[ name] (name :> api) where
  serveAll' c = server c
  describe c =
    let n = symbolVal (Proxy :: Proxy name)
        v = version c
     in [ComponentDescription n v]

instance (ToServantAPI names apis) =>
         Components (ComponentList names apis) names apis where
  serveAll' c = serveAll c
  describe c = descriptionsOf c

getVersions :: Components a names apis => a -> [Version]
getVersions = map componentVersion . describe

infixr 9 `with`

-- | Compose Components
--
-- This function combines a 'Component' with another 'Component',
-- or with a 'ComponentList', producing a 'ComponentList'.
--
-- It collects the names of the components,
-- and keeps the expected API structure.
--
-- Usage:
-- >>> componentA `with` componentB `with` componentC
with ::
     (Components a names apis)
  => Component name api
  -> a
  -> ComponentList (name : names) ((name :> api) :<|> apis)
with new lst =
  ComponentList (server new :<|> serveAll' lst) (version new : getVersions lst)