{-# LANGUAGE CPP #-} {-# LANGUAGE InstanceSigs #-} module RFC.Env ( isDevelopment , readEnvironment , 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 (readMaybe) envWithDefault :: Var a => String -> a -> Parser a envWithDefault name defaultValue = fmap (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 #-} readEnvironment :: (MonadIO m) => m String readEnvironment = readEnvWithDefault "ENV" "development" {-# INLINE readEnvironment #-} readAppSlug :: (MonadIO m, MonadFail m) => m String readAppSlug = readEnvWithDevDefault "APP_SLUG" "dev" {-# INLINE readAppSlug #-} readHost :: (MonadIO m, MonadFail m) => m String readHost = readEnvWithDevDefault "HOST" "localhost" {-# INLINE readHost #-} readPort :: (MonadIO m, MonadFail m) => Word16 -> m Word16 readPort devPort = readEnvWithDevDefault "PORT" devPort {-# INLINE readPort #-} readEnvWithDefault :: (MonadIO m, Read a) => String -> a -> m a readEnvWithDefault name defaultValue = either (const defaultValue) id <$> readEnv name {-# INLINE readEnvWithDefault #-} readEnvWithDevDefault :: (MonadIO m, MonadFail m, Read a) => String -> a -> m a readEnvWithDevDefault = if isDevelopment then readEnvWithDefault else (\name _ -> readEnvOrDie name) {-# INLINE readEnvWithDevDefault #-} readEnv :: (MonadIO m, Read a) => String -> m (Either String a) readEnv name = liftIO $ do result <- lookupEnv name return $ case result >>= readMaybe of Nothing -> Left (show result) Just goodValue -> Right goodValue {-# INLINE readEnv #-} readEnvOrDie :: (MonadIO m, MonadFail m, Read a) => String -> m a readEnvOrDie name = do maybeResult <- readEnv name case maybeResult of Left err -> if err == (show (Nothing::Maybe String)) then fail $ "No value set for mandatory environment variable: " ++ name else fail $ "Cannot use value set for mandatory environment variable: " ++ name ++ " => " ++ err Right result -> return result {-# INLINE readEnvOrDie #-} 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 #-}