    {-# LANGUAgE CPP #-}
    -- | Temporary file and directory support.
    --
    -- Strongly inspired by\/stolen from the <https://github.com/feuerbach/temporary> package.
    --
    -- @since 0.1.0.0
    module UnliftIO.Temporary
      ( withSystemTempFile
      , withSystemTempDirectory
      , withTempFile
      , withTempDirectory
      ) where

    import Control.Monad.IO.Unlift
    import Control.Monad (liftM)
    import UnliftIO.Exception
    import System.Directory
    import System.IO (Handle, openTempFile, hClose)
    import System.IO.Error
    import System.Posix.Internals (c_getpid)
    import System.FilePath ((</>))

    #ifdef mingw32_HOST_OS
    import System.Directory       ( createDirectory )
    #else
    import qualified System.Posix
    #endif

    -- | Create and use a temporary file in the system standard temporary directory.
    --
    -- Behaves exactly the same as 'withTempFile', except that the parent temporary directory
    -- will be that returned by 'getCanonicalTemporaryDirectory'.
    --
    -- @since 0.1.0.0
    withSystemTempFile :: MonadUnliftIO m =>
                          String   -- ^ File name template. See 'openTempFile'.
                       -> (FilePath -> Handle -> m a) -- ^ Callback that can use the file
                       -> m a
    withSystemTempFile :: String -> (String -> Handle -> m a) -> m a
    withSystemTempFile template :: String
    template action :: String -> Handle -> m a
    action = IO String -> m String
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO String
    getCanonicalTemporaryDirectory m String -> (String -> m a) -> m a
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \tmpDir :: String
    tmpDir -> String -> String -> (String -> Handle -> m a) -> m a
    forall (m :: * -> *) a.
    MonadUnliftIO m =>
    String -> String -> (String -> Handle -> m a) -> m a
    withTempFile String
    tmpDir String
    template String -> Handle -> m a
    action

    -- | Create and use a temporary directory in the system standard temporary directory.
    --
    -- Behaves exactly the same as 'withTempDirectory', except that the parent temporary directory
    -- will be that returned by 'getCanonicalTemporaryDirectory'.
    --
    -- @since 0.1.0.0
    withSystemTempDirectory :: MonadUnliftIO m =>
                               String   -- ^ Directory name template. See 'openTempFile'.
                            -> (FilePath -> m a) -- ^ Callback that can use the directory.
                            -> m a
    withSystemTempDirectory :: String -> (String -> m a) -> m a
    withSystemTempDirectory template :: String
    template action :: String -> m a
    action = IO String -> m String
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO String
    getCanonicalTemporaryDirectory m String -> (String -> m a) -> m a
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \tmpDir :: String
    tmpDir -> String -> String -> (String -> m a) -> m a
    forall (m :: * -> *) a.
    MonadUnliftIO m =>
    String -> String -> (String -> m a) -> m a
    withTempDirectory String
    tmpDir String
    template String -> m a
    action

    -- | Use a temporary filename that doesn't already exist.
    --
    -- Creates a new temporary file inside the given directory, making use of the
    -- template. The temp file is deleted after use. For example:
    --
    -- > withTempFile "src" "sdist." $ \tmpFile hFile -> do ...
    --
    -- The @tmpFile@ will be file in the given directory, e.g.
    -- @src/sdist.342@.
    --
    -- @since 0.1.0.0
    withTempFile :: MonadUnliftIO m =>
                    FilePath -- ^ Temp dir to create the file in.
                 -> String   -- ^ File name template. See 'openTempFile'.
                 -> (FilePath -> Handle -> m a) -- ^ Callback that can use the file.
                 -> m a
    withTempFile :: String -> String -> (String -> Handle -> m a) -> m a
    withTempFile tmpDir :: String
    tmpDir template :: String
    template action :: String -> Handle -> m a
    action =
      m (String, Handle)
    -> ((String, Handle) -> m ()) -> ((String, Handle) -> m a) -> m a
    forall (m :: * -> *) a b c.
    MonadUnliftIO m =>
    m a -> (a -> m b) -> (a -> m c) -> m c
    bracket
        (IO (String, Handle) -> m (String, Handle)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> String -> IO (String, Handle)
    openTempFile String
    tmpDir String
    template))
        (\(name :: String
    name, handle' :: Handle
    handle') -> IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (Handle -> IO ()
    hClose Handle
    handle' IO () -> IO () -> IO ()
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> IO () -> IO ()
    forall (m :: * -> *). MonadUnliftIO m => m () -> m ()
    ignoringIOErrors (String -> IO ()
    removeFile String
    name)))
        ((String -> Handle -> m a) -> (String, Handle) -> m a
    forall a b c. (a -> b -> c) -> (a, b) -> c
    uncurry String -> Handle -> m a
    action)

    -- | Create and use a temporary directory.
    --
    -- Creates a new temporary directory inside the given directory, making use
    -- of the template. The temp directory is deleted after use. For example:
    --
    -- > withTempDirectory "src" "sdist." $ \tmpDir -> do ...
    --
    -- The @tmpDir@ will be a new subdirectory of the given directory, e.g.
    -- @src/sdist.342@.
    --
    -- @since 0.1.0.0
    withTempDirectory :: MonadUnliftIO m =>
                         FilePath -- ^ Temp directory to create the directory in.
                      -> String   -- ^ Directory name template. See 'openTempFile'.
                      -> (FilePath -> m a) -- ^ Callback that can use the directory.
                      -> m a
    withTempDirectory :: String -> String -> (String -> m a) -> m a
    withTempDirectory targetDir :: String
    targetDir template :: String
    template =
      m String -> (String -> m ()) -> (String -> m a) -> m a
    forall (m :: * -> *) a b c.
    MonadUnliftIO m =>
    m a -> (a -> m b) -> (a -> m c) -> m c
    bracket
        (IO String -> m String
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> String -> IO String
    createTempDirectory String
    targetDir String
    template))
        (IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> m ()) -> (String -> IO ()) -> String -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO () -> IO ()
    forall (m :: * -> *). MonadUnliftIO m => m () -> m ()
    ignoringIOErrors (IO () -> IO ()) -> (String -> IO ()) -> String -> IO ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> IO ()
    removeDirectoryRecursive)

    -- | Return the absolute and canonical path to the system temporary
    -- directory.
    --
    -- >>> setCurrentDirectory "/home/feuerbach/"
    -- >>> setEnv "TMPDIR" "."
    -- >>> getTemporaryDirectory
    -- "."
    -- >>> getCanonicalTemporaryDirectory
    -- "/home/feuerbach"
    getCanonicalTemporaryDirectory :: IO FilePath
    getCanonicalTemporaryDirectory :: IO String
    getCanonicalTemporaryDirectory = IO String
    getTemporaryDirectory IO String -> (String -> IO String) -> IO String
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= String -> IO String
    canonicalizePath

    -- | Create a temporary directory. See 'withTempDirectory'.
    createTempDirectory
      :: FilePath -- ^ Temp directory to create the directory in.
      -> String -- ^ Directory name template.
      -> IO FilePath
    createTempDirectory :: String -> String -> IO String
    createTempDirectory dir :: String
    dir template :: String
    template = do
      CPid
    pid <- IO CPid
    c_getpid
      CPid -> IO String
    forall a. (Num a, Show a) => a -> IO String
    findTempName CPid
    pid
      where
        findTempName :: a -> IO String
    findTempName x :: a
    x = do
          let dirpath :: String
    dirpath = String
    dir String -> String -> String
    </> String
    template String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ a -> String
    forall a. Show a => a -> String
    show a
    x
          Either IOError ()
    r <- IO () -> IO (Either IOError ())
    forall (m :: * -> *) e a.
    (MonadUnliftIO m, Exception e) =>
    m a -> m (Either e a)
    try (IO () -> IO (Either IOError ()))
    -> IO () -> IO (Either IOError ())
    forall a b. (a -> b) -> a -> b
    $ String -> IO ()
    mkPrivateDir String
    dirpath
          case Either IOError ()
    r of
            Right _ -> String -> IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    dirpath
            Left  e :: IOError
    e | IOError -> Bool
    isAlreadyExistsError IOError
    e -> a -> IO String
    findTempName (a
    xa -> a -> a
    forall a. Num a => a -> a -> a
    +1)
                    | Bool
    otherwise              -> IOError -> IO String
    forall a. IOError -> IO a
    ioError IOError
    e

    mkPrivateDir :: String -> IO ()
    #ifdef mingw32_HOST_OS
    mkPrivateDir s = createDirectory s
    #else
    mkPrivateDir :: String -> IO ()
    mkPrivateDir s :: String
    s = String -> FileMode -> IO ()
    System.Posix.createDirectory String
    s 0o700
    #endif

    ignoringIOErrors :: MonadUnliftIO m => m () -> m ()
    ignoringIOErrors :: m () -> m ()
    ignoringIOErrors = (Either IOError () -> ()) -> m (Either IOError ()) -> m ()
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    liftM (() -> Either IOError () -> ()
    forall a b. a -> b -> a
    const ()) (m (Either IOError ()) -> m ())
    -> (m () -> m (Either IOError ())) -> m () -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . m () -> m (Either IOError ())
    forall (m :: * -> *) a.
    MonadUnliftIO m =>
    m a -> m (Either IOError a)
    tryIO -- yes, it's just void, but for pre-AMP GHCs