read-env-var 0.1.0.1 → 1.0.0.0
raw patch · 3 files changed
+272/−36 lines, 3 filesdep +exceptionsdep +transformersdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: exceptions, transformers
Dependency ranges changed: base
API changes (from Hackage documentation)
- System.ReadEnvVar: readEnvVar :: Read a => String -> IO (Maybe a)
- System.ReadEnvVar: readEnvVarDef :: Read a => String -> a -> IO a
+ System.ReadEnvVar: EnvVarCannotBeReadException :: String -> EnvVarCannotBeReadException
+ System.ReadEnvVar: EnvVarDoesNotExistException :: String -> EnvVarDoesNotExistException
+ System.ReadEnvVar: data EnvVarCannotBeReadException
+ System.ReadEnvVar: data EnvVarDoesNotExistException
+ System.ReadEnvVar: instance Data.Data.Data System.ReadEnvVar.EnvVarCannotBeReadException
+ System.ReadEnvVar: instance Data.Data.Data System.ReadEnvVar.EnvVarDoesNotExistException
+ System.ReadEnvVar: instance GHC.Classes.Eq System.ReadEnvVar.EnvVarCannotBeReadException
+ System.ReadEnvVar: instance GHC.Classes.Eq System.ReadEnvVar.EnvVarDoesNotExistException
+ System.ReadEnvVar: instance GHC.Classes.Ord System.ReadEnvVar.EnvVarCannotBeReadException
+ System.ReadEnvVar: instance GHC.Classes.Ord System.ReadEnvVar.EnvVarDoesNotExistException
+ System.ReadEnvVar: instance GHC.Exception.Exception System.ReadEnvVar.EnvVarCannotBeReadException
+ System.ReadEnvVar: instance GHC.Exception.Exception System.ReadEnvVar.EnvVarDoesNotExistException
+ System.ReadEnvVar: instance GHC.Read.Read System.ReadEnvVar.EnvVarCannotBeReadException
+ System.ReadEnvVar: instance GHC.Read.Read System.ReadEnvVar.EnvVarDoesNotExistException
+ System.ReadEnvVar: instance GHC.Show.Show System.ReadEnvVar.EnvVarCannotBeReadException
+ System.ReadEnvVar: instance GHC.Show.Show System.ReadEnvVar.EnvVarDoesNotExistException
+ System.ReadEnvVar: lookupEnv :: (IsString a, MonadIO m) => String -> m (Maybe a)
+ System.ReadEnvVar: lookupEnvEx :: (IsString a, MonadIO m, MonadThrow m) => String -> m a
+ System.ReadEnvVar: readEnv :: (MonadIO m, Read a) => String -> m (Maybe a)
+ System.ReadEnvVar: readEnvDef :: (MonadIO m, Read a) => String -> a -> m a
+ System.ReadEnvVar: readEnvEx :: (MonadIO m, Read a, MonadThrow m) => String -> m (Maybe a)
+ System.ReadEnvVar: readEnvEx' :: (MonadIO m, Read a, MonadThrow m) => String -> m a
+ System.ReadEnvVar: setEnv :: String -> String -> IO ()
- System.ReadEnvVar: lookupEnvDef :: IsString a => String -> a -> IO a
+ System.ReadEnvVar: lookupEnvDef :: (IsString a, MonadIO m) => String -> a -> m a
Files
- LICENSE +1/−1
- read-env-var.cabal +4/−2
- src/System/ReadEnvVar.hs +267/−33
LICENSE view
@@ -1,4 +1,4 @@-Copyright Author name here (c) 2016+Copyright Dennis Gosnell (c) 2016 All rights reserved.
read-env-var.cabal view
@@ -1,5 +1,5 @@ name: read-env-var-version: 0.1.0.1+version: 1.0.0.0 synopsis: Functions for safely reading environment variables. description: Please see README.md homepage: https://github.com/cdepillabout/read-env-var#readme@@ -16,7 +16,9 @@ library hs-source-dirs: src exposed-modules: System.ReadEnvVar- build-depends: base >= 4.6 && < 5+ build-depends: base >= 4.7 && < 5+ , exceptions+ , transformers default-language: Haskell2010 ghc-options: -Wall
src/System/ReadEnvVar.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE CPP #-} {-|@@ -9,12 +10,37 @@ Portability : POSIX This Haskell module exports functions for safely reading environment variables.++The @lookupEnv*@ functions are for reading in 'String'-like environment+variables (like hostnames, passwords, etc.), while the @readEnv*@ functions are+for reading in Haskell datatypes (like 'Int', 'Double', etc).++Most of these functions run in 'MonadIO'. This means that they can be used+from any monad that implements 'MonadIO'. This makes it easier to run in a+monad transformer stack. If you're not familiar with 'MonadIO', you can just+think of all the functions as being 'IO' actions.++The 'lookupEnv', 'lookupEnvDef', and 'lookupEnvEx' functions all use 'IsString'+to generalize the return type. This makes it more general than+"System.Environment"\'s 'Env.lookupEnv'. It makes it possible to read in+things other than 'String's, like+<https://hackage.haskell.org/package/text/docs/Data-Text.html Text> or+<https://hackage.haskell.org/package/bytestring/docs/Data-ByteString.html ByteString>. -} module System.ReadEnvVar- ( readEnvVar- , readEnvVarDef+ ( lookupEnv , lookupEnvDef+ , readEnv+ , readEnvDef+ -- * Unsafe functions+ , EnvVarDoesNotExistException(..)+ , lookupEnvEx+ , readEnvEx+ , EnvVarCannotBeReadException(..)+ , readEnvEx'+ -- * Re-exports+ , setEnv ) where #if __GLASGOW_HASKELL__ < 710@@ -23,69 +49,277 @@ import Control.Applicative #endif +import Control.Exception (Exception)+import Control.Monad.Catch (MonadThrow(throwM))+import Control.Monad.IO.Class (MonadIO(liftIO))+import Data.Data (Data) import Data.Maybe (fromMaybe) import Data.String (IsString(fromString))-import System.Environment (lookupEnv)+import Data.Typeable (Typeable)+import System.Environment (setEnv)+import qualified System.Environment as Env import Text.Read (readMaybe) -- | Lookup a value from an environment variable and read it in with -- 'readMaybe'. If the environment variable doesn't exist, or it can't be--- 'read', use the default value.+-- 'read', return the default value. Like 'readEnv' but with a default+-- value. ----- Note that this does not read string values as one would expect.+-- Read an environment variable that exists: ----- >>> import System.Environment (setEnv) -- >>> setEnv "TEST_ENV_VAR1" "1000"--- >>> readEnvVarDef "TEST_ENV_VAR1" 5 :: IO Int+-- >>> readEnvDef "TEST_ENV_VAR1" 5 :: IO Int -- 1000--- >>> readEnvVarDef "THIS_ENV_VAR_WILL_NOT_EXIST" 5 :: IO Int+--+-- Try reading an environment variable that does not exist. Returns the+-- default value:+--+-- >>> readEnvDef "THIS_ENV_VAR_WILL_NOT_EXIST" 5 :: IO Int -- 5+--+-- Try reading an environment variable that cannot be 'read'. Returns the+-- default value:+--+-- >>> setEnv "BAD_ENV_VAR" "not an int"+-- >>> readEnvDef "BAD_ENV_VAR" 10 :: IO Int+-- 10+--+-- Note that this __DOES NOT__ read string values as one might expect:+-- -- >>> setEnv "TEST_ENV_VAR2" "some string 1"--- >>> readEnvVarDef "TEST_ENV_VAR2" "def val" :: IO String+-- >>> readEnvDef "TEST_ENV_VAR2" "def val" :: IO String -- "def val"+--+-- It will read string values as if they were Haskell strings:+-- -- >>> setEnv "TEST_ENV_VAR3" "\"some string 1\""--- >>> readEnvVarDef "TEST_ENV_VAR3" "def val" :: IO String+-- >>> readEnvDef "TEST_ENV_VAR3" "def val" :: IO String -- "some string 1"-readEnvVarDef :: Read a- => String -- ^ environment variable to lookup- -> a -- ^ default value to use if the environment variable- -- either does not exist, or cannot be 'read'- -> IO a-readEnvVarDef envVar def = fromMaybe def <$> readEnvVar envVar+readEnvDef+ :: (MonadIO m, Read a)+ => String -- ^ environment variable to lookup+ -> a -- ^ default value to return if the environment variable+ -- either does not exist, or cannot be 'read'+ -> m a+readEnvDef envVar def = do+ maybeEnv <- readEnv envVar+ return $ fromMaybe def maybeEnv -- | Lookup a value from an environment variable and read it in with -- 'readMaybe'. ----- Note that this does not read string values as one would expect.+-- Read an environment variable that exists: ----- >>> import System.Environment (setEnv) -- >>> setEnv "TEST_ENV_VAR" "2000"--- >>> readEnvVar "TEST_ENV_VAR" :: IO (Maybe Int)+-- >>> readEnv "TEST_ENV_VAR" :: IO (Maybe Int) -- Just 2000--- >>> readEnvVar "THIS_ENV_VAR_WILL_NOT_EXIST" :: IO (Maybe Int)+--+-- Try reading an environment variable that does not exist. Returns 'Nothing':+--+-- >>> readEnv "THIS_ENV_VAR_WILL_NOT_EXIST" :: IO (Maybe Int) -- Nothing+--+-- Try reading an environment variable that cannot be 'read'. Returns+-- 'Nothing':+--+-- >>> setEnv "BAD_ENV_VAR" "not an int"+-- >>> readEnv "BAD_ENV_VAR" :: IO (Maybe Int)+-- Nothing+--+-- Note that this __DOES NOT__ read string values as one might expect:+-- -- >>> setEnv "TEST_ENV_VAR2" "some string 1"--- >>> readEnvVar "TEST_ENV_VAR2" :: IO (Maybe String)+-- >>> readEnv "TEST_ENV_VAR2" :: IO (Maybe String) -- Nothing+--+-- It will read string values as if they were Haskell strings:+-- -- >>> setEnv "TEST_ENV_VAR3" "\"some string 1\""--- >>> readEnvVar "TEST_ENV_VAR3" :: IO (Maybe String)+-- >>> readEnv "TEST_ENV_VAR3" :: IO (Maybe String) -- Just "some string 1"-readEnvVar :: Read a- => String -- ^ environment variable to lookup- -> IO (Maybe a)-readEnvVar = fmap (>>= readMaybe) . lookupEnv+readEnv+ :: (MonadIO m, Read a)+ => String -- ^ environment variable to lookup+ -> m (Maybe a)+readEnv envVar = do+ maybeEnv <- liftIO (Env.lookupEnv envVar)+ return $ maybeEnv >>= readMaybe -- | Like 'lookupEnv' but take a default value. ----- >>> import System.Environment (setEnv)+-- Lookup an environment variable that exists:+-- -- >>> setEnv "TEST_ENV_VAR" "foo" -- >>> lookupEnvDef "TEST_ENV_VAR" "bar" :: IO String -- "foo"+--+-- Lookup an environment variable that doesn't exist. Return the default+-- value:+-- -- >>> lookupEnvDef "THIS_ENV_VAR_WILL_NOT_EXIST" "bar" :: IO String -- "bar"-lookupEnvDef :: IsString a- => String -- ^ environment variable to lookup- -> a -- ^ default value to use if environment variable not defined- -> IO a-lookupEnvDef envVar defaultValue =- pure . maybe defaultValue fromString =<< lookupEnv envVar+lookupEnvDef+ :: (IsString a, MonadIO m)+ => String -- ^ environment variable to lookup+ -> a -- ^ default value to return if environment variable not defined+ -> m a+lookupEnvDef envVar defaultValue = do+ maybeEnv <- liftIO $ Env.lookupEnv envVar+ return $ maybe defaultValue fromString maybeEnv++-- | Like "System.Environment"\'s 'Env.lookupEnv', but using 'IsString' to make+-- it more general.+--+-- Lookup an environment variable that exists:+--+-- >>> setEnv "TEST_ENV_VAR" "foo"+-- >>> lookupEnv "TEST_ENV_VAR" :: IO (Maybe String)+-- Just "foo"+--+-- Lookup an environment variable that doesn't exist. Return 'Nothing':+--+-- >>> lookupEnv "THIS_ENV_VAR_WILL_NOT_EXIST" :: IO (Maybe String)+-- Nothing+lookupEnv+ :: (IsString a, MonadIO m)+ => String+ -> m (Maybe a)+lookupEnv envVar = do+ maybeEnv <- liftIO $ Env.lookupEnv envVar+ return $ fmap fromString maybeEnv++-- | 'Exception' thrown by 'lookupEnvEx' and 'readEnvEx' when the+-- environment variable being read doesn't exist.+data EnvVarDoesNotExistException =+ -- | The 'String' is the name of the environment variable that does not+ -- exist.+ EnvVarDoesNotExistException String+ deriving (Data, Eq, Ord, Read, Show, Typeable)++instance Exception EnvVarDoesNotExistException++-- | Like 'lookupEnv', but instead of returning a 'Maybe', throw an+-- 'EnvVarDoesNotExistException' if the environment variable does not exist.+-- The exception is thrown with 'throwM' from 'MonadThrow'.+--+-- Lookup an environment variable that exists:+--+-- >>> setEnv "TEST_ENV_VAR" "foo"+-- >>> lookupEnvEx "TEST_ENV_VAR" :: IO String+-- "foo"+--+-- Lookup an environment variable that doesn't exist. Throws+-- 'EnvVarDoesNotExistException'.+--+-- >>> lookupEnvEx "THIS_ENV_VAR_WILL_NOT_EXIST" :: IO String+-- *** Exception: EnvVarDoesNotExistException "THIS_ENV_VAR_WILL_NOT_EXIST"+lookupEnvEx+ :: (IsString a, MonadIO m, MonadThrow m)+ => String+ -> m a+lookupEnvEx envVar = do+ maybeEnv <- liftIO (Env.lookupEnv envVar)+ case maybeEnv of+ Nothing -> throwM $ EnvVarDoesNotExistException envVar+ Just env -> return $ fromString env++-- | Lookup a value from an environment variable and read it in with+-- 'readMaybe'. Throw an 'EnvVarDoesNotExistException' if the environment+-- variable does not exist. The exception is thrown with 'throwM' from+-- 'MonadThrow'.+--+-- Read an environment variable that exists:+--+-- >>> setEnv "TEST_ENV_VAR" "2000"+-- >>> readEnvEx "TEST_ENV_VAR" :: IO (Maybe Int)+-- Just 2000+--+-- Try reading an environment variable that does not exist. Throws+-- 'EnvVarDoesNotExistException':+--+-- >>> readEnvEx "THIS_ENV_VAR_WILL_NOT_EXIST" :: IO (Maybe Int)+-- *** Exception: EnvVarDoesNotExistException "THIS_ENV_VAR_WILL_NOT_EXIST"+--+-- Try reading an environment variable that cannot be 'read'. Returns+-- 'Nothing':+--+-- >>> setEnv "BAD_ENV_VAR" "not an int"+-- >>> readEnvEx "BAD_ENV_VAR" :: IO (Maybe Int)+-- Nothing+--+-- Note that this __DOES NOT__ read string values as one might expect:+--+-- >>> setEnv "TEST_ENV_VAR2" "some string 1"+-- >>> readEnvEx "TEST_ENV_VAR2" :: IO (Maybe String)+-- Nothing+--+-- It will read string values as if they were Haskell strings:+--+-- >>> setEnv "TEST_ENV_VAR3" "\"some string 1\""+-- >>> readEnvEx "TEST_ENV_VAR3" :: IO (Maybe String)+-- Just "some string 1"+readEnvEx+ :: (MonadIO m, Read a, MonadThrow m)+ => String -- ^ environment variable to lookup+ -> m (Maybe a)+readEnvEx envVar = do+ maybeEnv <- liftIO (Env.lookupEnv envVar)+ case maybeEnv of+ Nothing -> throwM $ EnvVarDoesNotExistException envVar+ Just env -> return $ readMaybe env++-- | 'Exception' thrown by 'readEnvEx'' when the environment variable+-- cannot be 'read'.+data EnvVarCannotBeReadException =+ -- | The first 'String' is the name of the environment variable that cannot+ -- be 'read'.+ EnvVarCannotBeReadException String+ deriving (Data, Eq, Ord, Read, Show, Typeable)++instance Exception EnvVarCannotBeReadException++-- | Just like 'readEnvEx', but also throw an exception when the environment+-- variable cannot be 'read'.+--+-- This can throw both 'EnvVarCannotBeReadException' and+-- 'EnvVarDoesNotExistException' with 'throwM'.+--+-- Read an environment variable that exists:+--+-- >>> setEnv "TEST_ENV_VAR" "2000"+-- >>> readEnvEx' "TEST_ENV_VAR" :: IO Int+-- 2000+--+-- Try reading an environment variable that does not exist. Throws+-- 'EnvVarDoesNotExistException':+--+-- >>> readEnvEx' "THIS_ENV_VAR_WILL_NOT_EXIST" :: IO Int+-- *** Exception: EnvVarDoesNotExistException "THIS_ENV_VAR_WILL_NOT_EXIST"+--+-- Try reading an environment variable that cannot be 'read'. Throws+-- 'EnvVarCannotBeReadException':+--+-- >>> setEnv "BAD_ENV_VAR" "not an int"+-- >>> readEnvEx' "BAD_ENV_VAR" :: IO Int+-- *** Exception: EnvVarCannotBeReadException "BAD_ENV_VAR"+--+-- Note that this __DOES NOT__ read string values as one might expect:+--+-- >>> setEnv "TEST_ENV_VAR2" "some string 1"+-- >>> readEnvEx' "TEST_ENV_VAR2" :: IO String+-- *** Exception: EnvVarCannotBeReadException "TEST_ENV_VAR2"+--+-- It will read string values as if they were Haskell strings:+--+-- >>> setEnv "TEST_ENV_VAR3" "\"some string 1\""+-- >>> readEnvEx' "TEST_ENV_VAR3" :: IO String+-- "some string 1"+readEnvEx'+ :: (MonadIO m, Read a, MonadThrow m)+ => String -- ^ environment variable to lookup+ -> m a+readEnvEx' envVar = do+ maybeEnv <- readEnvEx envVar+ case maybeEnv of+ Nothing -> throwM $ EnvVarCannotBeReadException envVar+ Just env -> return env