{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Salak(
loadAndRunSalak
, runSalak
, runSalakWith
, PropConfig(..)
, LoadSalakT
, loadCommandLine
, ParseCommandLine
, defaultParseCommandLine
, loadEnv
, loadMock
, ExtLoad
, loadByExt
, HasLoad(..)
, (:|:)(..)
, RunSalakT
, liftNT
, HasSourcePack(..)
, fetch
, require
, ReloadResult(..)
, exec
, requireD
, SourcePack
, Priority
, Value(..)
, Prop
, FromProp(..)
, FromEnumProp(..)
, readPrimitive
, PResult(..)
, (.?=)
, (.?:)
) where
import Control.Monad (unless)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Reader
import Control.Monad.State
import Data.Default
import Data.Text (Text, cons)
import Salak.Load.Dynamic
import Salak.Load.Env
import Salak.Prop
import Salak.Types
import Salak.Types.Value
import System.Directory
import System.FilePath ((</>))
data PropConfig = PropConfig
{ configName :: Maybe String
, configDirKey :: Text
, searchCurrent :: Bool
, searchHome :: Bool
, commandLine :: ParseCommandLine
, loadExt :: FilePath -> LoadSalakT IO ()
}
instance Default PropConfig where
def = PropConfig
Nothing
"salak.conf.dir"
True
False
defaultParseCommandLine
(\_ -> return ())
loadAndRunSalak
:: MonadIO m
=> LoadSalakT m ()
-> RunSalakT m a
-> m a
loadAndRunSalak spm a = do
sp <- runLoadT Nothing spm
let es = errs sp
unless (null es) $ fail (head es)
runT a sp
type ExtLoad = (String, FilePath -> LoadSalakT IO ())
class HasLoad a where
loaders :: a -> [ExtLoad]
data a :|: b = a :|: b
infixr 3 :|:
instance (HasLoad a, HasLoad b) => HasLoad (a :|: b) where
loaders (a :|: b) = loaders a ++ loaders b
loadByExt :: (HasLoad a, MonadIO m) => a -> FilePath -> LoadSalakT m ()
loadByExt xs f = mapM_ go (loaders xs)
where
go (ext, ly) = tryLoadFile (jump . ly) $ f ++ "." ++ ext
runSalak :: MonadIO m => PropConfig -> RunSalakT m a -> m a
runSalak PropConfig{..} = loadAndRunSalak $ do
loadCommandLine commandLine
loadEnv
forM_ configName $ forM_
[ require configDirKey
, ifS searchCurrent getCurrentDirectory
, ifS searchHome getHomeDirectory
] . loadConf
where
ifS True gxd = Just <$> liftIO gxd
ifS _ _ = return Nothing
loadConf n mf = mf >>= mapM_ (jump . loadExt . (</> n))
runSalakWith :: (HasLoad file, MonadIO m) => String -> file -> RunSalakT m a -> m a
runSalakWith name a = runSalak def { configName = Just name, loadExt = loadByExt a}
class Monad m => HasSourcePack m where
askSourcePack :: m SourcePack
logSP :: Text -> m ()
logSP _ = return ()
readLogs :: m [Text]
readLogs = return []
instance MonadIO m => HasSourcePack (RunSalakT m) where
askSourcePack = askRSP
logSP key = RunSalakT $ modify $ \rsp -> rsp { logs = key : logs rsp}
readLogs = RunSalakT $ do
rsp <- get
let ls = logs rsp
put rsp { logs = [] }
return (reverse ls)
instance Monad m => HasSourcePack (LoadSalakT m) where
askSourcePack = LoadSalakT get
fetch
:: (HasSourcePack m, FromProp a)
=> Text
-> m (Either String a)
fetch key = logSP key >> search key <$> askSourcePack
require
:: (HasSourcePack m, FromProp a)
=> Text
-> m a
require k = fetch k >>= either error return
requireD
:: (MonadIO m, FromProp a)
=> Text
-> RunSalakT m (IO a)
requireD k = logSP ('@' `cons` k) >> search' k >>= either error return