module Data.Conduit.SafeWrite
( safeSinkFile
, atomicConduitUseFile
) where
import qualified Data.ByteString as B
import qualified Data.Conduit as C
import qualified Data.Conduit.Combinators as CC
import Data.IORef (newIORef, writeIORef, readIORef, IORef)
import System.IO (Handle)
import Control.Monad.Trans.Resource
import Control.Monad (unless)
import Control.Monad.IO.Class (liftIO, MonadIO(..))
import System.IO.SafeWrite (allocateTempFile, finalizeTempFile)
safeSinkFile :: (MonadResource m) =>
FilePath
-> C.Sink B.ByteString m ()
safeSinkFile finalname = atomicConduitUseFile finalname CC.sinkHandle
atomicConduitUseFile :: (MonadResource m) =>
FilePath
-> (Handle -> C.ConduitM i o m a)
-> C.ConduitM i o m a
atomicConduitUseFile finalname cond = C.bracketP
acquire
deleteTempOnError
action
where
acquire :: IO ((FilePath, Handle), IORef Bool)
acquire = ((,) <$> allocateTempFile finalname <*> newIORef False)
action (tdata@(_, th), completed) = do
r <- cond th
liftIO $ do
finalizeTempFile finalname True tdata
writeIORef completed True
return r
deleteTempOnError :: ((FilePath, Handle), IORef Bool) -> IO ()
deleteTempOnError (tdata, completed) = do
unlessM (readIORef completed) $
liftIO $ finalizeTempFile finalname False tdata
unlessM c act = c >>= flip unless act