packages feed

safeio (empty) → 0.0.1.0

raw patch · 7 files changed

+183/−0 lines, 7 filesdep +HUnitdep +basedep +directorysetup-changed

Dependencies added: HUnit, base, directory, filepath, test-framework, test-framework-hunit, test-framework-th, unix

Files

+ COPYING view
@@ -0,0 +1,23 @@+Copyright (c) 2017+Luis Pedro Coelho <luis@luispedro.org>++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.
+ ChangeLog view
@@ -0,0 +1,2 @@+Version 0.0.1.0 2017-05-29 by luispedro+	* First version
+ README.md view
@@ -0,0 +1,22 @@+# SafeIO: Haskell library for safe (atomic) IO++This is a simple module, which enables writing in atomic mode. It+implements the following 4 step procedure:++1. Open a temporary file in the same directory as the final output.+2. Write to this temporary file.+3. Close and sync the file.+4. Atomically rename the file to its final destination.++## Example++    import System.IO.SafeWrite+    ...+    main = do+        withOutputFile "output.txt" $ \hout -> do+            hPutStrLn hout "Hello World"+++## Author++Luis Pedro Coelho [Email](mailto:luis@luispedro.org)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ System/IO/SafeWrite.hs view
@@ -0,0 +1,48 @@+module System.IO.SafeWrite+    ( withOutputFile+    , syncFile+    ) where++import           System.FilePath (takeDirectory)+import           System.Posix.IO (openFd, defaultFileFlags, closeFd, OpenMode(..))+import           System.Posix.Unistd (fileSynchronise)+import           Control.Exception (bracket, onException)+import           System.IO (Handle, hClose, openTempFile)+import           System.Directory (renameFile, removeFile)+++-- | Sync a file to disk+--+-- Only supported on Posix (patches with Windows support are welcome)+syncFile :: FilePath -- ^ File to sync+            -> IO ()+syncFile fname = do+    bracket (openFd fname ReadWrite Nothing defaultFileFlags)+        closeFd+        fileSynchronise+    -- The code below will not work on Windows+    bracket (openFd (takeDirectory fname) ReadOnly Nothing defaultFileFlags)+        closeFd+        fileSynchronise++-- | Variation of 'withFile' for output files.+--+-- Output is written to a temporary file. Once the action has completed, this+-- file is then sync'ed to disk (see |syncFile|) and renamed to its final+-- destination. In Posix, this is an atomic operation. If an exception is+-- raised, then the temporary output file will be deleted and not saved to+-- disk. Thus, the result file will either contain the complete result or will+-- be empty.+withOutputFile ::+            FilePath -- ^ Final desired file path+            -> (Handle -> IO a) -- ^ action to execute+            -> IO a+withOutputFile finalname act = do+    (tname, th) <- openTempFile (takeDirectory finalname) finalname+    (do+        r <- act th+        hClose th+        syncFile tname+        renameFile tname finalname+        return r) `onException` (hClose th >> removeFile tname)+
+ System/IO/SafeWrite/Tests.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++module Main where++import Test.Framework.TH+import Test.HUnit+import Test.Framework.Providers.HUnit++import Control.Exception (throwIO)+import System.IO.Error (isDoesNotExistError, catchIOError)+import System.Directory (doesFileExist, removeFile)+import System.IO (hPutStrLn)++import System.IO.SafeWrite++main :: IO ()+main = do+    removeFileIfExists outname+    $(defaultMainGenerator)++removeFileIfExists :: FilePath -> IO ()+removeFileIfExists fp = removeFile fp `catchIOError` ignoreDoesNotExistError+    where+        ignoreDoesNotExistError e+                | isDoesNotExistError e = return ()+                | otherwise = throwIO e++outname :: FilePath+outname = "testing-output.txt"++case_create_output = do+    withOutputFile outname $ flip hPutStrLn "Hello World"+    (doesFileExist outname) >>= assertBool "Output file was not created"+    removeFile outname++case_not_create_on_exception = do+    (withOutputFile outname $ \h -> do+        hPutStrLn h "Hello World"+        throwIO $ userError "Something bad happened") `catchIOError` \_ -> return ()+    (not <$> doesFileExist outname) >>= assertBool "Output file was created despite exception being raised"+
+ safeio.cabal view
@@ -0,0 +1,45 @@+name:               safeio+version:            0.0.1.0+synopsis:           Write output to disk atomically+description:        This package implements utilities to perform atomic output+		    so as to avoid the problem of partial intermediate files.+category:           IO+author:             Luis Pedro Coelho+maintainer:         Luis Pedro Coelho+license:            MIT+license-file:       COPYING+cabal-version:      >= 1.10+build-type:         Simple+bug-reports:        https://github.com/luispedro/safeio/issues+extra-source-files: README.md ChangeLog++library+  default-language: Haskell2010+  exposed-modules: System.IO.SafeWrite+  ghc-options: -Wall+  build-depends:+    base > 4 && < 5,+    directory,+    filepath,+    unix++Test-Suite safeiotest+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  main-is: System/IO/SafeWrite/Tests.hs+  other-modules: System.IO.SafeWrite+  ghc-options: -Wall+  hs-source-dirs: .+  build-depends:+    base > 4 && < 5,+    directory,+    filepath,+    unix,+    HUnit,+    test-framework,+    test-framework-hunit,+    test-framework-th++source-repository head+  type: git+  location: https://github.com/luispedro/safeio