module Data.Conduit.SafeWrite ( safeSinkFile ) where import qualified Data.Conduit as C import qualified Data.Conduit.Combinators as CC import qualified Data.ByteString as B import Data.IORef (newIORef, writeIORef, readIORef) import System.FilePath (takeDirectory) import System.IO (hClose, openTempFile) import System.Directory (renameFile, removeFile) import Control.Monad (unless) import Control.Monad.Trans.Resource import Control.Monad.IO.Class (liftIO) import System.IO.SafeWrite (syncFile) -- | Write to file |finalname| using a temporary file and atomic move. -- -- The file is only written if the sink runs to completion without errors. Any -- form of early termination will cause the output to be removed. safeSinkFile :: (MonadResource m) => FilePath -- ^ Final filename -> C.Sink B.ByteString m () safeSinkFile finalname = C.bracketP acquire deleteTempOnError writeMove where acquire = do (tname, th) <- openTempFile (takeDirectory finalname) finalname completed <- newIORef False return (tname, th, completed) writeMove (tname, th, completed) = do CC.sinkHandle th liftIO $ do hClose th syncFile tname renameFile tname finalname writeIORef completed True deleteTempOnError (tname, th, completed) = do completed' <- readIORef completed unless completed' $ do hClose th removeFile tname