{-# 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' :: Component name api -> Server (name :> api)
serveAll' Component name api
c = Component name api -> Server api
forall (name :: Symbol) api. Component name api -> Server api
server Component name api
c
  describe :: Component name api -> [ComponentDescription]
describe Component name api
c =
    let n :: String
n = Proxy name -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (Proxy name
forall k (t :: k). Proxy t
Proxy :: Proxy name)
        v :: Version
v = Component name api -> Version
forall (name :: Symbol) api. Component name api -> Version
version Component name api
c
     in [String -> Version -> ComponentDescription
ComponentDescription String
n Version
v]

instance (ToServantAPI names apis) =>
         Components (ComponentList names apis) names apis where
  serveAll' :: ComponentList names apis -> Server apis
serveAll' ComponentList names apis
c = ComponentList names apis -> Server apis
forall (names :: [Symbol]) apis.
ComponentList names apis -> Server apis
serveAll ComponentList names apis
c
  describe :: ComponentList names apis -> [ComponentDescription]
describe ComponentList names apis
c = ComponentList names apis -> [ComponentDescription]
forall (names :: [Symbol]) apis.
ManySymbolVal names =>
ComponentList names apis -> [ComponentDescription]
descriptionsOf ComponentList names apis
c

getVersions :: Components a names apis => a -> [Version]
getVersions :: a -> [Version]
getVersions = (ComponentDescription -> Version)
-> [ComponentDescription] -> [Version]
forall a b. (a -> b) -> [a] -> [b]
map ComponentDescription -> Version
componentVersion ([ComponentDescription] -> [Version])
-> (a -> [ComponentDescription]) -> a -> [Version]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> [ComponentDescription]
forall a (names :: [Symbol]) apis.
Components a names apis =>
a -> [ComponentDescription]
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 :: Component name api
-> a -> ComponentList (name : names) ((name :> api) :<|> apis)
with Component name api
new a
lst =
  Server ((name :> api) :<|> apis)
-> [Version]
-> ComponentList (name : names) ((name :> api) :<|> apis)
forall (names :: [Symbol]) apis.
Server apis -> [Version] -> ComponentList names apis
ComponentList (Component name api -> Server api
forall (name :: Symbol) api. Component name api -> Server api
server Component name api
new Server api
-> ServerT apis Handler -> Server api :<|> ServerT apis Handler
forall a b. a -> b -> a :<|> b
:<|> a -> ServerT apis Handler
forall a (names :: [Symbol]) apis.
Components a names apis =>
a -> Server apis
serveAll' a
lst) (Component name api -> Version
forall (name :: Symbol) api. Component name api -> Version
version Component name api
new Version -> [Version] -> [Version]
forall a. a -> [a] -> [a]
: a -> [Version]
forall a (names :: [Symbol]) apis.
Components a names apis =>
a -> [Version]
getVersions a
lst)