{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Servant.Benchmark.HasGenerator where

import Control.Monad (replicateM)
import Data.Data (Proxy (..))
import Data.Kind (Type)
import Servant.API
import Servant.Benchmark.Endpoint (Endpoint)
import Servant.Benchmark.Generator
import Servant.Benchmark.HasEndpoint

{- | HasGenerator provides combined type and value level interpretation of an API,
  producing corresponding `Endpoint` values.

  Instructions on forming a `Generator` type can be found on the module documentation.
-}
class HasGenerator api where
    generate :: Proxy api -> Generator api -> IO [Endpoint]

instance {-# OVERLAPPABLE #-} (HasEndpoint a, HasGenerator b) => HasGenerator (a :<|> b) where
    generate :: Proxy (a :<|> b) -> Generator (a :<|> b) -> IO [Endpoint]
    generate :: Proxy (a :<|> b) -> Generator (a :<|> b) -> IO [Endpoint]
generate Proxy (a :<|> b)
_ (hLeft :|: hRight) = do
        [Endpoint]
left <- Proxy a -> Generator a -> IO [Endpoint]
forall api.
HasGenerator api =>
Proxy api -> Generator api -> IO [Endpoint]
generate (Proxy a
forall k (t :: k). Proxy t
Proxy @a) Generator a
hLeft
        [Endpoint]
right <- Proxy b -> Generator b -> IO [Endpoint]
forall api.
HasGenerator api =>
Proxy api -> Generator api -> IO [Endpoint]
generate (Proxy b
forall k (t :: k). Proxy t
Proxy @b) Generator b
hRight
        [Endpoint] -> IO [Endpoint]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Endpoint] -> IO [Endpoint]) -> [Endpoint] -> IO [Endpoint]
forall a b. (a -> b) -> a -> b
$ [Endpoint]
left [Endpoint] -> [Endpoint] -> [Endpoint]
forall a. [a] -> [a] -> [a]
++ [Endpoint]
right

instance {-# OVERLAPPABLE #-} forall (a :: Type). HasEndpoint a => HasGenerator a where
    generate :: Proxy a -> Generator a -> IO [Endpoint]
generate Proxy a
p Generator a
gen =
        Int -> IO Endpoint -> IO [Endpoint]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM (Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word -> Int) -> Word -> Int
forall a b. (a -> b) -> a -> b
$ Proxy a -> Generator a -> Word
forall api. HasEndpoint api => Proxy api -> Generator api -> Word
weight Proxy a
p Generator a
gen) (Proxy a -> Generator a -> IO Endpoint
forall api.
HasEndpoint api =>
Proxy api -> Generator api -> IO Endpoint
getEndpoint Proxy a
p Generator a
gen)