-- |
-- 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 (Int -> ParsedVariable -> ShowS
[ParsedVariable] -> ShowS
ParsedVariable -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ParsedVariable] -> ShowS
$cshowList :: [ParsedVariable] -> ShowS
show :: ParsedVariable -> String
$cshow :: ParsedVariable -> String
showsPrec :: Int -> ParsedVariable -> ShowS
$cshowsPrec :: Int -> ParsedVariable -> ShowS
Show, ParsedVariable -> ParsedVariable -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ParsedVariable -> ParsedVariable -> Bool
$c/= :: ParsedVariable -> ParsedVariable -> Bool
== :: ParsedVariable -> ParsedVariable -> Bool
$c== :: ParsedVariable -> ParsedVariable -> Bool
Eq)

-- | Variable name
type VarName = String

-- | Possible state of values
data VarValue
  = Unquoted VarContents
  | SingleQuoted VarContents
  | DoubleQuoted VarContents deriving (Int -> VarValue -> ShowS
[VarValue] -> ShowS
VarValue -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VarValue] -> ShowS
$cshowList :: [VarValue] -> ShowS
show :: VarValue -> String
$cshow :: VarValue -> String
showsPrec :: Int -> VarValue -> ShowS
$cshowsPrec :: Int -> VarValue -> ShowS
Show, VarValue -> VarValue -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VarValue -> VarValue -> Bool
$c/= :: VarValue -> VarValue -> Bool
== :: VarValue -> VarValue -> Bool
$c== :: VarValue -> VarValue -> Bool
Eq)

-- | List of VarFragment
type VarContents = [VarFragment]

-- | Placeholder for possible values
data VarFragment
  = VarInterpolation String
  | VarLiteral String
  | CommandInterpolation String [String] deriving (Int -> VarFragment -> ShowS
VarContents -> ShowS
VarFragment -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: VarContents -> ShowS
$cshowList :: VarContents -> ShowS
show :: VarFragment -> String
$cshow :: VarFragment -> String
showsPrec :: Int -> VarFragment -> ShowS
$cshowsPrec :: Int -> VarFragment -> ShowS
Show, VarFragment -> VarFragment -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VarFragment -> VarFragment -> Bool
$c/= :: VarFragment -> VarFragment -> Bool
== :: VarFragment -> VarFragment -> Bool
$c== :: VarFragment -> VarFragment -> Bool
Eq)

-- | Interpotales parsed variables
interpolateParsedVariables :: [ParsedVariable] -> IO [(String, String)]
interpolateParsedVariables :: [ParsedVariable] -> IO [(String, String)]
interpolateParsedVariables = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM [(String, String)] -> ParsedVariable -> IO [(String, String)]
addInterpolated []

addInterpolated :: [(String, String)] -> ParsedVariable -> IO [(String, String)]
addInterpolated :: [(String, String)] -> ParsedVariable -> IO [(String, String)]
addInterpolated [(String, String)]
previous (ParsedVariable String
name VarValue
value) = (forall a. a -> [a] -> [a]
: [(String, String)]
previous) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((,) String
name forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(String, String)] -> VarValue -> IO String
interpolate [(String, String)]
previous VarValue
value)

interpolate :: [(String, String)] -> VarValue -> IO String
interpolate :: [(String, String)] -> VarValue -> IO String
interpolate [(String, String)]
_        (SingleQuoted VarContents
contents) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ VarContents -> String
joinContents VarContents
contents
interpolate [(String, String)]
previous (DoubleQuoted VarContents
contents) = [(String, String)] -> VarContents -> IO String
interpolateContents [(String, String)]
previous VarContents
contents
interpolate [(String, String)]
previous (Unquoted     VarContents
contents) = [(String, String)] -> VarContents -> IO String
interpolateContents [(String, String)]
previous VarContents
contents

interpolateContents :: [(String, String)] -> VarContents -> IO String
interpolateContents :: [(String, String)] -> VarContents -> IO String
interpolateContents [(String, String)]
previous VarContents
contents = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ([(String, String)] -> VarFragment -> IO String
interpolateFragment [(String, String)]
previous) VarContents
contents

interpolateFragment :: [(String, String)] -> VarFragment -> IO String
interpolateFragment :: [(String, String)] -> VarFragment -> IO String
interpolateFragment [(String, String)]
_        (VarLiteral       String
value  ) = forall (m :: * -> *) a. Monad m => a -> m a
return String
value
interpolateFragment [(String, String)]
previous (VarInterpolation String
varname) = IO (Maybe String)
fromPreviousOrEnv forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (m :: * -> *) a. Monad m => a -> m a
return String
"") forall (m :: * -> *) a. Monad m => a -> m a
return
  where
    fromPreviousOrEnv :: IO (Maybe String)
fromPreviousOrEnv = (forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
varname [(String, String)]
previous forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO (Maybe String)
lookupEnv String
varname
interpolateFragment [(String, String)]
_ (CommandInterpolation String
commandName [String]
args) = forall a. [a] -> [a]
init forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CreateProcess -> String -> IO String
readCreateProcess (String -> [String] -> CreateProcess
proc String
commandName [String]
args) String
""

joinContents :: VarContents -> String
joinContents :: VarContents -> String
joinContents = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap VarFragment -> String
fragmentToString
  where
    fragmentToString :: VarFragment -> String
fragmentToString (CommandInterpolation String
commandName [String]
args) = [String] -> String
unwords forall a b. (a -> b) -> a -> b
$ String
commandName forall a. a -> [a] -> [a]
: [String]
args
    fragmentToString (VarInterpolation String
value)                = String
value
    fragmentToString (VarLiteral String
value)                      = String
value