{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Kind
import GHC.TypeLits
import Text.Read (readMaybe)
data Rating = Bad | Good | Great
deriving Show
data ServiceStatus = Ok | Down
deriving Show
data Get (a :: Type)
data Capture (a :: Type)
data a :<|> b = a :<|> b
infixr 8 :<|>
data (a :: k) :> (b :: Type)
infixr 9 :>
type BookID = Int
type BookInfoAPI = Get ServiceStatus
:<|> "title" :> Capture BookID :> Get String
:<|> "year" :> Capture BookID :> Get Int
:<|> "rating" :> Capture BookID :> Get Rating
type HandlerAction a = IO a
-- This type is not used anywhere
-- BookInfoAPIImpl is the same as Server BookInfoAPI
type BookInfoAPIImpl = HandlerAction ServiceStatus
:<|> (BookID -> HandlerAction String)
:<|> (BookID -> HandlerAction Int)
:<|> (BookID -> HandlerAction Rating)
type family Server layout :: Type
type instance Server (Get a) = HandlerAction a
type instance Server (a :<|> b) = Server a :<|> Server b
type instance Server ((s :: Symbol) :> r) = Server r
type instance Server (Capture a :> r) = a -> Server r
impl :: Server BookInfoAPI
impl = pure Ok
:<|> title
:<|> year
:<|> rating
where
title _ = pure "Haskell in Depth"
year _ = pure 2020
rating _ = pure Great
impl2 :: Server BookInfoAPI
impl2 = pure Down
:<|> title
:<|> year
:<|> rating
where
notImplemented = ioError (userError "not implemented")
title _ = notImplemented
year _ = notImplemented
rating _ = notImplemented
type Request = [String]
encode :: Show a => IO a -> IO String
encode m = show <$> m
route :: Server BookInfoAPI -> Request -> Maybe (IO String)
route (root :<|> _) [] = pure $ encode $ root
route (_ :<|> title :<|> year :<|> rating) [op, bid'] = do
bid <- readMaybe bid'
case op of
"title" -> pure $ title bid
"year" -> pure $ encode $ year bid
"rating" -> pure $ encode $ rating bid
_ -> Nothing
route _ _ = Nothing
get :: Server BookInfoAPI -> Request -> IO String
get impl xs =
case route impl xs of
Just m -> m
Nothing -> pure "Wrong request"
check impl = do
b <- get impl []
answer <- get impl ["year", "7548"]
putStrLn (if b == "Ok" && answer == "2020"
then "OK"
else "Wrong answer!")
main = check impl