module Hackage.Security.Util.IO (
withTempFile
, getFileSize
, handleDoesNotExist
, atomicCopyFile
, atomicWriteFile
, atomicWithFile
) where
import Control.Exception
import Control.Monad
import System.IO.Error
import qualified Data.ByteString.Lazy as BS.L
import Hackage.Security.Util.Path
withTempFile :: forall a root. IsFileSystemRoot root
=> Path (Rooted root)
-> String
-> (AbsolutePath -> Handle -> IO a)
-> IO a
withTempFile tmpDir template callback = do
createDirectoryIfMissing True tmpDir
bracket (openTempFile tmpDir template) closeAndDelete (uncurry callback)
where
closeAndDelete :: (AbsolutePath, Handle) -> IO ()
closeAndDelete (fp, h) = do
hClose h
void $ handleDoesNotExist $ removeFile fp
getFileSize :: IsFileSystemRoot root => Path (Rooted root) -> IO Integer
getFileSize fp = withFileInReadMode fp hFileSize
handleDoesNotExist :: IO a -> IO (Maybe a)
handleDoesNotExist act =
handle aux (Just <$> act)
where
aux e =
if isDoesNotExistError e
then return Nothing
else throwIO e
atomicCopyFile :: AbsolutePath
-> AbsolutePath
-> IO ()
atomicCopyFile src dst = do
if takeDirectory src == takeDirectory dst
then renameFile src dst
else atomicWriteFile dst =<< readLazyByteString src
atomicWriteFile :: AbsolutePath
-> BS.L.ByteString
-> IO ()
atomicWriteFile dst src = atomicWithFile dst $ \h -> BS.L.hPut h src
atomicWithFile :: AbsolutePath
-> (Handle -> IO a)
-> IO a
atomicWithFile final callback =
withTempFile finalDir finalFileName $ \tempPath h -> do
a <- callback h
hClose h
renameFile tempPath final
return a
where
finalDir = takeDirectory final
finalFileName = unFragment (takeFileName final)