-- | -- Module : Configuration.Dotenv.Types -- Copyright : © 2015–2020 Stack Builders Inc. -- License : MIT -- -- Maintainer : Stack Builders -- Stability : experimental -- Portability : portable -- -- Helpers to interpolate environment variables module Configuration.Dotenv.ParsedVariable (ParsedVariable(..), VarName, VarValue(..), VarContents, VarFragment(..), interpolateParsedVariables) where import Configuration.Dotenv.Environment (lookupEnv) import Control.Applicative ((<|>)) import Control.Monad (foldM) import System.Process (proc, readCreateProcess) -- | Name and value pair data ParsedVariable = ParsedVariable VarName VarValue deriving (Show, Eq) -- | Variable name type VarName = String -- | Possible state of values data VarValue = Unquoted VarContents | SingleQuoted VarContents | DoubleQuoted VarContents deriving (Show, Eq) -- | List of VarFragment type VarContents = [VarFragment] -- | Placeholder for possible values data VarFragment = VarInterpolation String | VarLiteral String | CommandInterpolation String [String] deriving (Show, Eq) -- | Interpotales parsed variables interpolateParsedVariables :: [ParsedVariable] -> IO [(String, String)] interpolateParsedVariables = fmap reverse . foldM addInterpolated [] addInterpolated :: [(String, String)] -> ParsedVariable -> IO [(String, String)] addInterpolated previous (ParsedVariable name value) = (: previous) <$> ((,) name <$> interpolate previous value) interpolate :: [(String, String)] -> VarValue -> IO String interpolate _ (SingleQuoted contents) = return $ joinContents contents interpolate previous (DoubleQuoted contents) = interpolateContents previous contents interpolate previous (Unquoted contents) = interpolateContents previous contents interpolateContents :: [(String, String)] -> VarContents -> IO String interpolateContents previous contents = concat <$> mapM (interpolateFragment previous) contents interpolateFragment :: [(String, String)] -> VarFragment -> IO String interpolateFragment _ (VarLiteral value ) = return value interpolateFragment previous (VarInterpolation varname) = fromPreviousOrEnv >>= maybe (return "") return where fromPreviousOrEnv = (lookup varname previous <|>) <$> lookupEnv varname interpolateFragment _ (CommandInterpolation commandName args) = init <$> readCreateProcess (proc commandName args) "" joinContents :: VarContents -> String joinContents = concatMap fragmentToString where fragmentToString (CommandInterpolation commandName args) = unwords $ commandName : args fragmentToString (VarInterpolation value) = value fragmentToString (VarLiteral value) = value