module Test.Mockery.Directory (
  inTempDirectory
, inTempDirectoryNamed
, withFile
, touch
) where

import           Control.Exception
import           Control.Monad
import           System.Directory
import           System.FilePath
import           System.IO.Error
import           System.IO hiding (withFile)
import           System.IO.Temp (withSystemTempDirectory, withSystemTempFile)
import qualified Data.ByteString as B

-- |
-- Run given action with the current working directory set to a temporary
-- directory.
--
-- The directory is created before the action is run.  After the action has
-- completed the directory is removed and the current working directory is
-- restored to its original value.
inTempDirectory :: IO a -> IO a
inTempDirectory :: IO a -> IO a
inTempDirectory IO a
action = String -> (String -> IO a) -> IO a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"mockery" ((String -> IO a) -> IO a) -> (String -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \String
path -> do
  IO String -> (String -> IO ()) -> (String -> IO a) -> IO a
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket IO String
getCurrentDirectory String -> IO ()
setCurrentDirectory ((String -> IO a) -> IO a) -> (String -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \String
_ -> do
    String -> IO ()
setCurrentDirectory String
path
    IO a
action

-- |
-- Similar to `inTempDirectory`, but the temporary directory will have the
-- specified name.
inTempDirectoryNamed :: FilePath -> IO a -> IO a
inTempDirectoryNamed :: String -> IO a -> IO a
inTempDirectoryNamed String
name IO a
action = IO a -> IO a
forall a. IO a -> IO a
inTempDirectory (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ do
  String -> IO ()
createDirectory String
name
  String -> IO ()
setCurrentDirectory String
name
  IO a
action

-- |
-- Create a temporary file with the given contents and execute the given
-- action.
--
-- The file is removed after the action has completed.
withFile :: String -> (FilePath -> IO a) -> IO a
withFile :: String -> (String -> IO a) -> IO a
withFile String
input String -> IO a
action = String -> (String -> Handle -> IO a) -> IO a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> Handle -> m a) -> m a
withSystemTempFile String
"mockery" ((String -> Handle -> IO a) -> IO a)
-> (String -> Handle -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \String
file Handle
h -> do
  Handle -> String -> IO ()
hPutStr Handle
h String
input
  Handle -> IO ()
hClose Handle
h
  String -> IO a
action String
file

-- |
-- Update the modification time of the specified file.
-- Create an empty file (including any missing directories in the file path) if
-- the file does not exist.
touch :: FilePath -> IO ()
touch :: String -> IO ()
touch String
file = do
  Bool -> String -> IO ()
createDirectoryIfMissing Bool
True (String -> String
takeDirectory String
file)
  ByteString
c <- (IOError -> Maybe ())
-> IO ByteString -> (() -> IO ByteString) -> IO ByteString
forall e b a.
Exception e =>
(e -> Maybe b) -> IO a -> (b -> IO a) -> IO a
catchJust (Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (IOError -> Bool) -> IOError -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IOError -> Bool
isDoesNotExistError) (String -> IO ByteString
B.readFile String
file) (IO ByteString -> () -> IO ByteString
forall a b. a -> b -> a
const (IO ByteString -> () -> IO ByteString)
-> IO ByteString -> () -> IO ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> IO ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
B.empty)
  String -> ByteString -> IO ()
B.writeFile String
file ByteString
c