{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
module Configuration.Dotenv
(
load
, loadFile
, loadSafeFile
, parseFile
, onMissingFile
, module Configuration.Dotenv.Types
, ValidatorMap
, defaultValidatorMap
)
where
import Control.Monad (liftM, when)
import Configuration.Dotenv.Parse (configParser)
import Configuration.Dotenv.ParsedVariable (interpolateParsedVariables)
import Configuration.Dotenv.Scheme
import Configuration.Dotenv.Scheme.Types (ValidatorMap, defaultValidatorMap)
import Configuration.Dotenv.Types (Config(..))
import Control.Monad.Catch
import Control.Monad.IO.Class (MonadIO(..))
import Data.List (union, intersectBy, unionBy)
import System.Directory (doesFileExist)
import System.Environment (lookupEnv)
import System.IO.Error (isDoesNotExistError)
import Text.Megaparsec (parse, errorBundlePretty)
#if MIN_VERSION_base(4,7,0)
import System.Environment (getEnvironment, setEnv)
#else
import System.Environment.Compat (getEnvironment, setEnv)
#endif
load ::
MonadIO m =>
Bool
-> [(String, String)]
-> m ()
load override = mapM_ (applySetting override)
loadFile
:: MonadIO m
=> Config
-> m [(String, String)]
loadFile Config{..} = do
environment <- liftIO getEnvironment
readedVars <- concat `liftM` mapM parseFile configPath
neededVars <- concat `liftM` mapM parseFile configExamplePath
let coincidences = (environment `union` readedVars) `intersectEnvs` neededVars
cmpEnvs env1 env2 = fst env1 == fst env2
intersectEnvs = intersectBy cmpEnvs
unionEnvs = unionBy cmpEnvs
vars =
if (not . null) neededVars
then
if length neededVars == length coincidences
then readedVars `unionEnvs` neededVars
else error $ "Missing env vars! Please, check (this/these) var(s) (is/are) set:" ++ concatMap ((++) " " . fst) neededVars
else readedVars
mapM (applySetting configOverride) vars
parseFile ::
MonadIO m =>
FilePath
-> m [(String, String)]
parseFile f = do
contents <- liftIO $ readFile f
case parse configParser f contents of
Left e -> error $ errorBundlePretty e
Right options -> liftIO $ interpolateParsedVariables options
applySetting :: MonadIO m => Bool -> (String, String) -> m (String, String)
applySetting override (key, value) =
if override
then liftIO (setEnv key value) >> return (key, value)
else do
res <- liftIO $ lookupEnv key
case res of
Nothing -> liftIO $ setEnv key value >> return (key, value)
Just _ -> return (key, value)
onMissingFile :: MonadCatch m
=> m a
-> m a
-> m a
onMissingFile f h = catchIf isDoesNotExistError f (const h)
loadSafeFile
:: MonadIO m
=> ValidatorMap
-> FilePath
-> Config
-> m [(String, String)]
loadSafeFile mapFormat schemaFile config = do
envs <- loadFile config
exists <- liftIO $ doesFileExist schemaFile
when exists $
liftIO (readScheme schemaFile >>= checkConfig mapFormat envs . checkScheme)
return envs