-- | A wrapper around IO that allows you to treat temporary files like
-- garbage-collected values.
module Sound.Jammit.Internal.TempIO
( TempIO
, runTempIO
, newTempFile
, ask
, liftIO
) where

import Data.List (stripPrefix)
import System.IO (hClose)
import System.IO.Error (catchIOError)

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Reader (ask, ReaderT(..))
import System.Directory (copyFile, renameFile)
import System.FilePath (splitPath)
import System.IO.Temp (openTempFile, withSystemTempDirectory)

-- | A wrapper around IO with a designated directory for temporary files.
type TempIO = ReaderT FilePath IO

-- | Creates a new temporary directory to run a computation in. When finished,
-- the final file will be moved/copied to the given path, and the temporary
-- directory will be deleted.
runTempIO :: FilePath -> TempIO FilePath -> IO ()
runTempIO fout act = withSystemTempDirectory "tempfile" $ \tmp -> do
  res <- runReaderT act tmp
  case stripPrefix (splitPath tmp) (splitPath res) of
    Just f | ".." `notElem` f -> -- try rename if we know the file is in tmp
      catchIOError (renameFile res fout) $ \_ -> copyFile res fout
    _ -> copyFile res fout

-- | Creates a new file in the temporary directory, given a template.
newTempFile :: String -> TempIO FilePath
newTempFile pat = do
  tmp <- ask
  (f, h) <- liftIO $ openTempFile tmp pat
  liftIO $ hClose h
  return f
