    module Test.Mockery.Environment (
      withEnvironment
    , withModifiedEnvironment
    ) where

    import           Control.Exception.Base
    import           Control.Monad
    import           System.Environment.Compat

    -- |
    -- Run the given action within the specified environment.
    --
    -- Before executing the action, `withEnvironment` backs up the current
    -- environment, clears out the environment, and then applies the supplied
    -- environment.
    -- After the action has completed the original environment is restored.
    --
    -- __Note__: The environment is global to a process, so tests that modify the
    -- environment can no longer be run in parallel.
    withEnvironment :: [(String, String)] -> IO a -> IO a
    withEnvironment :: forall a. [(String, String)] -> IO a -> IO a
    withEnvironment [(String, String)]
    environment IO a
    action = IO a -> IO a
    forall a. IO a -> IO a
    bracketEnvironment (IO a -> IO a) -> IO a -> IO a
    forall a b. (a -> b) -> a -> b
    $ do
      [(String, String)] -> IO ()
    setEnvironment [(String, String)]
    environment
      IO a
    action

    -- |
    -- Run the given action within an augmented environment.
    --
    -- Before executing the action, `withModifiedEnvironment` backs up the current
    -- environment and then augments it with the supplied values.
    -- After the action has completed the original environment is restored.
    --
    -- __Note__: The environment is global to a process, so tests that modify the
    -- environment can no longer be run in parallel.
    withModifiedEnvironment :: [(String, String)] -> IO a -> IO a
    withModifiedEnvironment :: forall a. [(String, String)] -> IO a -> IO a
    withModifiedEnvironment [(String, String)]
    environment IO a
    action = IO a -> IO a
    forall a. IO a -> IO a
    bracketEnvironment (IO a -> IO a) -> IO a -> IO a
    forall a b. (a -> b) -> a -> b
    $ do
      [(String, String)] -> IO ()
    extendEnvironment [(String, String)]
    environment
      IO a
    action

    bracketEnvironment :: IO a -> IO a
    bracketEnvironment :: forall a. IO a -> IO a
    bracketEnvironment = IO [(String, String)]
    -> ([(String, String)] -> IO ())
    -> ([(String, String)] -> IO a)
    -> IO a
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
    bracket IO [(String, String)]
    getEnvironment [(String, String)] -> IO ()
    setEnvironment (([(String, String)] -> IO a) -> IO a)
    -> (IO a -> [(String, String)] -> IO a) -> IO a -> IO a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO a -> [(String, String)] -> IO a
    forall a b. a -> b -> a
    const

    setEnvironment :: [(String, String)] -> IO ()
    setEnvironment :: [(String, String)] -> IO ()
    setEnvironment [(String, String)]
    environment = do
      IO ()
    clearEnvironment
      [(String, String)] -> IO ()
    extendEnvironment [(String, String)]
    environment

    extendEnvironment :: [(String, String)] -> IO ()
    extendEnvironment :: [(String, String)] -> IO ()
    extendEnvironment [(String, String)]
    environment = [(String, String)] -> ((String, String) -> IO ()) -> IO ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ [(String, String)]
    environment (((String, String) -> IO ()) -> IO ())
    -> ((String, String) -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ (String -> String -> IO ()) -> (String, String) -> IO ()
    forall a b c. (a -> b -> c) -> (a, b) -> c
    uncurry String -> String -> IO ()
    setEnv

    clearEnvironment :: IO ()
    clearEnvironment :: IO ()
    clearEnvironment = do
      environment <- IO [(String, String)]
    getEnvironment
      forM_ environment (unsetEnv . fst)