packages feed

dotenv-0.11.0.1: src/Configuration/Dotenv/ParsedVariable.hs

-- |
-- Module      :  Configuration.Dotenv.Types
-- Copyright   :  © 2015–2020 Stack Builders Inc.
-- License     :  MIT
--
-- Maintainer  :  Stack Builders <hackage@stackbuilders.com>
-- 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