module DotEnv.Micro (loadDotEnv) where
import Control.Monad.IO.Class (MonadIO(..))
import Data.Foldable (traverse_)
import Data.Functor (void)
import Data.List (sortOn)
import Data.Maybe (listToMaybe, fromMaybe)
import Data.Ord (Down(..))
import System.Environment (lookupEnv, setEnv)
import qualified Text.ParserCombinators.ReadP as P (ReadP, readP_to_S, char, munch, sepBy1)
import System.Directory (doesFileExist)
loadDotEnv :: MonadIO m =>
Maybe FilePath
-> m ()
loadDotEnv :: forall (m :: * -> *). MonadIO m => Maybe FilePath -> m ()
loadDotEnv Maybe FilePath
mfp = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
let
fpath :: FilePath
fpath = forall a. a -> Maybe a -> a
fromMaybe FilePath
".env" Maybe FilePath
mfp
Bool
ok <- FilePath -> IO Bool
doesFileExist FilePath
fpath
if Bool
ok
then
do
Maybe [(FilePath, FilePath)]
mp <- FilePath -> Maybe [(FilePath, FilePath)]
parseDotEnv forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO FilePath
readFile FilePath
fpath
case Maybe [(FilePath, FilePath)]
mp of
Just [(FilePath, FilePath)]
es -> forall (m :: * -> *). MonadIO m => [(FilePath, FilePath)] -> m ()
setEnvs [(FilePath, FilePath)]
es
Maybe [(FilePath, FilePath)]
Nothing -> forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
unwords [FilePath
"dotenv: cannot parse", FilePath
fpath]
else
do
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
unwords [FilePath
"dotenv:", FilePath
fpath, FilePath
"file not found"]
setEnvs :: MonadIO m => [(String, String)] -> m ()
setEnvs :: forall (m :: * -> *). MonadIO m => [(FilePath, FilePath)] -> m ()
setEnvs = forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ forall {m :: * -> *}. MonadIO m => (FilePath, FilePath) -> m ()
insf
where
insf :: (FilePath, FilePath) -> m ()
insf (FilePath
k, FilePath
v) = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
Maybe FilePath
me <- FilePath -> IO (Maybe FilePath)
lookupEnv FilePath
k
case Maybe FilePath
me of
Just FilePath
_ -> do
FilePath -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
unwords [FilePath
"Variable", FilePath
k, FilePath
"already set in environment"]
Maybe FilePath
Nothing -> do
FilePath -> FilePath -> IO ()
setEnv FilePath
k FilePath
v
FilePath -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
unwords [FilePath
"dotenv:", FilePath
k, FilePath
"set"]
parseDotEnv :: String
-> Maybe [(String, String)]
parseDotEnv :: FilePath -> Maybe [(FilePath, FilePath)]
parseDotEnv = forall (t :: * -> *) a.
Foldable t =>
ReadP (t a) -> FilePath -> Maybe (t a)
parse1 ReadP [(FilePath, FilePath)]
keyValues
keyValues :: P.ReadP [(String, String)]
keyValues :: ReadP [(FilePath, FilePath)]
keyValues = forall a sep. ReadP a -> ReadP sep -> ReadP [a]
P.sepBy1 ReadP (FilePath, FilePath)
keyValue (Char -> ReadP Char
P.char Char
'\n') forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Char -> ReadP Char
P.char Char
'\n'
keyValue :: P.ReadP (String, String)
keyValue :: ReadP (FilePath, FilePath)
keyValue = do
FilePath
k <- ReadP FilePath
keyP
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ Char -> ReadP Char
P.char Char
'='
FilePath
v <- ReadP FilePath
valueP
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FilePath
k, FilePath
v)
keyP, valueP :: P.ReadP String
keyP :: ReadP FilePath
keyP = (Char -> Bool) -> ReadP FilePath
P.munch (forall a. Eq a => a -> a -> Bool
/= Char
'=')
valueP :: ReadP FilePath
valueP = (Char -> Bool) -> ReadP FilePath
P.munch (forall a. Eq a => a -> a -> Bool
/= Char
'\n')
parse1 :: Foldable t => P.ReadP (t a) -> String -> Maybe (t a)
parse1 :: forall (t :: * -> *) a.
Foldable t =>
ReadP (t a) -> FilePath -> Maybe (t a)
parse1 ReadP (t a)
p FilePath
str = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst forall a b. (a -> b) -> a -> b
$ forall a. [a] -> Maybe a
listToMaybe forall a b. (a -> b) -> a -> b
$ forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (forall a. a -> Down a
Down forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) (forall a. ReadP a -> ReadS a
P.readP_to_S ReadP (t a)
p FilePath
str)