rfc-env-0.0.0.1: src/RFC/Env.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
module RFC.Env
( isDevelopment
, readHost
, readPort
, readAppSlug
, forDevOnly
, envWithDefault
, envWithDevDefault
, module System.Envy
) where
import Control.Applicative
import Data.Time.Clock
import Data.Word
import Network ( PortID (..) )
import RFC.Prelude
import System.Environment
import System.Envy
import Text.Read ( readEither, readMaybe )
envWithDefault :: Var a => String -> a -> Parser a
envWithDefault name defaultValue = fromMaybe defaultValue <$> envMaybe name
{-# INLINE envWithDefault #-}
envWithDevDefault :: Var a => String -> a -> Parser a
envWithDevDefault name defaultValue =
if isDevelopment then
envWithDefault name defaultValue
else
env name
{-# INLINE envWithDevDefault #-}
isDevelopment :: Bool
isDevelopment =
#ifdef DEVELOPMENT
True
#else
False
#endif
{-# INLINE isDevelopment #-}
forDevOnly :: a -> Maybe a
forDevOnly defaultValue =
if isDevelopment then
Just defaultValue
else
Nothing
{-# INLINE forDevOnly #-}
readAppSlug :: (MonadIO m, MonadFail m) => m String
readAppSlug = readEnvStringWithDevDefault "APP_SLUG" "dev"
{-# INLINE readAppSlug #-}
readHost :: (MonadIO m, MonadFail m) => m String
readHost =
readEnvStringWithDevDefault "HOST" "localhost"
{-# INLINE readHost #-}
readPort :: (MonadIO m, MonadFail m) => Word16 -> m Word16
readPort = readEnvWithDevDefault "PORT"
{-# INLINE readPort #-}
readEnvStringWithDefault :: (MonadIO m) => String -> String -> m String
readEnvStringWithDefault name defaultValue =
fromMaybe defaultValue <$> readEnvString name
{-# INLINE readEnvStringWithDefault #-}
readEnvStringWithDevDefault :: (MonadIO m, MonadFail m) => String -> String -> m String
readEnvStringWithDevDefault =
if isDevelopment then
readEnvStringWithDefault
else
(\name _ -> do -- Need to join the MonadFail instances
result <- readEnvString name
case result of
Left err -> fail err
Right x -> return x
)
{-# INLINE readEnvStringWithDevDefault #-}
readEnvWithDefault :: (MonadIO m, Read a) => String -> a -> m a
readEnvWithDefault name defaultValue = do
maybeVal <- readEnvString name
return . fromMaybe defaultValue $ maybeVal >>= readMaybe
{-# INLINE readEnvWithDefault #-}
readEnvWithDevDefault :: (MonadIO m, MonadFail m, Read a) => String -> a -> m a
readEnvWithDevDefault =
if isDevelopment then
readEnvWithDefault
else
(\name _ -> do
errOrResult <- readEnv name
case errOrResult of
Left err -> fail err
Right x -> return x
)
{-# INLINE readEnvWithDevDefault #-}
readEnvString :: (MonadIO m1, MonadFail m2) => String -> m1 (m2 String)
readEnvString name = do
result <- liftIO $ lookupEnv name
return $ case result of
Nothing -> fail $ "No value set for environment variable: " <> name
Just x -> return x
{-# INLINE readEnvString #-}
readEnv :: (MonadIO m1, MonadFail m2, Read a) => String -> m1 (m2 a)
readEnv name = do
eitherOrVal <- readEnvString name
return $ case eitherOrVal :: ((Either String) String) of
Left err -> fail err
Right val ->
case readEither val of
Left err -> fail $ "Error parsing value from environment variable. " <> name <> " = " <> (show val) <> " => " <> err
Right result -> return result
{-# INLINE readEnv #-}
instance Var NominalDiffTime where
toVar :: NominalDiffTime -> String
toVar = show
{-# INLINE toVar #-}
fromVar :: String -> Maybe NominalDiffTime
fromVar var = fromInteger <$> readMaybe var
{-# INLINE fromVar #-}
instance Var PortID where
toVar (PortNumber portNum) = toVar $ toInteger portNum
toVar _ = error "Can only write port numbers to var"
{-# INLINE toVar #-}
fromVar = fmap (PortNumber . fromInteger) . fromVar
{-# INLINE fromVar #-}