Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c95c9f8f9 | |||
| 0ec2cf8ca5 | |||
| 9ac10a6a7d | |||
| 1a2c77c6a6 |
@@ -1,3 +1,5 @@
|
|||||||
|
0.8.1
|
||||||
|
* add 'readFile', 'readFileEOF', 'writeFile' and 'appendFile'
|
||||||
0.8.0
|
0.8.0
|
||||||
* 'copyDirRecursiveOverwrite', 'copyFileOverwrite', 'easyCopyOverwrite' and 'moveFileOverwrite' have been removed, instead use the versions without the *Overwrite suffix and pass in 'Strict' (for default behavior) or 'Overwrite' as the CopyMode argument
|
* 'copyDirRecursiveOverwrite', 'copyFileOverwrite', 'easyCopyOverwrite' and 'moveFileOverwrite' have been removed, instead use the versions without the *Overwrite suffix and pass in 'Strict' (for default behavior) or 'Overwrite' as the CopyMode argument
|
||||||
* introduced a new 'RecursiveErrorMode' type to allow controlling recursive behavior of 'copyDirRecursive' (use 'FailEarly' for default behavior)
|
* introduced a new 'RecursiveErrorMode' type to allow controlling recursive behavior of 'copyDirRecursive' (use 'FailEarly' for default behavior)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: hpath
|
name: hpath
|
||||||
version: 0.8.0
|
version: 0.8.1
|
||||||
synopsis: Support for well-typed paths
|
synopsis: Support for well-typed paths
|
||||||
description: Support for well-typed paths, utilizing ByteString under the hood.
|
description: Support for well-typed paths, utilizing ByteString under the hood.
|
||||||
license: BSD3
|
license: BSD3
|
||||||
|
|||||||
@@ -314,10 +314,6 @@ getAllParents (MkPath p)
|
|||||||
|
|
||||||
-- | Extract the directory name of a path.
|
-- | Extract the directory name of a path.
|
||||||
--
|
--
|
||||||
-- The following properties hold:
|
|
||||||
--
|
|
||||||
-- @dirname (p \<\/> a) == dirname p@
|
|
||||||
--
|
|
||||||
-- >>> dirname (MkPath "/abc/def/dod")
|
-- >>> dirname (MkPath "/abc/def/dod")
|
||||||
-- "/abc/def"
|
-- "/abc/def"
|
||||||
-- >>> dirname (MkPath "/")
|
-- >>> dirname (MkPath "/")
|
||||||
|
|||||||
127
src/HPath/IO.hs
127
src/HPath/IO.hs
@@ -60,6 +60,12 @@ module HPath.IO
|
|||||||
-- * File renaming/moving
|
-- * File renaming/moving
|
||||||
, renameFile
|
, renameFile
|
||||||
, moveFile
|
, moveFile
|
||||||
|
-- * File reading
|
||||||
|
, readFile
|
||||||
|
, readFileEOF
|
||||||
|
-- * File writing
|
||||||
|
, writeFile
|
||||||
|
, appendFile
|
||||||
-- * File permissions
|
-- * File permissions
|
||||||
, newFilePerms
|
, newFilePerms
|
||||||
, newDirPerms
|
, newDirPerms
|
||||||
@@ -97,6 +103,17 @@ import Data.ByteString
|
|||||||
(
|
(
|
||||||
ByteString
|
ByteString
|
||||||
)
|
)
|
||||||
|
import Data.ByteString.Builder
|
||||||
|
(
|
||||||
|
Builder
|
||||||
|
, byteString
|
||||||
|
, toLazyByteString
|
||||||
|
)
|
||||||
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
import Data.ByteString.Unsafe
|
||||||
|
(
|
||||||
|
unsafePackCStringFinalizer
|
||||||
|
)
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
(
|
(
|
||||||
for_
|
for_
|
||||||
@@ -112,6 +129,10 @@ import Data.Maybe
|
|||||||
(
|
(
|
||||||
catMaybes
|
catMaybes
|
||||||
)
|
)
|
||||||
|
import Data.Monoid
|
||||||
|
(
|
||||||
|
(<>)
|
||||||
|
)
|
||||||
import Data.Word
|
import Data.Word
|
||||||
(
|
(
|
||||||
Word8
|
Word8
|
||||||
@@ -145,7 +166,7 @@ import GHC.IO.Exception
|
|||||||
import HPath
|
import HPath
|
||||||
import HPath.Internal
|
import HPath.Internal
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import Prelude hiding (readFile)
|
import Prelude hiding (appendFile, readFile, writeFile)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
catchIOError
|
catchIOError
|
||||||
@@ -845,6 +866,110 @@ moveFile from to cm = do
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
--[ File Reading ]--
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- |Read the given file at once into memory as a strict ByteString.
|
||||||
|
-- Symbolic links are followed, no sanity checks on file size
|
||||||
|
-- or file type. File must exist.
|
||||||
|
--
|
||||||
|
-- Note: the size of the file is determined in advance, as to only
|
||||||
|
-- have one allocation.
|
||||||
|
--
|
||||||
|
-- Safety/reliability concerns:
|
||||||
|
--
|
||||||
|
-- * since amount of bytes to read is determined in advance,
|
||||||
|
-- the file might be read partially only if something else is
|
||||||
|
-- appending to it while reading
|
||||||
|
-- * the whole file is read into memory!
|
||||||
|
--
|
||||||
|
-- Throws:
|
||||||
|
--
|
||||||
|
-- - `InappropriateType` if file is not a regular file or a symlink
|
||||||
|
-- - `PermissionDenied` if we cannot read the file or the directory
|
||||||
|
-- containting it
|
||||||
|
-- - `NoSuchThing` if the file does not exist
|
||||||
|
readFile :: Path Abs -> IO ByteString
|
||||||
|
readFile p = withAbsPath p $ \fp ->
|
||||||
|
bracket (openFd fp SPI.ReadOnly [] Nothing) (SPI.closeFd) $ \fd -> do
|
||||||
|
stat <- PF.getFdStatus fd
|
||||||
|
let fsize = PF.fileSize stat
|
||||||
|
SPB.fdRead fd (fromIntegral fsize)
|
||||||
|
|
||||||
|
|
||||||
|
-- |Read the given file in chunks of size `8192` into memory until
|
||||||
|
-- `fread` returns 0. Returns a lazy ByteString, because it uses
|
||||||
|
-- Builders under the hood.
|
||||||
|
--
|
||||||
|
-- Safety/reliability concerns:
|
||||||
|
--
|
||||||
|
-- * the whole file is read into memory!
|
||||||
|
--
|
||||||
|
-- Throws:
|
||||||
|
--
|
||||||
|
-- - `InappropriateType` if file is not a regular file or a symlink
|
||||||
|
-- - `PermissionDenied` if we cannot read the file or the directory
|
||||||
|
-- containting it
|
||||||
|
-- - `NoSuchThing` if the file does not exist
|
||||||
|
readFileEOF :: Path Abs -> IO L.ByteString
|
||||||
|
readFileEOF p = withAbsPath p $ \fp ->
|
||||||
|
bracket (openFd fp SPI.ReadOnly [] Nothing) (SPI.closeFd) $ \fd ->
|
||||||
|
allocaBytes (fromIntegral bufSize) $ \buf -> read' fd buf mempty
|
||||||
|
where
|
||||||
|
bufSize :: CSize
|
||||||
|
bufSize = 8192
|
||||||
|
read' :: Fd -> Ptr Word8 -> Builder -> IO L.ByteString
|
||||||
|
read' fd buf builder = do
|
||||||
|
size <- SPB.fdReadBuf fd buf bufSize
|
||||||
|
if size == 0
|
||||||
|
then return $ toLazyByteString builder
|
||||||
|
else do
|
||||||
|
readBS <- unsafePackCStringFinalizer buf
|
||||||
|
(fromIntegral size)
|
||||||
|
mempty
|
||||||
|
read' fd buf (builder <> byteString readBS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
--[ File Writing ]--
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- |Write a given ByteString to a file, truncating the file beforehand.
|
||||||
|
-- The file must exist. Follows symlinks.
|
||||||
|
--
|
||||||
|
-- Throws:
|
||||||
|
--
|
||||||
|
-- - `InappropriateType` if file is not a regular file or a symlink
|
||||||
|
-- - `PermissionDenied` if we cannot read the file or the directory
|
||||||
|
-- containting it
|
||||||
|
-- - `NoSuchThing` if the file does not exist
|
||||||
|
writeFile :: Path Abs -> ByteString -> IO ()
|
||||||
|
writeFile p bs = withAbsPath p $ \fp ->
|
||||||
|
bracket (openFd fp SPI.WriteOnly [SPDF.oTrunc] Nothing) (SPI.closeFd) $ \fd ->
|
||||||
|
void $ SPB.fdWrite fd bs
|
||||||
|
|
||||||
|
|
||||||
|
-- |Append a given ByteString to a file.
|
||||||
|
-- The file must exist. Follows symlinks.
|
||||||
|
--
|
||||||
|
-- Throws:
|
||||||
|
--
|
||||||
|
-- - `InappropriateType` if file is not a regular file or a symlink
|
||||||
|
-- - `PermissionDenied` if we cannot read the file or the directory
|
||||||
|
-- containting it
|
||||||
|
-- - `NoSuchThing` if the file does not exist
|
||||||
|
appendFile :: Path Abs -> ByteString -> IO ()
|
||||||
|
appendFile p bs = withAbsPath p $ \fp ->
|
||||||
|
bracket (openFd fp SPI.WriteOnly [SPDF.oAppend] Nothing)
|
||||||
|
(SPI.closeFd) $ \fd -> void $ SPB.fdWrite fd bs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
--[ File Permissions]--
|
--[ File Permissions]--
|
||||||
|
|||||||
109
test/HPath/IO/AppendFileSpec.hs
Normal file
109
test/HPath/IO/AppendFileSpec.hs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
|
module HPath.IO.AppendFileSpec where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
ioeGetErrorType
|
||||||
|
)
|
||||||
|
import GHC.IO.Exception
|
||||||
|
(
|
||||||
|
IOErrorType(..)
|
||||||
|
)
|
||||||
|
import System.Process
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
upTmpDir :: IO ()
|
||||||
|
upTmpDir = do
|
||||||
|
setTmpDir "AppendFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "fileWithContent"
|
||||||
|
createRegularFile' "fileWithoutContent"
|
||||||
|
createSymlink' "inputFileSymL" "fileWithContent"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createRegularFile' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
createDir' "noPermsD"
|
||||||
|
createRegularFile' "noPermsD/inputFile"
|
||||||
|
noPerms "noPermsD"
|
||||||
|
writeFile' "fileWithContent" "BLKASL"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "fileWithContent"
|
||||||
|
deleteFile' "fileWithoutContent"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
normalFilePerms "noPerms"
|
||||||
|
deleteFile' "noPerms"
|
||||||
|
normalDirPerms "noPermsD"
|
||||||
|
deleteFile' "noPermsD/inputFile"
|
||||||
|
deleteDir' "noPermsD"
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
|
describe "HPath.IO.appendFile" $ do
|
||||||
|
|
||||||
|
-- successes --
|
||||||
|
it "appendFile file with content, everything clear" $ do
|
||||||
|
appendFile' "fileWithContent" "blahfaselllll"
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` "BLKASLblahfaselllll"
|
||||||
|
|
||||||
|
it "appendFile file with content, everything clear" $ do
|
||||||
|
appendFile' "fileWithContent" "gagagaga"
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` "BLKASLblahfaselllllgagagaga"
|
||||||
|
|
||||||
|
it "appendFile file with content, everything clear" $ do
|
||||||
|
appendFile' "fileWithContent" ""
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` "BLKASLblahfaselllllgagagaga"
|
||||||
|
|
||||||
|
it "appendFile file without content, everything clear" $ do
|
||||||
|
appendFile' "fileWithoutContent" "blahfaselllll"
|
||||||
|
out <- readFile' "fileWithoutContent"
|
||||||
|
out `shouldBe` "blahfaselllll"
|
||||||
|
|
||||||
|
it "appendFile, everything clear" $ do
|
||||||
|
appendFile' "fileWithoutContent" "gagagaga"
|
||||||
|
out <- readFile' "fileWithoutContent"
|
||||||
|
out `shouldBe` "blahfaselllllgagagaga"
|
||||||
|
|
||||||
|
it "appendFile symlink, everything clear" $ do
|
||||||
|
appendFile' "inputFileSymL" "blahfaselllll"
|
||||||
|
out <- readFile' "inputFileSymL"
|
||||||
|
out `shouldBe` "BLKASLblahfaselllllgagagagablahfaselllll"
|
||||||
|
|
||||||
|
it "appendFile symlink, everything clear" $ do
|
||||||
|
appendFile' "inputFileSymL" "gagagaga"
|
||||||
|
out <- readFile' "inputFileSymL"
|
||||||
|
out `shouldBe` "BLKASLblahfaselllllgagagagablahfaselllllgagagaga"
|
||||||
|
|
||||||
|
|
||||||
|
-- posix failures --
|
||||||
|
it "appendFile to dir, inappropriate type" $ do
|
||||||
|
appendFile' "alreadyExistsD" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
|
it "appendFile, no permissions to file" $ do
|
||||||
|
appendFile' "noPerms" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "appendFile, no permissions to file" $ do
|
||||||
|
appendFile' "noPermsD/inputFile" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "appendFile, file does not exist" $ do
|
||||||
|
appendFile' "gaga" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
86
test/HPath/IO/ReadFileEOFSpec.hs
Normal file
86
test/HPath/IO/ReadFileEOFSpec.hs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
|
module HPath.IO.ReadFileEOFSpec where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
ioeGetErrorType
|
||||||
|
)
|
||||||
|
import GHC.IO.Exception
|
||||||
|
(
|
||||||
|
IOErrorType(..)
|
||||||
|
)
|
||||||
|
import System.Process
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
upTmpDir :: IO ()
|
||||||
|
upTmpDir = do
|
||||||
|
setTmpDir "ReadFileEOFSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "fileWithContent"
|
||||||
|
createRegularFile' "fileWithoutContent"
|
||||||
|
createSymlink' "inputFileSymL" "fileWithContent"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createRegularFile' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
createDir' "noPermsD"
|
||||||
|
createRegularFile' "noPermsD/inputFile"
|
||||||
|
noPerms "noPermsD"
|
||||||
|
writeFile' "fileWithContent" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "fileWithContent"
|
||||||
|
deleteFile' "fileWithoutContent"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
normalFilePerms "noPerms"
|
||||||
|
deleteFile' "noPerms"
|
||||||
|
normalDirPerms "noPermsD"
|
||||||
|
deleteFile' "noPermsD/inputFile"
|
||||||
|
deleteDir' "noPermsD"
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
|
describe "HPath.IO.readFileEOF" $ do
|
||||||
|
|
||||||
|
-- successes --
|
||||||
|
it "readFileEOF (Strict) file with content, everything clear" $ do
|
||||||
|
out <- readFileEOF' "fileWithContent"
|
||||||
|
out `shouldBe` "Blahfaselgagaga"
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) symlink, everything clear" $ do
|
||||||
|
out <- readFileEOF' "inputFileSymL"
|
||||||
|
out `shouldBe` "Blahfaselgagaga"
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) empty file, everything clear" $ do
|
||||||
|
out <- readFileEOF' "fileWithoutContent"
|
||||||
|
out `shouldBe` ""
|
||||||
|
|
||||||
|
|
||||||
|
-- posix failures --
|
||||||
|
it "readFileEOF (Strict) directory, wrong file type" $ do
|
||||||
|
readFileEOF' "alreadyExistsD"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) file, no permissions" $ do
|
||||||
|
readFileEOF' "noPerms"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) file, no permissions on dir" $ do
|
||||||
|
readFileEOF' "noPermsD/inputFile"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) file, no such file" $ do
|
||||||
|
readFileEOF' "lalala"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
86
test/HPath/IO/ReadFileSpec.hs
Normal file
86
test/HPath/IO/ReadFileSpec.hs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
|
module HPath.IO.ReadFileSpec where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
ioeGetErrorType
|
||||||
|
)
|
||||||
|
import GHC.IO.Exception
|
||||||
|
(
|
||||||
|
IOErrorType(..)
|
||||||
|
)
|
||||||
|
import System.Process
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
upTmpDir :: IO ()
|
||||||
|
upTmpDir = do
|
||||||
|
setTmpDir "ReadFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "fileWithContent"
|
||||||
|
createRegularFile' "fileWithoutContent"
|
||||||
|
createSymlink' "inputFileSymL" "fileWithContent"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createRegularFile' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
createDir' "noPermsD"
|
||||||
|
createRegularFile' "noPermsD/inputFile"
|
||||||
|
noPerms "noPermsD"
|
||||||
|
writeFile' "fileWithContent" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "fileWithContent"
|
||||||
|
deleteFile' "fileWithoutContent"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
normalFilePerms "noPerms"
|
||||||
|
deleteFile' "noPerms"
|
||||||
|
normalDirPerms "noPermsD"
|
||||||
|
deleteFile' "noPermsD/inputFile"
|
||||||
|
deleteDir' "noPermsD"
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
|
describe "HPath.IO.readFile" $ do
|
||||||
|
|
||||||
|
-- successes --
|
||||||
|
it "readFile (Strict) file with content, everything clear" $ do
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` "Blahfaselgagaga"
|
||||||
|
|
||||||
|
it "readFile (Strict) symlink, everything clear" $ do
|
||||||
|
out <- readFile' "inputFileSymL"
|
||||||
|
out `shouldBe` "Blahfaselgagaga"
|
||||||
|
|
||||||
|
it "readFile (Strict) empty file, everything clear" $ do
|
||||||
|
out <- readFile' "fileWithoutContent"
|
||||||
|
out `shouldBe` ""
|
||||||
|
|
||||||
|
|
||||||
|
-- posix failures --
|
||||||
|
it "readFile (Strict) directory, wrong file type" $ do
|
||||||
|
readFile' "alreadyExistsD"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
|
it "readFile (Strict) file, no permissions" $ do
|
||||||
|
readFile' "noPerms"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "readFile (Strict) file, no permissions on dir" $ do
|
||||||
|
readFile' "noPermsD/inputFile"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "readFile (Strict) file, no such file" $ do
|
||||||
|
readFile' "lalala"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
109
test/HPath/IO/WriteFileSpec.hs
Normal file
109
test/HPath/IO/WriteFileSpec.hs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
|
module HPath.IO.WriteFileSpec where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
ioeGetErrorType
|
||||||
|
)
|
||||||
|
import GHC.IO.Exception
|
||||||
|
(
|
||||||
|
IOErrorType(..)
|
||||||
|
)
|
||||||
|
import System.Process
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
upTmpDir :: IO ()
|
||||||
|
upTmpDir = do
|
||||||
|
setTmpDir "WriteFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "fileWithContent"
|
||||||
|
createRegularFile' "fileWithoutContent"
|
||||||
|
createSymlink' "inputFileSymL" "fileWithContent"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createRegularFile' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
createDir' "noPermsD"
|
||||||
|
createRegularFile' "noPermsD/inputFile"
|
||||||
|
noPerms "noPermsD"
|
||||||
|
writeFile' "fileWithContent" "BLKASL"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "fileWithContent"
|
||||||
|
deleteFile' "fileWithoutContent"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
normalFilePerms "noPerms"
|
||||||
|
deleteFile' "noPerms"
|
||||||
|
normalDirPerms "noPermsD"
|
||||||
|
deleteFile' "noPermsD/inputFile"
|
||||||
|
deleteDir' "noPermsD"
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
|
describe "HPath.IO.writeFile" $ do
|
||||||
|
|
||||||
|
-- successes --
|
||||||
|
it "writeFile file with content, everything clear" $ do
|
||||||
|
writeFile' "fileWithContent" "blahfaselllll"
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` "blahfaselllll"
|
||||||
|
|
||||||
|
it "writeFile file with content, everything clear" $ do
|
||||||
|
writeFile' "fileWithContent" "gagagaga"
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` "gagagaga"
|
||||||
|
|
||||||
|
it "writeFile file with content, everything clear" $ do
|
||||||
|
writeFile' "fileWithContent" ""
|
||||||
|
out <- readFile' "fileWithContent"
|
||||||
|
out `shouldBe` ""
|
||||||
|
|
||||||
|
it "writeFile file without content, everything clear" $ do
|
||||||
|
writeFile' "fileWithoutContent" "blahfaselllll"
|
||||||
|
out <- readFile' "fileWithoutContent"
|
||||||
|
out `shouldBe` "blahfaselllll"
|
||||||
|
|
||||||
|
it "writeFile, everything clear" $ do
|
||||||
|
writeFile' "fileWithoutContent" "gagagaga"
|
||||||
|
out <- readFile' "fileWithoutContent"
|
||||||
|
out `shouldBe` "gagagaga"
|
||||||
|
|
||||||
|
it "writeFile symlink, everything clear" $ do
|
||||||
|
writeFile' "inputFileSymL" "blahfaselllll"
|
||||||
|
out <- readFile' "inputFileSymL"
|
||||||
|
out `shouldBe` "blahfaselllll"
|
||||||
|
|
||||||
|
it "writeFile symlink, everything clear" $ do
|
||||||
|
writeFile' "inputFileSymL" "gagagaga"
|
||||||
|
out <- readFile' "inputFileSymL"
|
||||||
|
out `shouldBe` "gagagaga"
|
||||||
|
|
||||||
|
|
||||||
|
-- posix failures --
|
||||||
|
it "writeFile to dir, inappropriate type" $ do
|
||||||
|
writeFile' "alreadyExistsD" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
|
it "writeFile, no permissions to file" $ do
|
||||||
|
writeFile' "noPerms" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "writeFile, no permissions to file" $ do
|
||||||
|
writeFile' "noPermsD/inputFile" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "writeFile, file does not exist" $ do
|
||||||
|
writeFile' "gaga" ""
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
@@ -28,6 +28,7 @@ import Data.IORef
|
|||||||
)
|
)
|
||||||
import HPath.IO
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
|
import Prelude hiding (appendFile, readFile, writeFile)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
(
|
(
|
||||||
fromJust
|
fromJust
|
||||||
@@ -46,6 +47,7 @@ import Data.ByteString
|
|||||||
(
|
(
|
||||||
ByteString
|
ByteString
|
||||||
)
|
)
|
||||||
|
import qualified Data.ByteString.Lazy as L
|
||||||
import System.Posix.Files.ByteString
|
import System.Posix.Files.ByteString
|
||||||
(
|
(
|
||||||
groupExecuteMode
|
groupExecuteMode
|
||||||
@@ -243,6 +245,12 @@ normalDirPerms path =
|
|||||||
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms
|
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms
|
||||||
|
|
||||||
|
|
||||||
|
normalFilePerms :: ByteString -> IO ()
|
||||||
|
{-# NOINLINE normalFilePerms #-}
|
||||||
|
normalFilePerms path =
|
||||||
|
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newFilePerms
|
||||||
|
|
||||||
|
|
||||||
getFileType' :: ByteString -> IO FileType
|
getFileType' :: ByteString -> IO FileType
|
||||||
{-# NOINLINE getFileType' #-}
|
{-# NOINLINE getFileType' #-}
|
||||||
getFileType' path = withTmpDir path getFileType
|
getFileType' path = withTmpDir path getFileType
|
||||||
@@ -276,11 +284,13 @@ canonicalizePath' p = withTmpDir p canonicalizePath
|
|||||||
writeFile' :: ByteString -> ByteString -> IO ()
|
writeFile' :: ByteString -> ByteString -> IO ()
|
||||||
{-# NOINLINE writeFile' #-}
|
{-# NOINLINE writeFile' #-}
|
||||||
writeFile' ip bs =
|
writeFile' ip bs =
|
||||||
withTmpDir ip $ \p -> do
|
withTmpDir ip $ \p -> writeFile p bs
|
||||||
fd <- SPI.openFd (P.fromAbs p) SPI.WriteOnly Nothing
|
|
||||||
SPI.defaultFileFlags
|
|
||||||
_ <- SPB.fdWrite fd bs
|
appendFile' :: ByteString -> ByteString -> IO ()
|
||||||
SPI.closeFd fd
|
{-# NOINLINE appendFile' #-}
|
||||||
|
appendFile' ip bs =
|
||||||
|
withTmpDir ip $ \p -> appendFile p bs
|
||||||
|
|
||||||
|
|
||||||
allDirectoryContents' :: ByteString -> IO [ByteString]
|
allDirectoryContents' :: ByteString -> IO [ByteString]
|
||||||
@@ -288,3 +298,13 @@ allDirectoryContents' :: ByteString -> IO [ByteString]
|
|||||||
allDirectoryContents' ip =
|
allDirectoryContents' ip =
|
||||||
withTmpDir ip $ \p -> DT.allDirectoryContents' (P.fromAbs p)
|
withTmpDir ip $ \p -> DT.allDirectoryContents' (P.fromAbs p)
|
||||||
|
|
||||||
|
|
||||||
|
readFile' :: ByteString -> IO ByteString
|
||||||
|
{-# NOINLINE readFile' #-}
|
||||||
|
readFile' p = withTmpDir p readFile
|
||||||
|
|
||||||
|
|
||||||
|
readFileEOF' :: ByteString -> IO L.ByteString
|
||||||
|
{-# NOINLINE readFileEOF' #-}
|
||||||
|
readFileEOF' p = withTmpDir p readFileEOF
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user