atomic-write 0.2.0.5 → 0.2.0.6
raw patch · 15 files changed
+1124/−41 lines, 15 filesdep +atomic-writenew-uploaderPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
Dependencies added: atomic-write
API changes (from Hackage documentation)
+ System.AtomicWrite.Writer.ByteString: atomicWriteFileWithMode :: FileMode -> FilePath -> ByteString -> IO ()
+ System.AtomicWrite.Writer.ByteStringBuilder: atomicWriteFileWithMode :: FileMode -> FilePath -> Builder -> IO ()
+ System.AtomicWrite.Writer.LazyByteString: atomicWriteFileWithMode :: FileMode -> FilePath -> ByteString -> IO ()
+ System.AtomicWrite.Writer.LazyText: atomicWriteFile :: FilePath -> Text -> IO ()
+ System.AtomicWrite.Writer.LazyText: atomicWriteFileWithMode :: FileMode -> FilePath -> Text -> IO ()
+ System.AtomicWrite.Writer.String: atomicWithFileAndMode :: FileMode -> FilePath -> (Handle -> IO ()) -> IO ()
+ System.AtomicWrite.Writer.String: atomicWriteFileWithMode :: FileMode -> FilePath -> String -> IO ()
+ System.AtomicWrite.Writer.Text: atomicWriteFileWithMode :: FileMode -> FilePath -> Text -> IO ()
Files
- LICENSE +1/−1
- atomic-write.cabal +16/−7
- spec/System/AtomicWrite/Writer/ByteStringBuilderSpec.hs +138/−0
- spec/System/AtomicWrite/Writer/ByteStringSpec.hs +136/−0
- spec/System/AtomicWrite/Writer/LazyByteStringSpec.hs +136/−0
- spec/System/AtomicWrite/Writer/LazyTextSpec.hs +136/−0
- spec/System/AtomicWrite/Writer/StringSpec.hs +140/−0
- spec/System/AtomicWrite/Writer/TextSpec.hs +136/−0
- src/System/AtomicWrite/Internal.hs +20/−2
- src/System/AtomicWrite/Writer/ByteString.hs +40/−6
- src/System/AtomicWrite/Writer/ByteStringBuilder.hs +43/−7
- src/System/AtomicWrite/Writer/LazyByteString.hs +41/−6
- src/System/AtomicWrite/Writer/LazyText.hs +51/−0
- src/System/AtomicWrite/Writer/String.hs +49/−6
- src/System/AtomicWrite/Writer/Text.hs +41/−6
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015 Justin Leitgeb+Copyright (c) 2015-2018 Stack Builders Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
atomic-write.cabal view
@@ -1,5 +1,5 @@ name: atomic-write-version: 0.2.0.5+version: 0.2.0.6 synopsis: Atomically write to a file homepage: https://github.com/stackbuilders/atomic-write description:@@ -35,15 +35,15 @@ . > import System.AtomicWrite.Writer.ByteString .- Then you can use the atomicWrite function that accepts a `FilePath` and a+ Then you can use the atomicWriteFile function that accepts a `FilePath` and a `ByteString`, e.g.: .- > atomicWrite myFilePath myByteString+ > atomicWriteFile myFilePath myByteString license: MIT license-file: LICENSE author: Justin Leitgeb-maintainer: justin@stackbuilders.com-copyright: 2015 Stack Builders Inc.+maintainer: support@stackbuilders.com+copyright: 2015-2018 Stack Builders Inc. category: System build-type: Simple cabal-version: >=1.10@@ -55,6 +55,7 @@ , System.AtomicWrite.Writer.LazyByteString , System.AtomicWrite.Writer.String , System.AtomicWrite.Writer.Text+ , System.AtomicWrite.Writer.LazyText other-modules: System.AtomicWrite.Internal @@ -70,15 +71,23 @@ default-language: Haskell2010 ghc-options: -Wall + test-suite atomic-write-test type: exitcode-stdio-1.0- hs-source-dirs: spec, src+ hs-source-dirs: spec main-is: Spec.hs + other-modules: System.AtomicWrite.Writer.ByteStringSpec+ , System.AtomicWrite.Writer.ByteStringBuilderSpec+ , System.AtomicWrite.Writer.LazyByteStringSpec+ , System.AtomicWrite.Writer.StringSpec+ , System.AtomicWrite.Writer.TextSpec+ , System.AtomicWrite.Writer.LazyTextSpec+ build-depends: base >= 4.5 && < 5.0+ , atomic-write , temporary , unix-compat- , directory , filepath , text , bytestring >= 0.10.4
+ spec/System/AtomicWrite/Writer/ByteStringBuilderSpec.hs view
@@ -0,0 +1,138 @@+module System.AtomicWrite.Writer.ByteStringBuilderSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+ (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.ByteString.Builder (lazyByteString)++import Data.ByteString.Lazy.Char8 (pack)++spec :: Spec+spec = do+ describe "atomicWriteFile" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFile path $ lazyByteString $ pack "just testing"+ contents <- readFile path++ contents `shouldBe` "just testing"+ it "preserves the permissions of original file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFile filePath $ lazyByteString $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100644+++ it "creates a new file with permissions based on active umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ sampleFilePath = joinPath [tmpDir, "sampleFile"]++ -- Set somewhat distinctive defaults for test+ _ <- setFileCreationMask 0o100171++ -- We don't know what the default file permissions are, so create a+ -- file to sample them.+ writeFile sampleFilePath "I'm being written to sample permissions"++ newStat <- getFileStatus sampleFilePath+ fileMode newStat `shouldBe` 0o100606++ atomicWriteFile filePath $ lazyByteString $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- The default tempfile permissions are 0600, so this fails unless we+ -- make sure that the default umask is relied on for creation of the+ -- tempfile.+ fileMode resultStat `shouldBe` 0o100606+ describe "atomicWriteFileWithMode" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFileWithMode 0o100777 path $ lazyByteString $ pack "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "changes the permissions of a previously created file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFileWithMode 0o100655 filePath $ lazyByteString $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100655+++ it "creates a new file with specified permissions" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ atomicWriteFileWithMode 0o100606 filePath $ lazyByteString $ pack "new contents"++ resultStat <- getFileStatus filePath++ fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/ByteStringSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.ByteStringSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.ByteString (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+ (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.ByteString.Char8 (pack)++spec :: Spec+spec = do+ describe "atomicWriteFile" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFile path $ pack "just testing"+ contents <- readFile path++ contents `shouldBe` "just testing"+ it "preserves the permissions of original file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100644+++ it "creates a new file with permissions based on active umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ sampleFilePath = joinPath [tmpDir, "sampleFile"]++ -- Set somewhat distinctive defaults for test+ _ <- setFileCreationMask 0o100171++ -- We don't know what the default file permissions are, so create a+ -- file to sample them.+ writeFile sampleFilePath "I'm being written to sample permissions"++ newStat <- getFileStatus sampleFilePath+ fileMode newStat `shouldBe` 0o100606++ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- The default tempfile permissions are 0600, so this fails unless we+ -- make sure that the default umask is relied on for creation of the+ -- tempfile.+ fileMode resultStat `shouldBe` 0o100606+ describe "atomicWriteFileWithMode" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFileWithMode 0o100777 path $ pack "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "changes the permissions of a previously created file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100655+++ it "creates a new file with specified permissions" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/LazyByteStringSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.LazyByteStringSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.LazyByteString (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+ (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.ByteString.Lazy.Char8 (pack)++spec :: Spec+spec = do+ describe "atomicWriteFile" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFile path $ pack "just testing"+ contents <- readFile path++ contents `shouldBe` "just testing"+ it "preserves the permissions of original file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100644+++ it "creates a new file with permissions based on active umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ sampleFilePath = joinPath [tmpDir, "sampleFile"]++ -- Set somewhat distinctive defaults for test+ _ <- setFileCreationMask 0o100171++ -- We don't know what the default file permissions are, so create a+ -- file to sample them.+ writeFile sampleFilePath "I'm being written to sample permissions"++ newStat <- getFileStatus sampleFilePath+ fileMode newStat `shouldBe` 0o100606++ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- The default tempfile permissions are 0600, so this fails unless we+ -- make sure that the default umask is relied on for creation of the+ -- tempfile.+ fileMode resultStat `shouldBe` 0o100606+ describe "atomicWriteFileWithMode" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFileWithMode 0o100777 path $ pack "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "changes the permissions of a previously created file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100655+++ it "creates a new file with specified permissions" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/LazyTextSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.LazyTextSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.LazyText (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+ (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.Text.Lazy (pack)++spec :: Spec+spec = do+ describe "atomicWriteFile" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFile path $ pack "just testing"+ contents <- readFile path++ contents `shouldBe` "just testing"+ it "preserves the permissions of original file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100644+++ it "creates a new file with permissions based on active umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ sampleFilePath = joinPath [tmpDir, "sampleFile"]++ -- Set somewhat distinctive defaults for test+ _ <- setFileCreationMask 0o100171++ -- We don't know what the default file permissions are, so create a+ -- file to sample them.+ writeFile sampleFilePath "I'm being written to sample permissions"++ newStat <- getFileStatus sampleFilePath+ fileMode newStat `shouldBe` 0o100606++ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- The default tempfile permissions are 0600, so this fails unless we+ -- make sure that the default umask is relied on for creation of the+ -- tempfile.+ fileMode resultStat `shouldBe` 0o100606+ describe "atomicWriteFileWithMode" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFileWithMode 0o100777 path $ pack "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "changes the permissions of a previously created file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100655+++ it "creates a new file with specified permissions" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/StringSpec.hs view
@@ -0,0 +1,140 @@+module System.AtomicWrite.Writer.StringSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.String (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath (joinPath)+import System.PosixCompat.Files+ (setFileMode, setFileCreationMask, getFileStatus, fileMode)+++{-# ANN module "HLint: ignore Reduce duplication" #-}++spec :: Spec+spec = do+ describe "atomicWriteFile" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFile path "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "preserves the permissions of original file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFile filePath "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100644+++ it "creates a new file with permissions based on active umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ sampleFilePath = joinPath [tmpDir, "sampleFile"]++ -- Set somewhat distinctive defaults for test+ _ <- setFileCreationMask 0o100171++ -- We don't know what the default file permissions are, so create a+ -- file to sample them.+ writeFile sampleFilePath "I'm being written to sample permissions"++ newStat <- getFileStatus sampleFilePath+ fileMode newStat `shouldBe` 0o100606++ atomicWriteFile filePath "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- The default tempfile permissions are 0600, so this fails unless we+ -- make sure that the default umask is relied on for creation of the+ -- tempfile.+ fileMode resultStat `shouldBe` 0o100606+ describe "atomicWriteFileWithMode" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFileWithMode 0o100777 path "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "changes the permissions of a previously created file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFileWithMode 0o100655 filePath "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100655+++ it "creates a new file with specified permissions" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ atomicWriteFileWithMode 0o100606 filePath "new contents"++ resultStat <- getFileStatus filePath++ fileMode resultStat `shouldBe` 0o100606
+ spec/System/AtomicWrite/Writer/TextSpec.hs view
@@ -0,0 +1,136 @@+module System.AtomicWrite.Writer.TextSpec (spec) where++import Test.Hspec (it, describe, shouldBe, Spec)++import System.AtomicWrite.Writer.Text (atomicWriteFile, atomicWriteFileWithMode)++import System.IO.Temp (withSystemTempDirectory)+import System.FilePath.Posix (joinPath)+import System.PosixCompat.Files+ (setFileMode, setFileCreationMask, getFileStatus, fileMode)++import Data.Text (pack)++spec :: Spec+spec = do+ describe "atomicWriteFile" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFile path $ pack "just testing"+ contents <- readFile path++ contents `shouldBe` "just testing"+ it "preserves the permissions of original file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100644+++ it "creates a new file with permissions based on active umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ sampleFilePath = joinPath [tmpDir, "sampleFile"]++ -- Set somewhat distinctive defaults for test+ _ <- setFileCreationMask 0o100171++ -- We don't know what the default file permissions are, so create a+ -- file to sample them.+ writeFile sampleFilePath "I'm being written to sample permissions"++ newStat <- getFileStatus sampleFilePath+ fileMode newStat `shouldBe` 0o100606++ atomicWriteFile filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- The default tempfile permissions are 0600, so this fails unless we+ -- make sure that the default umask is relied on for creation of the+ -- tempfile.+ fileMode resultStat `shouldBe` 0o100606+ describe "atomicWriteFileWithMode" $ do+ it "writes contents to a file" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do++ let path = joinPath [ tmpDir, "writeTest.tmp" ]++ atomicWriteFileWithMode 0o100777 path $ pack "just testing"++ contents <- readFile path++ contents `shouldBe` "just testing"+++ it "changes the permissions of a previously created file, regardless of umask" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let filePath = joinPath [tmpDir, "testFile"]++ writeFile filePath "initial contents"+ setFileMode filePath 0o100644++ newStat <- getFileStatus filePath+ fileMode newStat `shouldBe` 0o100644++ -- New files are created with 100600 perms.+ _ <- setFileCreationMask 0o100066++ -- Create a new file once different mask is set and make sure that mask+ -- is applied.+ writeFile (joinPath [tmpDir, "sanityCheck"]) "with sanity check mask"+ sanityCheckStat <- getFileStatus $ joinPath [tmpDir, "sanityCheck"]+ fileMode sanityCheckStat `shouldBe` 0o100600++ -- Since we move, this makes the new file assume the filemask of 0600+ atomicWriteFileWithMode 0o100655 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ -- reset mask to not break subsequent specs+ _ <- setFileCreationMask 0o100022++ -- Fails when using atomic mv command unless apply perms on initial file+ fileMode resultStat `shouldBe` 0o100655+++ it "creates a new file with specified permissions" $+ withSystemTempDirectory "atomicFileTest" $ \tmpDir -> do+ let+ filePath = joinPath [tmpDir, "testFile"]+ atomicWriteFileWithMode 0o100606 filePath $ pack "new contents"++ resultStat <- getFileStatus filePath++ fileMode resultStat `shouldBe` 0o100606
src/System/AtomicWrite/Internal.hs view
@@ -1,9 +1,21 @@-module System.AtomicWrite.Internal (closeAndRename, tempFileFor) where+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to create a temporary file with correct permissions+-- atomically. +module System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode) where+ import System.Directory (doesFileExist, renameFile) import System.PosixCompat.Files (setFileMode, getFileStatus, fileMode)+import System.Posix.Types (FileMode) import System.FilePath (takeDirectory)- import System.IO (hClose, Handle, openTempFile, openTempFileWithDefaultPermissions) @@ -39,3 +51,9 @@ closeAndRename :: Handle -> FilePath -> FilePath -> IO () closeAndRename tmpHandle tempFile destFile = hClose tmpHandle >> renameFile tempFile destFile++maybeSetFileMode :: FilePath -> Maybe FileMode -> IO ()+maybeSetFileMode path =+ maybe+ ( return () )+ ( \mode -> setFileMode path mode )
src/System/AtomicWrite/Writer/ByteString.hs view
@@ -1,14 +1,48 @@-module System.AtomicWrite.Writer.ByteString (atomicWriteFile) where+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to dump the contents of a ByteString+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.ByteString (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)++import System.Posix.Types (FileMode)+ import Data.ByteString (ByteString, hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile :: FilePath -- ^ The path where the file will be updated or created -> ByteString -- ^ The content to write to the file -> IO ()-atomicWriteFile f txt =- tempFileFor f >>= \(tmpPath, h) -> hPutStr h txt >> closeAndRename h tmpPath f+atomicWriteFile = atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems and updates permissions.+atomicWriteFileWithMode ::+ FileMode+ -> FilePath -- ^ The path where the file will be updated or created+ -> ByteString -- ^ The content to write to the file+ -> IO ()+atomicWriteFileWithMode mode =+ atomicWriteFileMaybeMode $ Just mode++-- | Helper function+atomicWriteFileMaybeMode ::+ Maybe FileMode+ -> FilePath -- ^ The path where the file will be updated or created+ -> ByteString -- ^ The content to write to the file+ -> IO ()+atomicWriteFileMaybeMode mmode path text =+ tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+ >> closeAndRename h tmpPath path+ >> maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/ByteStringBuilder.hs view
@@ -1,20 +1,53 @@-module System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile) where+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to dump the contents of a ByteStringBuilder+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.ByteStringBuilder (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import Data.ByteString.Builder (hPutBuilder, Builder, hPutBuilder) import GHC.IO.Handle (hSetBinaryMode, hSetBuffering, BufferMode (BlockBuffering)) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile :: FilePath -- ^ The path where the file will be updated or created -> Builder -- ^ The content to write to the file -> IO ()-atomicWriteFile f builder = do- (temppath, h) <- tempFileFor f+atomicWriteFile =+ atomicWriteFileMaybeMode Nothing +-- | Creates or modifies a file atomically on POSIX-compliant+-- systems and updates permissions.+atomicWriteFileWithMode ::+ FileMode+ -> FilePath -- ^ The path where the file will be updated or created+ -> Builder -- ^ The content to write to the file+ -> IO ()+atomicWriteFileWithMode mode =+ atomicWriteFileMaybeMode $ Just mode++-- Helper function+atomicWriteFileMaybeMode ::+ Maybe FileMode+ -> FilePath -- ^ The path where the file will be updated or created+ -> Builder -- ^ The content to write to the file+ -> IO ()+atomicWriteFileMaybeMode mmode path builder = do+ (temppath, h) <- tempFileFor path+ -- Recommendations for binary and buffering are from the -- Data.ByteString.Builder docs: -- http://hackage.haskell.org/package/bytestring-0.10.2.0/docs/Data-ByteString-Builder.html#v:hPutBuilder@@ -23,4 +56,7 @@ hPutBuilder h builder - closeAndRename h temppath f+ closeAndRename h temppath path++ -- set new permissions if a FileMode was provided+ maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/LazyByteString.hs view
@@ -1,14 +1,49 @@-module System.AtomicWrite.Writer.LazyByteString (atomicWriteFile) where+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to dump the contents of a Lazy ByteString+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.LazyByteString (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import Data.ByteString.Lazy (ByteString, hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile :: FilePath -- ^ The path where the file will be updated or created -> ByteString -- ^ The content to write to the file -> IO ()-atomicWriteFile f txt =- tempFileFor f >>= \(tmpPath, h) -> hPutStr h txt >> closeAndRename h tmpPath f+atomicWriteFile =+ atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+ FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> ByteString -- ^ The content to write to the file+ -> IO ()+atomicWriteFileWithMode mode =+ atomicWriteFileMaybeMode $ Just mode++-- Helper Function+atomicWriteFileMaybeMode ::+ Maybe FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> ByteString -- ^ The content to write to the file+ -> IO ()+atomicWriteFileMaybeMode mmode path text =+ tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+ >> closeAndRename h tmpPath path+ >> maybeSetFileMode path mmode
+ src/System/AtomicWrite/Writer/LazyText.hs view
@@ -0,0 +1,51 @@+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to dump the contents of a Text+-- to a file.++module System.AtomicWrite.Writer.LazyText (atomicWriteFile, atomicWriteFileWithMode) where++import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)++import Data.Text.Lazy (Text)++import Data.Text.Lazy.IO (hPutStr)++import System.Posix.Types (FileMode)++-- | Creates a file atomically on POSIX-compliant+-- systems while preserving permissions.+atomicWriteFile ::+ FilePath -- ^ The path where the file will be updated or created+ -> Text -- ^ The content to write to the file+ -> IO ()+atomicWriteFile =+ atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+ FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> Text -- ^ The content to write to the file+ -> IO ()+atomicWriteFileWithMode mode =+ atomicWriteFileMaybeMode $ Just mode++-- Helper Function+atomicWriteFileMaybeMode ::+ Maybe FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> Text -- ^ The content to write to the file+ -> IO ()+atomicWriteFileMaybeMode mmode path text =+ tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+ >> closeAndRename h tmpPath path+ >> maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/String.hs view
@@ -1,18 +1,61 @@-module System.AtomicWrite.Writer.String (atomicWriteFile, atomicWithFile) where+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to dump the contents of a String+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.String (atomicWriteFile, atomicWithFile, atomicWriteFileWithMode, atomicWithFileAndMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import System.IO (Handle, hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates or modifies a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile :: FilePath -- ^ The path where the file will be updated or created -> String -- ^ The content to write to the file -> IO () atomicWriteFile = (. flip hPutStr) . atomicWithFile ++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+ FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> String -- ^ The content to write to the file+ -> IO ()+atomicWriteFileWithMode mode = ( . flip hPutStr)+ . ( atomicWithFileAndMode mode )+ -- | A general version of 'atomicWriteFile' atomicWithFile :: FilePath -> (Handle -> IO ()) -> IO ()-atomicWithFile f action = - tempFileFor f >>= \(tmpPath, h) -> action h >> closeAndRename h tmpPath f+atomicWithFile f action =+ atomicWithFileAndMaybeMode Nothing f action++-- | A general version of 'atomicWriteFileWithMode'+atomicWithFileAndMode :: FileMode+ -> FilePath+ -> (Handle -> IO ())+ -> IO ()+atomicWithFileAndMode mode path action =+ atomicWithFileAndMaybeMode (Just mode) path action++-- | Helper function+atomicWithFileAndMaybeMode :: Maybe FileMode+ -> FilePath+ -> (Handle -> IO ())+ -> IO ()+atomicWithFileAndMaybeMode mmode path action =+ tempFileFor path >>= \(tmpPath, h) -> action h+ >> closeAndRename h tmpPath path+ >> maybeSetFileMode path mmode
src/System/AtomicWrite/Writer/Text.hs view
@@ -1,16 +1,51 @@-module System.AtomicWrite.Writer.Text (atomicWriteFile) where+-- |+-- Module : Configuration.Dotenv.Parse+-- Copyright : © 2015-2017 Stack Builders Inc.+-- License : MIT+--+-- Maintainer : Stack Builders <hackage@stackbuilders.com>+-- Stability : experimental+-- Portability : portable+--+-- Provides functionality to dump the contents of a Text+-- to a file. -import System.AtomicWrite.Internal (closeAndRename, tempFileFor)+module System.AtomicWrite.Writer.Text (atomicWriteFile, atomicWriteFileWithMode) where +import System.AtomicWrite.Internal (closeAndRename, tempFileFor, maybeSetFileMode)+ import Data.Text (Text) import Data.Text.IO (hPutStr) --- | Creates a file atomically on POSIX-compliant systems while preserving--- permissions.+import System.Posix.Types (FileMode)++-- | Creates a file atomically on POSIX-compliant+-- systems while preserving permissions. atomicWriteFile :: FilePath -- ^ The path where the file will be updated or created -> Text -- ^ The content to write to the file -> IO ()-atomicWriteFile f txt =- tempFileFor f >>= \(tmpPath, h) -> hPutStr h txt >> closeAndRename h tmpPath f+atomicWriteFile =+ atomicWriteFileMaybeMode Nothing++-- | Creates or modifies a file atomically on+-- POSIX-compliant systems and updates permissions+atomicWriteFileWithMode ::+ FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> Text -- ^ The content to write to the file+ -> IO ()+atomicWriteFileWithMode mode =+ atomicWriteFileMaybeMode $ Just mode++-- Helper Function+atomicWriteFileMaybeMode ::+ Maybe FileMode -- ^ The mode to set the file to+ -> FilePath -- ^ The path where the file will be updated or created+ -> Text -- ^ The content to write to the file+ -> IO ()+atomicWriteFileMaybeMode mmode path text =+ tempFileFor path >>= \(tmpPath, h) -> hPutStr h text+ >> closeAndRename h tmpPath path+ >> maybeSetFileMode path mmode