{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
module Boots.Factory.Application(
HasApp(..)
, AppEnv(..)
, askExt
, buildApp
) where
import Boots.Factory.Logger
import Boots.Factory.Salak
import Boots.Health
import Boots.Prelude
import Boots.Random
import Control.Concurrent (setNumCapabilities)
import Control.Monad.Factory
import Data.IORef
import Data.Maybe
import Data.Text (Text)
import Data.Version (Version)
import Salak
import Salak.Yaml
class HasApp env ext where
askApp :: Lens' env (AppEnv ext)
instance HasApp (AppEnv ext) ext where
askApp = id
{-# INLINE askApp #-}
instance HasLogger (AppEnv ext) where
askLogger = lens logFunc (\x y -> x {logFunc = y})
{-# INLINE askLogger #-}
instance HasSalak (AppEnv ext) where
askSalak = lens configure (\x y -> x {configure = y})
{-# INLINE askSalak #-}
instance HasRandom (AppEnv ext) where
askRandom = lens randSeed (\x y -> x {randSeed = y})
{-# INLINE askRandom #-}
instance HasHealth (AppEnv ext) where
askHealth = lens health (\x y -> x {health = y})
{-# INLINE askHealth #-}
data AppEnv ext = AppEnv
{ name :: Text
, instanceId :: Text
, version :: Version
, logFunc :: LogFunc
, configure :: Salak
, randSeed :: RD
, health :: IO Health
, ext :: ext
}
askExt :: Lens' (AppEnv ext) ext
askExt = lens ext (\x y -> x {ext = y})
data AppConfig = AppConfig
{ appName :: Maybe Text
, numCapabilities :: Maybe Int
, randomType :: RDType
}
instance FromProp m AppConfig where
{-# INLINE fromProp #-}
fromProp = AppConfig
<$> "name"
<*> "num-capabilities"
<*> "random.type" .?= RDMVar
buildApp
:: (MonadIO m, MonadMask m)
=> String
-> Version
-> ParseCommandLine
-> ext
-> Factory m () (AppEnv ext)
buildApp confName version mcli ext = do
mv <- liftIO $ newIORef []
configure <- liftIO $ runSalak def
{ configName = confName
, loggerF = \c s -> modifyIORef' mv ((c,s):)
, loadExt = loadByExt YAML
, commandLine = mcli
} askSourcePack
within configure $ do
AppConfig{..} <- require "application"
liftIO $ whenJust numCapabilities setNumCapabilities
let name = fromMaybe (fromString confName) appName
randSeed <- liftIO $ newRD randomType
instanceId <- liftIO $ hex32 <$> unRD randSeed nextWord64
logFunc <- buildLogger (name <> "," <> instanceId)
let
health = emptyHealth
lf c s = logCS c LevelTrace (toLogStr s) logFunc
liftIO $ atomicModifyIORef' mv ([],) >>= sequence_ . reverse . fmap (uncurry lf)
setLogF lf
return AppEnv{..}