module IHP.EnvVar
( env
, envOrDefault
, envOrNothing
, hasEnvVar
, EnvVarReader (..)
)
where
import Prelude
import Data.ByteString (ByteString)
import Data.Text (Text)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Maybe (isJust, fromMaybe)
import Data.String.Conversions (cs)
import IHP.HaskellSupport (textToInt)
import qualified System.Posix.Env.ByteString as Posix
import Network.Socket (PortNumber)
import IHP.Environment
env :: forall result monad. (MonadIO monad) => EnvVarReader result => ByteString -> monad result
env :: forall result (monad :: * -> *).
(MonadIO monad, EnvVarReader result) =>
ByteString -> monad result
env ByteString
name = ByteString -> result -> monad result
forall (monad :: * -> *) result.
(MonadIO monad, EnvVarReader result) =>
ByteString -> result -> monad result
envOrDefault ByteString
name ([Char] -> result
forall a. HasCallStack => [Char] -> a
error ([Char]
"Env var '" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> ByteString -> [Char]
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
name [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"' not set, but it's required for the app to run"))
{-# INLINE env #-}
envOrDefault :: (MonadIO monad) => EnvVarReader result => ByteString -> result -> monad result
envOrDefault :: forall (monad :: * -> *) result.
(MonadIO monad, EnvVarReader result) =>
ByteString -> result -> monad result
envOrDefault ByteString
name result
defaultValue = result -> Maybe result -> result
forall a. a -> Maybe a -> a
fromMaybe result
defaultValue (Maybe result -> result) -> monad (Maybe result) -> monad result
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString -> monad (Maybe result)
forall (monad :: * -> *) result.
(MonadIO monad, EnvVarReader result) =>
ByteString -> monad (Maybe result)
envOrNothing ByteString
name
{-# INLINE envOrDefault #-}
envOrNothing :: (MonadIO monad) => EnvVarReader result => ByteString -> monad (Maybe result)
envOrNothing :: forall (monad :: * -> *) result.
(MonadIO monad, EnvVarReader result) =>
ByteString -> monad (Maybe result)
envOrNothing ByteString
name = IO (Maybe result) -> monad (Maybe result)
forall a. IO a -> monad a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe result) -> monad (Maybe result))
-> IO (Maybe result) -> monad (Maybe result)
forall a b. (a -> b) -> a -> b
$ (ByteString -> result) -> Maybe ByteString -> Maybe result
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> result
parseString (Maybe ByteString -> Maybe result)
-> IO (Maybe ByteString) -> IO (Maybe result)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString -> IO (Maybe ByteString)
Posix.getEnv ByteString
name
where
parseString :: ByteString -> result
parseString ByteString
string = case ByteString -> Either Text result
forall valueType.
EnvVarReader valueType =>
ByteString -> Either Text valueType
envStringToValue ByteString
string of
Left Text
errorMessage -> [Char] -> result
forall a. HasCallStack => [Char] -> a
error ([Char]
"Env var '" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> ByteString -> [Char]
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
name [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"' is invalid: " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
forall a b. ConvertibleStrings a b => a -> b
cs Text
errorMessage)
Right result
value -> result
value
{-# INLINE envOrNothing #-}
hasEnvVar :: (MonadIO monad) => ByteString -> monad Bool
hasEnvVar :: forall (monad :: * -> *). MonadIO monad => ByteString -> monad Bool
hasEnvVar ByteString
name = IO Bool -> monad Bool
forall a. IO a -> monad a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO do
value :: Maybe ByteString <- ByteString -> IO (Maybe ByteString)
forall (monad :: * -> *) result.
(MonadIO monad, EnvVarReader result) =>
ByteString -> monad (Maybe result)
envOrNothing ByteString
name
pure (isJust value)
{-# INLINE hasEnvVar #-}
class EnvVarReader valueType where
envStringToValue :: ByteString -> Either Text valueType
instance EnvVarReader Environment where
envStringToValue :: ByteString -> Either Text Environment
envStringToValue ByteString
"Production" = Environment -> Either Text Environment
forall a b. b -> Either a b
Right Environment
Production
envStringToValue ByteString
"Development" = Environment -> Either Text Environment
forall a b. b -> Either a b
Right Environment
Development
envStringToValue ByteString
otherwise = Text -> Either Text Environment
forall a b. a -> Either a b
Left Text
"Should be set to 'Development' or 'Production'"
instance EnvVarReader Int where
envStringToValue :: ByteString -> Either Text Int
envStringToValue ByteString
string = case Text -> Maybe Int
textToInt (ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
string) of
Just Int
integer -> Int -> Either Text Int
forall a b. b -> Either a b
Right Int
integer
Maybe Int
Nothing -> Text -> Either Text Int
forall a b. a -> Either a b
Left (Text
"Expected integer, got " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
string)
instance EnvVarReader Text where
envStringToValue :: ByteString -> Either Text Text
envStringToValue ByteString
string = Text -> Either Text Text
forall a b. b -> Either a b
Right (ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
string)
instance EnvVarReader String where
envStringToValue :: ByteString -> Either Text [Char]
envStringToValue ByteString
string = [Char] -> Either Text [Char]
forall a b. b -> Either a b
Right (ByteString -> [Char]
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
string)
instance EnvVarReader ByteString where
envStringToValue :: ByteString -> Either Text ByteString
envStringToValue ByteString
string = ByteString -> Either Text ByteString
forall a b. b -> Either a b
Right ByteString
string
instance EnvVarReader Bool where
envStringToValue :: ByteString -> Either Text Bool
envStringToValue ByteString
"1" = Bool -> Either Text Bool
forall a b. b -> Either a b
Right Bool
True
envStringToValue ByteString
"0" = Bool -> Either Text Bool
forall a b. b -> Either a b
Right Bool
False
envStringToValue ByteString
otherwise = Text -> Either Text Bool
forall a b. a -> Either a b
Left Text
"Should be set to '1' or '0'"
instance EnvVarReader PortNumber where
envStringToValue :: ByteString -> Either Text PortNumber
envStringToValue ByteString
string = case Text -> Maybe Int
textToInt (ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
string) of
Just Int
integer -> PortNumber -> Either Text PortNumber
forall a b. b -> Either a b
Right (PortNumber -> Either Text PortNumber)
-> PortNumber -> Either Text PortNumber
forall a b. (a -> b) -> a -> b
$ Int -> PortNumber
convertIntToPortNumber Int
integer
Maybe Int
Nothing -> Text -> Either Text PortNumber
forall a b. a -> Either a b
Left (Text
"Expected integer to be used as a Port number, got " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
string)
convertIntToPortNumber :: Int -> PortNumber
convertIntToPortNumber :: Int -> PortNumber
convertIntToPortNumber Int
int = Int -> PortNumber
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
int :: Int) :: PortNumber