{-| Module : System.Environment.Extra Description : Safe helpers for accessing and modifying environment variables Copyright : (c) Boris Buliga, 2016-2020 License : MIT Maintainer : boris@d12frosted.io Stability : experimental Portability : POSIX The module defines function 'setEnv' - a lifted version of 'E.setEnv' that works with 'Text' input and various safe versions of 'E.lookupEnv' that allow one to get any 'IsString' ('getEnv' and 'envMaybe') or even provide a 'Reader' to parse the value ('envRead') >>> getEnv "HOME" "/Users/d12frosted" >>> getEnv "WHAAT" *** Exception: Could not find value of $WHAAT in environment. >>> setEnv "WHAAT" "HOME" >>> getEnv "WHAAT" "HOME" >>> getEnv "WHAAT" >>= getEnv "/Users/d12frosted" >>> getEnv "WHAAT" >>= putStrLn HOME >>> setEnv "AGE" "12" >>> envMaybe "AGE" Just "12" >>> envRead decimal "AGE" Just 12 -} -------------------------------------------------------------------------------- {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} -------------------------------------------------------------------------------- module System.Environment.Extra ( setEnv , getEnv , envMaybe , envRead , read -- * Data types , EnvironmentException(..) -- * Reexport several Readers from Data.Text , Reader , decimal , signed , hexadecimal , rational , double ) where -------------------------------------------------------------------------------- import Control.Exception import Control.Monad import Control.Monad.Catch (MonadThrow (..)) import Control.Monad.IO.Class import Data.Maybe import Data.String (IsString, fromString) import Data.Text import Data.Text.Read import Data.Typeable import Prelude hiding (read) import qualified System.Environment as E (lookupEnv, setEnv) import Text.Read (readEither) -------------------------------------------------------------------------------- -- EnvironmentException definition -- | Exceptions that can occur during reading the environment variable. newtype EnvironmentException = EnvVarNotFound Text deriving (Typeable) instance Exception EnvironmentException instance Show EnvironmentException where show (EnvVarNotFound var) = "Could not find value of $" ++ unpack var ++ " in environment." -------------------------------------------------------------------------------- -- Setting value -- | Set value of environment variable. -- -- Thorws 'IOException'. -- -- >>> envMaybe "NAME" -- Nothing -- >>> setEnv "NAME" "Boris" -- >>> envMaybe "NAME" -- Just "Boris" setEnv :: (MonadThrow m, MonadIO m) => Text -> Text -> m () setEnv k v = liftIO $ E.setEnv (unpack k) (unpack v) -------------------------------------------------------------------------------- -- Getting value -- | Get value of environment variable. -- -- Throws 'EnvVarNotFound'. -- -- >>> getEnv "NAME" -- *** Exception: Could not find value of $NAME in environment. -- >>> getEnv "HOME" -- "/Users/d12frosted" getEnv :: ( MonadThrow m, MonadIO m, IsString a ) => Text -> m a getEnv key = envMaybe key >>= \case Nothing -> throwM $ EnvVarNotFound key Just v -> return v -- | Get value of environment variable. -- >>> getEnv "NAME" -- Nothing -- >>> getEnv "HOME" -- Just "/Users/d12frosted" envMaybe :: ( MonadIO m, IsString a ) => Text -> m (Maybe a) envMaybe key = liftIO $ fmap (fromString <$>) (E.lookupEnv (unpack key)) -- | Get value of environment variable and parse it using specific reader. -- >>> setEnv "AGE" "12" -- >>> envMaybe "AGE" -- Just "12" -- >>> envRead decimal "AGE" -- Just 12 envRead :: (MonadIO m) => Reader a -> Text -> m (Maybe a) envRead r = fmap (((fmap fst . fromRight) . r) =<<) . envMaybe -- | Generic reader for readable values. -- -- Keep in mind that it's always better from performance view to use specific -- Reader functions like @'decimal'@ instead of this generic one. read :: Read a => Reader a read = fmap (, "") . readEither . unpack fromRight :: Either a b -> Maybe b fromRight = either (const Nothing) Just --------------------------------------------------------------------------------