{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-}

module DotEnv (DotEnv, EnvPath(..), loadEnv, getEnv) where

import Data.ByteString (ByteString)
import Data.HashMap.Strict (HashMap)

import qualified Data.ByteString.Char8 as BS
import qualified Data.HashMap.Strict as HashMap

-- | A DotEnv is a HashMap of ByteString keys and values
type DotEnv = HashMap ByteString ByteString

-- | An EnvPath is either a path to a .env file or the default .env file
data EnvPath = EnvPath FilePath | DefaultEnv

-- | Convert an EnvPath to a FilePath
fromEnvPath :: EnvPath -> FilePath 
fromEnvPath :: EnvPath -> FilePath
fromEnvPath (EnvPath FilePath
path) = FilePath
path 
fromEnvPath EnvPath
DefaultEnv = FilePath
".env"

-- | Load a DotEnv from an EnvPath
loadEnv :: EnvPath -> IO DotEnv
loadEnv :: EnvPath -> IO DotEnv
loadEnv EnvPath
path = [(ByteString, ByteString)] -> DotEnv
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList ([(ByteString, ByteString)] -> DotEnv)
-> (ByteString -> [(ByteString, ByteString)])
-> ByteString
-> DotEnv
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> (ByteString, ByteString))
-> [ByteString] -> [(ByteString, ByteString)]
forall a b. (a -> b) -> [a] -> [b]
map ByteString -> (ByteString, ByteString)
parseEnvLine ([ByteString] -> [(ByteString, ByteString)])
-> (ByteString -> [ByteString])
-> ByteString
-> [(ByteString, ByteString)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
BS.lines (ByteString -> DotEnv) -> IO ByteString -> IO DotEnv
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO ByteString
BS.readFile (EnvPath -> FilePath
fromEnvPath EnvPath
path)

-- | Get a value from a DotEnv
--
-- Format: key=value
getEnv :: DotEnv -> ByteString -> Maybe ByteString
getEnv :: DotEnv -> ByteString -> Maybe ByteString
getEnv = (ByteString -> DotEnv -> Maybe ByteString)
-> DotEnv -> ByteString -> Maybe ByteString
forall a b c. (a -> b -> c) -> b -> a -> c
flip ByteString -> DotEnv -> Maybe ByteString
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup

-- | Parse a line from a .env file
parseEnvLine :: ByteString -> (ByteString, ByteString)
parseEnvLine :: ByteString -> (ByteString, ByteString)
parseEnvLine ByteString
x = let (ByteString
key, HasCallStack => ByteString -> ByteString
ByteString -> ByteString
BS.tail -> ByteString
value) = (Char -> Bool) -> ByteString -> (ByteString, ByteString)
BS.break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'=') ByteString
x in (ByteString
key, ByteString
value)