module IHP.EnvVar
( env
, envOrDefault
, envOrNothing
, hasEnvVar
, EnvVarReader (..)
)
where
import IHP.Prelude
import Data.String.Interpolate.IsString (i)
import qualified System.Posix.Env.ByteString as Posix
import Network.Socket (PortNumber)
import IHP.Mail.Types
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 (Text -> result
forall a. Text -> a
error [i|Env var '#{name}' 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 -> Text -> result
forall a. Text -> a
error [i|Env var '#{name}' is invalid: #{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
Maybe ByteString
value :: Maybe ByteString <- ByteString -> IO (Maybe ByteString)
forall (monad :: * -> *) result.
(MonadIO monad, EnvVarReader result) =>
ByteString -> monad (Maybe result)
envOrNothing ByteString
name
Bool -> IO Bool
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe ByteString -> Bool
forall a. Maybe a -> Bool
isJust Maybe ByteString
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 [i|Expected integer, got #{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 String
envStringToValue ByteString
string = String -> Either Text String
forall a b. b -> Either a b
Right (ByteString -> String
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 [i|Expected integer to be used as a Port number, got #{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
instance EnvVarReader SMTPEncryption where
envStringToValue :: ByteString -> Either Text SMTPEncryption
envStringToValue ByteString
"Unencrypted" = SMTPEncryption -> Either Text SMTPEncryption
forall a b. b -> Either a b
Right SMTPEncryption
Unencrypted
envStringToValue ByteString
"TLS" = SMTPEncryption -> Either Text SMTPEncryption
forall a b. b -> Either a b
Right SMTPEncryption
TLS
envStringToValue ByteString
"STARTTLS" = SMTPEncryption -> Either Text SMTPEncryption
forall a b. b -> Either a b
Right SMTPEncryption
STARTTLS
envStringToValue ByteString
otherwise = Text -> Either Text SMTPEncryption
forall a b. a -> Either a b
Left [i|Expected 'Unencrypted', 'TLS' or 'STARTTLS', got #{otherwise}|]