{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} -- | -- Module: Salak -- Copyright: (c) 2019 Daniel YU -- License: BSD3 -- Maintainer: leptonyu@gmail.com -- Stability: experimental -- Portability: portable -- -- Configuration Loader for Production in Haskell. -- module Salak( -- * How to use this library -- $use -- * Source Selector , SourcePack , SourcePackT , Reload(..) , load , loadJSON , loadYaml , loadEnv -- * Salak , defaultLoadSalak , loadSalak , PropConfig(..) , HasSourcePack(..) , fetch , require -- * Utilities , ReloadableSourcePack , runReloadable , Prop , FromProp(..) , PResult(..) , readPrimitive , readSelect , err ) where import Control.Monad (unless) import Control.Monad.IO.Class (MonadIO) import Control.Monad.Reader import Data.Default import Data.Text (Text) import Salak.Dynamic import Salak.Env import Salak.Json import Salak.Prop import Salak.Types data PropConfig = PropConfig { configFileKey :: Maybe String , commandLine :: ParseCommandLine } instance Default PropConfig where def = PropConfig Nothing defaultParseCommandLine loadSalak :: Monad m => ReaderT SourcePack m a -> SourcePackT m () -> m a loadSalak a spm = do (es, sp) <- runSourcePackT spm unless (null es) $ do fail (head es) runReaderT a sp defaultLoadSalak :: MonadIO m => PropConfig -> ReaderT SourcePack m a -> m a defaultLoadSalak PropConfig{..} a = loadSalak a $ do loadCommandLine commandLine loadEnv go configFileKey where go (Just file) = loadYaml file go _ = return () class Monad m => HasSourcePack m where askSourcePack :: m SourcePack fetch :: (HasSourcePack m, FromProp a) => Text -> m (Either String a) fetch key = askSourcePack >>= return . search key require :: (HasSourcePack m, FromProp a) => Text -> m a require k = do x <- fetch k case x of Left e -> fail e Right v -> return v instance Monad m => HasSourcePack (ReaderT SourcePack m) where askSourcePack = ask -- $use -- -- | This library default a standard configuration load process. It can load properties from `CommandLine`, `Environment`, -- `JSON value` and `Yaml` files. They all load to the same format `SourcePack`. Earler property source has higher order -- to load property. For example: -- -- > CommandLine: --package.a.enabled=true -- > Environment: PACKAGE_A_ENABLED: false -- -- > lookup "package.a.enabled" properties => Just True -- -- `CommandLine` has higher order then `Environment`, for the former load properties earler then later. -- -- Usage: -- -- > data Config = Config -- > { name :: Text -- > , dir :: Maybe Text -- > , ext :: Int -- > } deriving (Eq, Show) -- > -- > instance FromProp Config where -- > fromProp = Config -- > <$> "user" -- > <*> "pwd" -- > <*> "ext" .?= 1 -- -- > main = do -- > c :: Config <- defaultLoadSalak def $ require "" -- > print c -- -- > λ> c -- > Config {name = "daniel", dir = Nothing, ext = 1}