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

-- | Interpotales parsed variables
interpolateParsedVariables :: [ParsedVariable] -> IO [(String, String)]
interpolateParsedVariables :: [ParsedVariable] -> IO [(String, String)]
interpolateParsedVariables = ([(String, String)] -> [(String, String)])
-> IO [(String, String)] -> IO [(String, String)]
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [(String, String)] -> [(String, String)]
forall a. [a] -> [a]
reverse (IO [(String, String)] -> IO [(String, String)])
-> ([ParsedVariable] -> IO [(String, String)])
-> [ParsedVariable]
-> IO [(String, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([(String, String)] -> ParsedVariable -> IO [(String, String)])
-> [(String, String)] -> [ParsedVariable] -> IO [(String, String)]
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) = ((String, String) -> [(String, String)] -> [(String, String)]
forall a. a -> [a] -> [a]
: [(String, String)]
previous) ((String, String) -> [(String, String)])
-> IO (String, String) -> IO [(String, String)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((,) String
name (String -> (String, String)) -> IO String -> IO (String, String)
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) = String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> IO String) -> String -> IO String
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 = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> IO [String] -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (VarFragment -> IO String) -> VarContents -> IO [String]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [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  ) = String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
value
interpolateFragment [(String, String)]
previous (VarInterpolation String
varname) = IO (Maybe String)
fromPreviousOrEnv IO (Maybe String) -> (Maybe String -> IO String) -> IO String
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= IO String -> (String -> IO String) -> Maybe String -> IO String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
"") String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
  where
    fromPreviousOrEnv :: IO (Maybe String)
fromPreviousOrEnv = (String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
varname [(String, String)]
previous Maybe String -> Maybe String -> Maybe String
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>) (Maybe String -> Maybe String)
-> IO (Maybe String) -> IO (Maybe String)
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) = ShowS
forall a. HasCallStack => [a] -> [a]
init ShowS -> IO String -> IO String
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 = (VarFragment -> String) -> VarContents -> String
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 ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ String
commandName String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
args
    fragmentToString (VarInterpolation String
value)                = String
value
    fragmentToString (VarLiteral String
value)                      = String
value