packages feed

domaindriven-0.5.0: src/DomainDriven/Server/Helpers.hs

module DomainDriven.Server.Helpers where

import Control.Monad
import Control.Monad.State
import Data.Generics.Product
import qualified Data.List as L
import DomainDriven.Internal.Text
import DomainDriven.Server.Types
import Language.Haskell.TH
import Language.Haskell.TH.Syntax (OccName (..))
import Lens.Micro
import Prelude

runServerGenM :: ServerGenState -> ServerGenM a -> Q a
runServerGenM s m = evalStateT (unServerGenM m) s

liftQ :: Q a -> ServerGenM a
liftQ m = ServerGenM $ lift m

withLocalState :: (ServerGenState -> ServerGenState) -> ServerGenM a -> ServerGenM a
withLocalState fs m = ServerGenM $ do
    startState <- get
    modify fs
    a <- unServerGenM m
    put startState
    pure a

mkUrlSegment :: ConstructorName -> ServerGenM UrlSegment
mkUrlSegment n = do
    opts <- gets (^. field @"info" . field @"options")
    pure $
        n
            ^. typed
                . unqualifiedString
                . to (opts ^. field @"renameConstructor")
                . to UrlSegment

unqualifiedString :: Lens' Name String
unqualifiedString = typed @OccName . typed

askTypeName :: ServerGenM Name
askTypeName = do
    si <- get
    let baseName :: String
        baseName =
            si ^. field @"info" . field @"baseGadt" . typed @Name . unqualifiedString

        cNames :: [String]
        cNames =
            si
                ^.. field @"info"
                    . typed @[ConstructorName]
                    . folded
                    . typed @Name
                    . unqualifiedString
        separator :: String
        separator = si ^. field @"info" . typed @ApiOptions . field @"typenameSeparator"

    pure . mkName . L.intercalate separator $ baseName : cNames

askApiTypeName :: ServerGenM Name
askApiTypeName = (unqualifiedString <>~ "Api") <$> askTypeName

askEndpointTypeName :: ServerGenM Name
askEndpointTypeName = (unqualifiedString <>~ "Endpoint") <$> askTypeName

askServerName :: ServerGenM Name
askServerName =
    (\n -> n & unqualifiedString %~ lowerFirst & unqualifiedString <>~ "Server")
        <$> askTypeName

askHandlerName :: ServerGenM Name
askHandlerName =
    (\n -> n & unqualifiedString %~ lowerFirst & unqualifiedString <>~ "Handler")
        <$> askTypeName

askBodyTag :: ConstructorName -> ServerGenM TyLit
askBodyTag cName = do
    constructorSegment <- mkUrlSegment cName
    gadtSegment <-
        gets (^. field @"info" . field @"options" . field @"bodyNameBase") >>= \case
            Just n -> pure $ UrlSegment n
            Nothing ->
                gets
                    ( ^.
                        field @"info"
                            . field @"currentGadt"
                            . typed
                            . to nameBase
                            . to UrlSegment
                    )

    separator <- gets (^. field @"info" . typed @ApiOptions . field @"typenameSeparator")
    pure
        . StrTyLit
        . L.intercalate separator
        $ (gadtSegment : [constructorSegment])
            ^.. folded
                . typed

enterApi :: ApiSpec -> ServerGenM a -> ServerGenM a
enterApi spec m = withLocalState (field @"info" %~ extendServerInfo) m
  where
    extendServerInfo :: ServerInfo -> ServerInfo
    extendServerInfo i =
        i & typed .~ spec ^. typed @ApiOptions & field @"currentGadt" .~ spec ^. typed

enterApiPiece :: ApiPiece -> ServerGenM a -> ServerGenM a
enterApiPiece p m = do
    newSegment <- mkUrlSegment (p ^. typed @ConstructorName)
    let extendServerInfo :: ServerInfo -> ServerInfo
        extendServerInfo i =
            i
                & (typed @[UrlSegment] <>~ [newSegment])
                & (typed @[ConstructorName] <>~ p ^. typed . to pure)
    withLocalState (field @"info" %~ extendServerInfo) m

hasJsonContentType :: HandlerSettings -> Bool
hasJsonContentType hs = case hs ^. field @"contentTypes" of
    AppT (AppT PromotedConsT (ConT n)) (SigT PromotedNilT (AppT ListT StarT)) ->
        nameBase n == "JSON"
    _ -> False