33 Commits
0.7.5 ... test

Author SHA1 Message Date
8a19f54a34 Test 2016-11-16 10:23:14 +01:00
3baecb7b51 Improve CopyDirRecursiveCollectFailures tests 2016-06-14 19:32:33 +02:00
5d5b0ae3c1 Add missing language pragma 2016-06-14 19:32:14 +02:00
f47c8edb42 Fix build for GHC < 7.10 2016-06-14 19:21:03 +02:00
ef66a24f87 Improve error handling
* remove some obsolete functions and error types from HPath.IO.Errors
  that are completely unused
* reworked the RecursiveFailure type to contain more information,
  so we can use it to programmatically make useful choices
  without examining the weakly types IO error attributes (like
  'ioGetFileName')
2016-06-14 19:13:25 +02:00
f6a5cb8668 Add test to basename 2016-06-13 13:51:53 +02:00
4dec385332 Improve createDirRecursive 2016-06-13 01:38:44 +02:00
5b08e14b55 Add createDirRecursive, fixes #6 2016-06-13 01:28:55 +02:00
ac381cbf60 Improve documentation 2016-06-05 22:19:30 +02:00
ce7fdcdcd6 Move documentation note about RecursiveFailure where it belongs 2016-06-05 22:04:16 +02:00
a31c9d1e88 Improve documentation and tests for file creation 2016-06-05 21:59:31 +02:00
a5942ff026 Use IfElse package for whenM/unlessM 2016-06-05 21:52:52 +02:00
4d71ad08ce Release 0.8.0 2016-06-05 17:56:31 +02:00
92017ab630 Make createRegularFile and createDir accept FileMode parameter 2016-06-05 17:46:25 +02:00
16af98b32d Be more specific about Overwrite mode 2016-06-05 16:38:54 +02:00
6da01e382f Improve documentation 2016-06-05 16:31:08 +02:00
ed06543981 Proper GHC conditionals to fix compiler warnings 2016-06-05 16:16:41 +02:00
d3eb2fc254 Fix build with GHC-7.8 2016-06-05 16:12:51 +02:00
a1eb06324f Rm unused imports 2016-06-05 16:09:34 +02:00
d12ce30f57 Fix docs and rename RecursiveMode to RecursiveErrorMode 2016-06-05 16:07:46 +02:00
7a6f0e8728 Fix spelling 2016-06-05 16:00:15 +02:00
7ed5829d47 Fix documentation 2016-06-05 15:57:41 +02:00
d708f80a1f TESTS: don't assume ordering of exceptions 2016-06-05 15:37:26 +02:00
f07619b7c6 TESTS: fix before/after 2016-06-05 15:25:57 +02:00
c5bcb90b65 TESTS: don't use lazy IO -.- 2016-06-05 14:55:21 +02:00
4f047dbc77 TESTS: import unsafePerformIO from System.IO.Unsafe 2016-06-05 14:46:45 +02:00
bc348c7dd5 TESTS: less side effects plz 2016-06-05 14:33:53 +02:00
5d1c5cc2ce Fix linter warning 2016-06-05 03:26:05 +02:00
8f6ca81d22 Add tests to RecreateSymlinkOverwriteSpec 2016-06-05 03:22:35 +02:00
a27d4ed55d Improve documentation 2016-06-05 03:22:11 +02:00
64ae6db83a New API: use CopyMode for overwriting and introduce RecursiveMode
This allows to specify the behavior on recursive operations,
such that one can collect failures instead of dying on the first
failure.
2016-06-05 03:13:33 +02:00
2a0a88a96d Release 0.7.5 2016-06-04 00:39:03 +02:00
69dbf6714d Relicense to BSD3 2016-06-04 00:39:03 +02:00
30 changed files with 1370 additions and 605 deletions

View File

@@ -1,3 +1,9 @@
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
* introduced a new 'RecursiveErrorMode' type to allow controlling recursive behavior of 'copyDirRecursive' (use 'FailEarly' for default behavior)
* 'createRegularFile' and 'createDir' now take FileMode as a parameter (also see 'newFilePerms' and 'newDirPerms')
* various documentation fixes
* improved reliability of tests
0.7.5: 0.7.5:
* relicense to BSD3 * relicense to BSD3
0.7.3: 0.7.3:

View File

@@ -1,5 +1,5 @@
name: hpath name: hpath
version: 0.7.5 version: 0.8.0
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
@@ -19,18 +19,21 @@ extra-source-files: README.md
library library
hs-source-dirs: src/ hs-source-dirs: src/
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -Wall if impl(ghc >= 8.0)
ghc-options: -Wall -Wno-redundant-constraints
else
ghc-options: -Wall
c-sources: cbits/dirutils.c c-sources: cbits/dirutils.c
exposed-modules: HPath, exposed-modules: HPath,
HPath.IO, HPath.IO,
HPath.IO.Errors, HPath.IO.Errors,
HPath.IO.Utils,
System.Posix.Directory.Foreign, System.Posix.Directory.Foreign,
System.Posix.Directory.Traversals, System.Posix.Directory.Traversals,
System.Posix.FD, System.Posix.FD,
System.Posix.FilePath System.Posix.FilePath
other-modules: HPath.Internal other-modules: HPath.Internal
build-depends: base >= 4.2 && <5 build-depends: base >= 4.2 && <5
, IfElse
, bytestring >= 0.9.2.0 , bytestring >= 0.9.2.0
, deepseq , deepseq
, exceptions , exceptions
@@ -73,11 +76,13 @@ test-suite spec
Main-Is: Main.hs Main-Is: Main.hs
other-modules: other-modules:
HPath.IO.CanonicalizePathSpec HPath.IO.CanonicalizePathSpec
HPath.IO.CopyDirRecursiveCollectFailuresSpec
HPath.IO.CopyDirRecursiveOverwriteSpec HPath.IO.CopyDirRecursiveOverwriteSpec
HPath.IO.CopyDirRecursiveSpec HPath.IO.CopyDirRecursiveSpec
HPath.IO.CopyFileOverwriteSpec HPath.IO.CopyFileOverwriteSpec
HPath.IO.CopyFileSpec HPath.IO.CopyFileSpec
HPath.IO.CreateDirSpec HPath.IO.CreateDirSpec
HPath.IO.CreateDirRecursiveSpec
HPath.IO.CreateRegularFileSpec HPath.IO.CreateRegularFileSpec
HPath.IO.CreateSymlinkSpec HPath.IO.CreateSymlinkSpec
HPath.IO.DeleteDirRecursiveSpec HPath.IO.DeleteDirRecursiveSpec
@@ -87,6 +92,7 @@ test-suite spec
HPath.IO.GetFileTypeSpec HPath.IO.GetFileTypeSpec
HPath.IO.MoveFileOverwriteSpec HPath.IO.MoveFileOverwriteSpec
HPath.IO.MoveFileSpec HPath.IO.MoveFileSpec
HPath.IO.RecreateSymlinkOverwriteSpec
HPath.IO.RecreateSymlinkSpec HPath.IO.RecreateSymlinkSpec
HPath.IO.RenameFileSpec HPath.IO.RenameFileSpec
Spec Spec
@@ -94,6 +100,7 @@ test-suite spec
GHC-Options: -Wall GHC-Options: -Wall
Build-Depends: base Build-Depends: base
, HUnit , HUnit
, IfElse
, bytestring , bytestring
, hpath , hpath
, hspec >= 1.3 , hspec >= 1.3

0
mo Normal file
View File

View File

@@ -336,6 +336,8 @@ dirname (MkPath fp) = MkPath (takeDirectory $ dropTrailingPathSeparator fp)
-- --
-- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Fn) -- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Fn)
-- Just "dod" -- Just "dod"
-- >>> basename (MkPath "/abc/def/dod/") :: Maybe (Path Fn)
-- Just "dod"
-- >>> basename (MkPath "/") :: Maybe (Path Fn) -- >>> basename (MkPath "/") :: Maybe (Path Fn)
-- Nothing -- Nothing
basename :: MonadThrow m => Path b -> m (Path Fn) basename :: MonadThrow m => Path b -> m (Path Fn)

View File

@@ -37,14 +37,13 @@ module HPath.IO
( (
-- * Types -- * Types
FileType(..) FileType(..)
, RecursiveErrorMode(..)
, CopyMode(..)
-- * File copying -- * File copying
, copyDirRecursive , copyDirRecursive
, copyDirRecursiveOverwrite
, recreateSymlink , recreateSymlink
, copyFile , copyFile
, copyFileOverwrite
, easyCopy , easyCopy
, easyCopyOverwrite
-- * File deletion -- * File deletion
, deleteFile , deleteFile
, deleteDir , deleteDir
@@ -56,11 +55,11 @@ module HPath.IO
-- * File creation -- * File creation
, createRegularFile , createRegularFile
, createDir , createDir
, createDirRecursive
, createSymlink , createSymlink
-- * File renaming/moving -- * File renaming/moving
, renameFile , renameFile
, moveFile , moveFile
, moveFileOverwrite
-- * File permissions -- * File permissions
, newFilePerms , newFilePerms
, newDirPerms , newDirPerms
@@ -80,14 +79,20 @@ import Control.Applicative
) )
import Control.Exception import Control.Exception
( (
bracket IOException
, bracket
, throwIO , throwIO
) )
import Control.Monad import Control.Monad
( (
void unless
, void
, when , when
) )
import Control.Monad.IfElse
(
unlessM
)
import Data.ByteString import Data.ByteString
( (
ByteString ByteString
@@ -96,6 +101,13 @@ import Data.Foldable
( (
for_ for_
) )
import Data.IORef
(
IORef
, modifyIORef
, newIORef
, readIORef
)
import Data.Maybe import Data.Maybe
( (
catMaybes catMaybes
@@ -108,9 +120,11 @@ import Foreign.C.Error
( (
eEXIST eEXIST
, eINVAL , eINVAL
, eNOENT
, eNOSYS , eNOSYS
, eNOTEMPTY , eNOTEMPTY
, eXDEV , eXDEV
, getErrno
) )
import Foreign.C.Types import Foreign.C.Types
( (
@@ -131,7 +145,6 @@ import GHC.IO.Exception
import HPath import HPath
import HPath.Internal import HPath.Internal
import HPath.IO.Errors import HPath.IO.Errors
import HPath.IO.Utils
import Prelude hiding (readFile) import Prelude hiding (readFile)
import System.IO.Error import System.IO.Error
( (
@@ -213,6 +226,28 @@ data FileType = Directory
-- |The error mode for recursive operations.
--
-- On `FailEarly` the whole operation fails immediately if any of the
-- recursive sub-operations fail, which is sort of the default
-- for IO operations.
--
-- On `CollectFailures` skips errors in the recursion and keeps on recursing.
-- However all errors are collected in the `RecursiveFailure` error type,
-- which is raised finally if there was any error. Also note that
-- `RecursiveFailure` does not give any guarantees on the ordering
-- of the collected exceptions.
data RecursiveErrorMode = FailEarly
| CollectFailures
-- |The mode for copy and file moves.
-- Overwrite mode is usually not very well defined, but is a convenience
-- shortcut.
data CopyMode = Strict -- ^ fail if any target exists
| Overwrite -- ^ overwrite targets
-------------------- --------------------
@@ -221,12 +256,22 @@ data FileType = Directory
-- |Copies a directory recursively to the given destination. -- |Copies the contents of a directory recursively to the given destination, while preserving permissions.
-- Does not follow symbolic links. -- Does not follow symbolic links. This behaves more or less like
-- the following, without descending into the destination if it
-- already exists:
-- --
-- For directory contents, this has the same behavior as `easyCopy` -- @
-- and thus will ignore any file type that is not `RegularFile`, -- cp -a \/source\/dir \/destination\/somedir
-- `SymbolicLink` or `Directory`. -- @
--
-- For directory contents, this will ignore any file type that is not
-- `RegularFile`, `SymbolicLink` or `Directory`.
--
-- For `Overwrite` copy mode this does not prune destination directory
-- contents, so the destination might contain more files than the source after
-- the operation has completed. Permissions of existing directories are
-- fixed.
-- --
-- Safety/reliability concerns: -- Safety/reliability concerns:
-- --
@@ -240,113 +285,138 @@ data FileType = Directory
-- Throws: -- Throws:
-- --
-- - `NoSuchThing` if source directory does not exist -- - `NoSuchThing` if source directory does not exist
-- - `PermissionDenied` if output directory is not writable
-- - `PermissionDenied` if source directory can't be opened -- - `PermissionDenied` if source directory can't be opened
-- - `SameFile` if source and destination are the same file
-- (`HPathIOException`)
-- - `DestinationInSource` if destination is contained in source
-- (`HPathIOException`)
--
-- Throws in `FailEarly` RecursiveErrorMode only:
--
-- - `PermissionDenied` if output directory is not writable
-- - `InvalidArgument` if source directory is wrong type (symlink) -- - `InvalidArgument` if source directory is wrong type (symlink)
-- - `InvalidArgument` if source directory is wrong type (regular file) -- - `InappropriateType` if source directory is wrong type (regular file)
--
-- Throws in `CollectFailures` RecursiveErrorMode only:
--
-- - `RecursiveFailure` if any of the recursive operations that are not
-- part of the top-directory sanity-checks fail (`HPathIOException`)
--
-- Throws in `Strict` CopyMode only:
--
-- - `AlreadyExists` if destination already exists -- - `AlreadyExists` if destination already exists
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
-- - `DestinationInSource` if destination is contained in source (`HPathIOException`)
copyDirRecursive :: Path Abs -- ^ source dir copyDirRecursive :: Path Abs -- ^ source dir
-> Path Abs -- ^ full destination -> Path Abs -- ^ destination (parent dirs
-- are not automatically created)
-> CopyMode
-> RecursiveErrorMode
-> IO () -> IO ()
copyDirRecursive fromp destdirp copyDirRecursive fromp destdirp cm rm
= do = do
ce <- newIORef []
-- for performance, sanity checks are only done for the top dir -- for performance, sanity checks are only done for the top dir
throwSameFile fromp destdirp throwSameFile fromp destdirp
throwDestinationInSource fromp destdirp throwDestinationInSource fromp destdirp
go fromp destdirp go ce fromp destdirp
collectedExceptions <- readIORef ce
unless (null collectedExceptions)
(throwIO . RecursiveFailure $ collectedExceptions)
where where
go :: Path Abs -> Path Abs -> IO () go :: IORef [(RecursiveFailureHint, IOException)]
go fromp' destdirp' = do -> Path Abs -> Path Abs -> IO ()
-- order is important here, so we don't get empty directories go ce fromp' destdirp' = do
-- NOTE: order is important here, so we don't get empty directories
-- on failure -- on failure
contents <- getDirsFiles fromp'
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp') -- get the contents of the source dir
createDirectory (fromAbs destdirp') fmode' contents <- handleIOE (ReadContentsFailed fromp' destdirp') ce [] $ do
contents <- getDirsFiles fromp'
-- we can't use `easyCopy` here, because we want to call `go` -- create the destination dir and
-- only return contents if we succeed
handleIOE (CreateDirFailed fromp' destdirp') ce [] $ do
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
case cm of
Strict -> createDirectory (fromAbs destdirp') fmode'
Overwrite -> catchIOError (createDirectory (fromAbs destdirp')
fmode')
$ \e ->
case ioeGetErrorType e of
AlreadyExists -> setFileMode (fromAbs destdirp')
fmode'
_ -> ioError e
return contents
-- NOTE: we can't use `easyCopy` here, because we want to call `go`
-- recursively to skip the top-level sanity checks -- recursively to skip the top-level sanity checks
-- if reading the contents and creating the destination dir worked,
-- then copy the contents to the destination too
for_ contents $ \f -> do for_ contents $ \f -> do
ftype <- getFileType f ftype <- getFileType f
newdest <- (destdirp' </>) <$> basename f newdest <- (destdirp' </>) <$> basename f
case ftype of case ftype of
SymbolicLink -> recreateSymlink f newdest SymbolicLink -> handleIOE (RecreateSymlinkFailed f newdest) ce ()
Directory -> go f newdest $ recreateSymlink f newdest cm
RegularFile -> copyFile f newdest Directory -> go ce f newdest
RegularFile -> handleIOE (CopyFileFailed f newdest) ce ()
$ copyFile f newdest cm
_ -> return () _ -> return ()
-- helper to handle errors for both RecursiveErrorModes and return a
-- |Like `copyDirRecursive` except it overwrites contents of directories -- default value
-- if any. handleIOE :: RecursiveFailureHint
-- -> IORef [(RecursiveFailureHint, IOException)]
-- For directory contents, this has the same behavior as `easyCopyOverwrite` -> a -> IO a -> IO a
-- and thus will ignore any file type that is not `RegularFile`, handleIOE hint ce def = case rm of
-- `SymbolicLink` or `Directory`. FailEarly -> handleIOError throwIO
-- CollectFailures -> handleIOError (\e -> modifyIORef ce ((hint, e):)
-- Throws: >> return def)
--
-- - `NoSuchThing` if source directory does not exist
-- - `PermissionDenied` if output directory is not writable
-- - `PermissionDenied` if source directory can't be opened
-- - `InvalidArgument` if source directory is wrong type (symlink)
-- - `InvalidArgument` if source directory is wrong type (regular file)
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
-- - `DestinationInSource` if destination is contained in source (`HPathIOException`)
copyDirRecursiveOverwrite :: Path Abs -- ^ source dir
-> Path Abs -- ^ full destination
-> IO ()
copyDirRecursiveOverwrite fromp destdirp
= do
-- for performance, sanity checks are only done for the top dir
throwSameFile fromp destdirp
throwDestinationInSource fromp destdirp
go fromp destdirp
where
go :: Path Abs -> Path Abs -> IO ()
go fromp' destdirp' = do
-- order is important here, so we don't get empty directories
-- on failure
contents <- getDirsFiles fromp'
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
catchIOError (createDirectory (fromAbs destdirp') fmode') $ \e ->
case ioeGetErrorType e of
AlreadyExists -> setFileMode (fromAbs destdirp') fmode'
_ -> ioError e
-- we can't use `easyCopyOverwrite` here, because we want to call `go`
-- recursively to skip the top-level sanity checks
for_ contents $ \f -> do
ftype <- getFileType f
newdest <- (destdirp' </>) <$> basename f
case ftype of
SymbolicLink -> whenM (doesFileExist newdest) (deleteFile newdest)
>> recreateSymlink f newdest
Directory -> go f newdest
RegularFile -> copyFileOverwrite f newdest
_ -> return ()
-- |Recreate a symlink. -- |Recreate a symlink.
-- --
-- In `Overwrite` copy mode only files and empty directories are deleted.
--
-- Safety/reliability concerns:
--
-- * `Overwrite` mode is inherently non-atomic
--
-- Throws: -- Throws:
-- --
-- - `InvalidArgument` if source file is wrong type (not a symlink) -- - `InvalidArgument` if source file is wrong type (not a symlink)
-- - `PermissionDenied` if output directory cannot be written to -- - `PermissionDenied` if output directory cannot be written to
-- - `PermissionDenied` if source directory cannot be opened -- - `PermissionDenied` if source directory cannot be opened
-- - `AlreadyExists` if destination file already exists -- - `SameFile` if source and destination are the same file
-- - `SameFile` if source and destination are the same file (`HPathIOException`) -- (`HPathIOException`)
--
--
-- Throws in `Strict` mode only:
--
-- - `AlreadyExists` if destination already exists
--
-- Throws in `Overwrite` mode only:
--
-- - `UnsatisfiedConstraints` if destination file is non-empty directory
-- --
-- Note: calls `symlink` -- Note: calls `symlink`
recreateSymlink :: Path Abs -- ^ the old symlink file recreateSymlink :: Path Abs -- ^ the old symlink file
-> Path Abs -- ^ destination file -> Path Abs -- ^ destination file
-> CopyMode
-> IO () -> IO ()
recreateSymlink symsource newsym recreateSymlink symsource newsym cm
= do = do
throwSameFile symsource newsym throwSameFile symsource newsym
sympoint <- readSymbolicLink (fromAbs symsource) sympoint <- readSymbolicLink (fromAbs symsource)
case cm of
Strict -> return ()
Overwrite -> do
writable <- isWritable (dirname newsym)
isfile <- doesFileExist newsym
isdir <- doesDirectoryExist newsym
when (writable && isfile) (deleteFile newsym)
when (writable && isdir) (deleteDir newsym)
createSymbolicLink sympoint (fromAbs newsym) createSymbolicLink sympoint (fromAbs newsym)
@@ -358,8 +428,11 @@ recreateSymlink symsource newsym
-- examine file types. For a more high-level version, use `easyCopy` -- examine file types. For a more high-level version, use `easyCopy`
-- instead. -- instead.
-- --
-- In `Overwrite` copy mode only overwrites actual files, not directories.
--
-- Safety/reliability concerns: -- Safety/reliability concerns:
-- --
-- * `Overwrite` mode is not atomic
-- * when used on `CharacterDevice`, reads the "contents" and copies -- * when used on `CharacterDevice`, reads the "contents" and copies
-- them to a regular file, which might take indefinitely -- them to a regular file, which might take indefinitely
-- * when used on `BlockDevice`, may either read the "contents" -- * when used on `BlockDevice`, may either read the "contents"
@@ -374,61 +447,39 @@ recreateSymlink symsource newsym
-- - `PermissionDenied` if output directory is not writable -- - `PermissionDenied` if output directory is not writable
-- - `PermissionDenied` if source directory can't be opened -- - `PermissionDenied` if source directory can't be opened
-- - `InvalidArgument` if source file is wrong type (symlink or directory) -- - `InvalidArgument` if source file is wrong type (symlink or directory)
-- - `SameFile` if source and destination are the same file
-- (`HPathIOException`)
--
-- Throws in `Strict` mode only:
--
-- - `AlreadyExists` if destination already exists -- - `AlreadyExists` if destination already exists
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
-- --
-- Note: calls `sendfile` and possibly `read`/`write` as fallback -- Note: calls `sendfile` and possibly `read`/`write` as fallback
copyFile :: Path Abs -- ^ source file copyFile :: Path Abs -- ^ source file
-> Path Abs -- ^ destination file -> Path Abs -- ^ destination file
-> CopyMode
-> IO () -> IO ()
copyFile from to = do copyFile from to cm = do
throwSameFile from to throwSameFile from to
_copyFile [SPDF.oNofollow]
[SPDF.oNofollow, SPDF.oExcl] case cm of
from to Strict -> _copyFile [SPDF.oNofollow]
[SPDF.oNofollow, SPDF.oExcl]
from to
-- |Like `copyFile` except it overwrites the destination if it already Overwrite ->
-- exists. catchIOError (_copyFile [SPDF.oNofollow]
-- [SPDF.oNofollow, SPDF.oTrunc]
-- Safety/reliability concerns: from to) $ \e ->
-- case ioeGetErrorType e of
-- * when used on `CharacterDevice`, reads the "contents" and copies -- if the destination file is not writable, we need to
-- them to a regular file, which might take indefinitely -- figure out if we can still copy by deleting it first
-- * when used on `BlockDevice`, may either read the "contents" PermissionDenied -> do
-- and copy them to a regular file (potentially hanging indefinitely) exists <- doesFileExist to
-- or may create a regular empty destination file writable <- isWritable (dirname to)
-- * when used on `NamedPipe`, will hang indefinitely if exists && writable
-- * not atomic, since it uses read/write then deleteFile to >> copyFile from to Strict
-- else ioError e
-- Throws: _ -> ioError e
--
-- - `NoSuchThing` if source file does not exist
-- - `NoSuchThing` if source file is a `Socket`
-- - `PermissionDenied` if output directory is not writable
-- - `PermissionDenied` if source directory can't be opened
-- - `InvalidArgument` if source file is wrong type (symlink or directory)
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
--
-- Note: calls `sendfile` and possibly `read`/`write` as fallback
copyFileOverwrite :: Path Abs -- ^ source file
-> Path Abs -- ^ destination file
-> IO ()
copyFileOverwrite from to = do
throwSameFile from to
catchIOError (_copyFile [SPDF.oNofollow]
[SPDF.oNofollow, SPDF.oTrunc]
from to) $ \e ->
case ioeGetErrorType e of
-- if the destination file is not writable, we need to
-- figure out if we can still copy by deleting it first
PermissionDenied -> do
exists <- doesFileExist to
writable <- isWritable (dirname to)
if exists && writable
then deleteFile to >> copyFile from to
else ioError e
_ -> ioError e
_copyFile :: [SPDF.Flags] _copyFile :: [SPDF.Flags]
@@ -439,8 +490,8 @@ _copyFile :: [SPDF.Flags]
_copyFile sflags dflags from to _copyFile sflags dflags from to
= =
-- from sendfile(2) manpage: -- from sendfile(2) manpage:
-- Applications may wish to fall back to read(2)/write(2) in the case -- Applications may wish to fall back to read(2)/write(2) in
-- where sendfile() fails with EINVAL or ENOSYS. -- the case where sendfile() fails with EINVAL or ENOSYS.
withAbsPath to $ \to' -> withAbsPath from $ \from' -> withAbsPath to $ \to' -> withAbsPath from $ \from' ->
catchErrno [eINVAL, eNOSYS] catchErrno [eINVAL, eNOSYS]
(sendFileCopy from' to') (sendFileCopy from' to')
@@ -476,7 +527,8 @@ _copyFile sflags dflags from to
if size == 0 if size == 0
then return $ fromIntegral totalsize then return $ fromIntegral totalsize
else do rsize <- SPB.fdWriteBuf dfd buf size else do rsize <- SPB.fdWriteBuf dfd buf size
when (rsize /= size) (throwIO . CopyFailed $ "wrong size!") when (rsize /= size) (ioError $ userError
"wrong size!")
write' sfd dfd buf (totalsize + fromIntegral size) write' sfd dfd buf (totalsize + fromIntegral size)
@@ -490,38 +542,18 @@ _copyFile sflags dflags from to
-- * calls `copyDirRecursive` for directories -- * calls `copyDirRecursive` for directories
easyCopy :: Path Abs easyCopy :: Path Abs
-> Path Abs -> Path Abs
-> CopyMode
-> RecursiveErrorMode
-> IO () -> IO ()
easyCopy from to = do easyCopy from to cm rm = do
ftype <- getFileType from ftype <- getFileType from
case ftype of case ftype of
SymbolicLink -> recreateSymlink from to SymbolicLink -> recreateSymlink from to cm
RegularFile -> copyFile from to RegularFile -> copyFile from to cm
Directory -> copyDirRecursive from to Directory -> copyDirRecursive from to cm rm
_ -> return () _ -> return ()
-- |Like `easyCopy` except it overwrites the destination if it already exists.
-- For directories, this overwrites contents without pruning them, so the resulting
-- directory may have more files than have been copied.
--
-- Safety/reliability concerns:
--
-- * examines filetypes explicitly
-- * calls `copyDirRecursive` for directories
easyCopyOverwrite :: Path Abs
-> Path Abs
-> IO ()
easyCopyOverwrite from to = do
ftype <- getFileType from
case ftype of
SymbolicLink -> whenM (doesFileExist to) (deleteFile to)
>> recreateSymlink from to
RegularFile -> copyFileOverwrite from to
Directory -> copyDirRecursiveOverwrite from to
_ -> return ()
@@ -644,15 +676,18 @@ executeFile fp args
--------------------- ---------------------
-- |Create an empty regular file at the given directory with the given filename. -- |Create an empty regular file at the given directory with the given
-- filename.
-- --
-- Throws: -- Throws:
-- --
-- - `PermissionDenied` if output directory cannot be written to -- - `PermissionDenied` if output directory cannot be written to
-- - `AlreadyExists` if destination file already exists -- - `AlreadyExists` if destination already exists
createRegularFile :: Path Abs -> IO () -- - `NoSuchThing` if any of the parent components of the path
createRegularFile dest = -- do not exist
bracket (SPI.openFd (fromAbs dest) SPI.WriteOnly (Just newFilePerms) createRegularFile :: FileMode -> Path Abs -> IO ()
createRegularFile fm dest =
bracket (SPI.openFd (fromAbs dest) SPI.WriteOnly (Just fm)
(SPI.defaultFileFlags { exclusive = True })) (SPI.defaultFileFlags { exclusive = True }))
SPI.closeFd SPI.closeFd
(\_ -> return ()) (\_ -> return ())
@@ -663,9 +698,40 @@ createRegularFile dest =
-- Throws: -- Throws:
-- --
-- - `PermissionDenied` if output directory cannot be written to -- - `PermissionDenied` if output directory cannot be written to
-- - `AlreadyExists` if destination directory already exists -- - `AlreadyExists` if destination already exists
createDir :: Path Abs -> IO () -- - `NoSuchThing` if any of the parent components of the path
createDir dest = createDirectory (fromAbs dest) newDirPerms -- do not exist
createDir :: FileMode -> Path Abs -> IO ()
createDir fm dest = createDirectory (fromAbs dest) fm
-- |Create an empty directory at the given directory with the given filename.
-- All parent directories are created with the same filemode. This
-- basically behaves like:
--
-- @
-- mkdir -p \/some\/dir
-- @
--
-- Safety/reliability concerns:
--
-- * not atomic
--
-- Throws:
--
-- - `PermissionDenied` if any part of the path components do not
-- exist and cannot be written to
-- - `AlreadyExists` if destination already exists and
-- is not a directory
createDirRecursive :: FileMode -> Path Abs -> IO ()
createDirRecursive fm dest =
catchIOError (createDirectory (fromAbs dest) fm) $ \e -> do
errno <- getErrno
case errno of
en | en == eEXIST -> unlessM (doesDirectoryExist dest) (ioError e)
| en == eNOENT -> createDirRecursive fm (dirname dest)
>> createDirectory (fromAbs dest) fm
| otherwise -> ioError e
-- |Create a symlink. -- |Create a symlink.
@@ -674,6 +740,8 @@ createDir dest = createDirectory (fromAbs dest) newDirPerms
-- --
-- - `PermissionDenied` if output directory cannot be written to -- - `PermissionDenied` if output directory cannot be written to
-- - `AlreadyExists` if destination file already exists -- - `AlreadyExists` if destination file already exists
-- - `NoSuchThing` if any of the parent components of the path
-- do not exist
-- --
-- Note: calls `symlink` -- Note: calls `symlink`
createSymlink :: Path Abs -- ^ destination file createSymlink :: Path Abs -- ^ destination file
@@ -703,10 +771,11 @@ createSymlink dest sympoint
-- - `NoSuchThing` if source file does not exist -- - `NoSuchThing` if source file does not exist
-- - `PermissionDenied` if output directory cannot be written to -- - `PermissionDenied` if output directory cannot be written to
-- - `PermissionDenied` if source directory cannot be opened -- - `PermissionDenied` if source directory cannot be opened
-- - `UnsupportedOperation` if source and destination are on different devices -- - `UnsupportedOperation` if source and destination are on different
-- - `FileDoesExist` if destination file already exists (`HPathIOException`) -- devices
-- - `DirDoesExist` if destination directory already exists (`HPathIOException`) -- - `AlreadyExists` if destination already exists
-- - `SameFile` if destination and source are the same file (`HPathIOException`) -- - `SameFile` if destination and source are the same file
-- (`HPathIOException`)
-- --
-- Note: calls `rename` (but does not allow to rename over existing files) -- Note: calls `rename` (but does not allow to rename over existing files)
renameFile :: Path Abs -> Path Abs -> IO () renameFile :: Path Abs -> Path Abs -> IO ()
@@ -725,70 +794,54 @@ renameFile fromf tof = do
-- --
-- Safety/reliability concerns: -- Safety/reliability concerns:
-- --
-- * `Overwrite` mode is not atomic
-- * copy-delete fallback is inherently non-atomic -- * copy-delete fallback is inherently non-atomic
-- * since this function calls `easyCopy` and `easyDelete` as a fallback -- * since this function calls `easyCopy` and `easyDelete` as a fallback
-- to `renameFile`, file types that are not `RegularFile`, `SymbolicLink` -- to `renameFile`, file types that are not `RegularFile`, `SymbolicLink`
-- or `Directory` may be ignored -- or `Directory` may be ignored
-- * for `Overwrite` mode, the destination will be deleted (not recursively)
-- before moving
-- --
-- Throws: -- Throws:
-- --
-- - `NoSuchThing` if source file does not exist -- - `NoSuchThing` if source file does not exist
-- - `PermissionDenied` if output directory cannot be written to -- - `PermissionDenied` if output directory cannot be written to
-- - `PermissionDenied` if source directory cannot be opened -- - `PermissionDenied` if source directory cannot be opened
-- - `FileDoesExist` if destination file already exists (`HPathIOException`) -- - `SameFile` if destination and source are the same file
-- - `DirDoesExist` if destination directory already exists (`HPathIOException`) -- (`HPathIOException`)
-- - `SameFile` if destination and source are the same file (`HPathIOException`) --
-- Throws in `Strict` mode only:
--
-- - `AlreadyExists` if destination already exists
-- --
-- Note: calls `rename` (but does not allow to rename over existing files) -- Note: calls `rename` (but does not allow to rename over existing files)
moveFile :: Path Abs -- ^ file to move moveFile :: Path Abs -- ^ file to move
-> Path Abs -- ^ destination -> Path Abs -- ^ destination
-> CopyMode
-> IO () -> IO ()
moveFile from to = do moveFile from to cm = do
throwSameFile from to throwSameFile from to
catchErrno [eXDEV] (renameFile from to) $ do case cm of
easyCopy from to Strict -> catchErrno [eXDEV] (renameFile from to) $ do
easyDelete from easyCopy from to Strict FailEarly
easyDelete from
Overwrite -> do
ft <- getFileType from
writable <- isWritable $ dirname to
case ft of
RegularFile -> do
exists <- doesFileExist to
when (exists && writable) (deleteFile to)
SymbolicLink -> do
exists <- doesFileExist to
when (exists && writable) (deleteFile to)
Directory -> do
exists <- doesDirectoryExist to
when (exists && writable) (deleteDir to)
_ -> return ()
moveFile from to Strict
-- |Like `moveFile`, but overwrites the destination if it exists.
--
-- Does not follow symbolic links, but renames the symbolic link file.
--
-- Ignores any file type that is not `RegularFile`, `SymbolicLink` or
-- `Directory`.
--
-- Safety/reliability concerns:
--
-- * copy-delete fallback is inherently non-atomic
-- * checks for file types and destination file existence explicitly
--
-- Throws:
--
-- - `NoSuchThing` if source file does not exist
-- - `PermissionDenied` if output directory cannot be written to
-- - `PermissionDenied` if source directory cannot be opened
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
--
-- Note: calls `rename` (but does not allow to rename over existing files)
moveFileOverwrite :: Path Abs -- ^ file to move
-> Path Abs -- ^ destination
-> IO ()
moveFileOverwrite from to = do
throwSameFile from to
ft <- getFileType from
writable <- isWritable $ dirname to
case ft of
RegularFile -> do
exists <- doesFileExist to
when (exists && writable) (deleteFile to)
SymbolicLink -> do
exists <- doesFileExist to
when (exists && writable) (deleteFile to)
Directory -> do
exists <- doesDirectoryExist to
when (exists && writable) (deleteDir to)
_ -> return ()
moveFile from to
@@ -828,6 +881,8 @@ newDirPerms
-- |Gets all filenames of the given directory. This excludes "." and "..". -- |Gets all filenames of the given directory. This excludes "." and "..".
-- This version does not follow symbolic links. -- This version does not follow symbolic links.
-- --
-- The contents are not sorted and there is no guarantee on the ordering.
--
-- Throws: -- Throws:
-- --
-- - `NoSuchThing` if directory does not exist -- - `NoSuchThing` if directory does not exist

View File

@@ -16,23 +16,20 @@ module HPath.IO.Errors
( (
-- * Types -- * Types
HPathIOException(..) HPathIOException(..)
, RecursiveFailureHint(..)
-- * Exception identifiers -- * Exception identifiers
, isFileDoesNotExist
, isDirDoesNotExist
, isSameFile , isSameFile
, isDestinationInSource , isDestinationInSource
, isFileDoesExist , isRecursiveFailure
, isDirDoesExist , isReadContentsFailed
, isInvalidOperation , isCreateDirFailed
, isCan'tOpenDirectory , isCopyFileFailed
, isCopyFailed , isRecreateSymlinkFailed
-- * Path based functions -- * Path based functions
, throwFileDoesExist , throwFileDoesExist
, throwDirDoesExist , throwDirDoesExist
, throwFileDoesNotExist
, throwDirDoesNotExist
, throwSameFile , throwSameFile
, sameFile , sameFile
, throwDestinationInSource , throwDestinationInSource
@@ -40,7 +37,6 @@ module HPath.IO.Errors
, doesDirectoryExist , doesDirectoryExist
, isWritable , isWritable
, canOpenDirectory , canOpenDirectory
, throwCantOpenDirectory
-- * Error handling functions -- * Error handling functions
, catchErrno , catchErrno
@@ -62,6 +58,10 @@ import Control.Monad
forM forM
, when , when
) )
import Control.Monad.IfElse
(
whenM
)
import Data.ByteString import Data.ByteString
( (
ByteString ByteString
@@ -70,11 +70,10 @@ import Data.ByteString.UTF8
( (
toString toString
) )
import Data.Data
(
Data(..)
)
import Data.Typeable import Data.Typeable
(
Typeable
)
import Foreign.C.Error import Foreign.C.Error
( (
getErrno getErrno
@@ -89,11 +88,12 @@ import {-# SOURCE #-} HPath.IO
( (
canonicalizePath canonicalizePath
) )
import HPath.IO.Utils
import System.IO.Error import System.IO.Error
( (
catchIOError alreadyExistsErrorType
, catchIOError
, ioeGetErrorType , ioeGetErrorType
, mkIOError
) )
import qualified System.Posix.Directory.ByteString as PFD import qualified System.Posix.Directory.ByteString as PFD
@@ -105,40 +105,36 @@ import System.Posix.Files.ByteString
import qualified System.Posix.Files.ByteString as PF import qualified System.Posix.Files.ByteString as PF
data HPathIOException = FileDoesNotExist ByteString -- |Additional generic IO exceptions that the posix functions
| DirDoesNotExist ByteString -- do not provide.
| SameFile ByteString ByteString data HPathIOException = SameFile ByteString ByteString
| DestinationInSource ByteString ByteString | DestinationInSource ByteString ByteString
| FileDoesExist ByteString | RecursiveFailure [(RecursiveFailureHint, IOException)]
| DirDoesExist ByteString deriving (Eq, Show, Typeable)
| InvalidOperation String
| Can'tOpenDirectory ByteString
| CopyFailed String
deriving (Typeable, Eq, Data)
instance Show HPathIOException where -- |A type for giving failure hints on recursive failure, which allows
show (FileDoesNotExist fp) = "File does not exist:" ++ toString fp -- to programmatically make choices without examining
show (DirDoesNotExist fp) = "Directory does not exist: " -- the weakly typed I/O error attributes (like `ioeGetFileName`).
++ toString fp --
show (SameFile fp1 fp2) = toString fp1 -- The first argument to the data constructor is always the
++ " and " ++ toString fp2 -- source and the second the destination.
++ " are the same file!" data RecursiveFailureHint = ReadContentsFailed (Path Abs) (Path Abs)
show (DestinationInSource fp1 fp2) = toString fp1 | CreateDirFailed (Path Abs) (Path Abs)
++ " is contained in " | CopyFileFailed (Path Abs) (Path Abs)
++ toString fp2 | RecreateSymlinkFailed (Path Abs) (Path Abs)
show (FileDoesExist fp) = "File does exist: " ++ toString fp deriving (Eq, Show)
show (DirDoesExist fp) = "Directory does exist: " ++ toString fp
show (InvalidOperation str) = "Invalid operation: " ++ str
show (Can'tOpenDirectory fp) = "Can't open directory: "
++ toString fp
show (CopyFailed str) = "Copying failed: " ++ str
instance Exception HPathIOException instance Exception HPathIOException
toConstr :: HPathIOException -> String
toConstr SameFile {} = "SameFile"
toConstr DestinationInSource {} = "DestinationInSource"
toConstr RecursiveFailure {} = "RecursiveFailure"
@@ -146,16 +142,23 @@ instance Exception HPathIOException
--[ Exception identifiers ]-- --[ Exception identifiers ]--
----------------------------- -----------------------------
isFileDoesNotExist, isDirDoesNotExist, isSameFile, isDestinationInSource, isFileDoesExist, isDirDoesExist, isInvalidOperation, isCan'tOpenDirectory, isCopyFailed :: HPathIOException -> Bool
isFileDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesNotExist{} isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool
isDirDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesNotExist{}
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{} isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{}
isDestinationInSource ex = toConstr (ex :: HPathIOException) == toConstr DestinationInSource{} isDestinationInSource ex = toConstr (ex :: HPathIOException) == toConstr DestinationInSource{}
isFileDoesExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesExist{} isRecursiveFailure ex = toConstr (ex :: HPathIOException) == toConstr RecursiveFailure{}
isDirDoesExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesExist{}
isInvalidOperation ex = toConstr (ex :: HPathIOException) == toConstr InvalidOperation{}
isCan'tOpenDirectory ex = toConstr (ex :: HPathIOException) == toConstr Can'tOpenDirectory{} isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool
isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{} isReadContentsFailed ReadContentsFailed{} = True
isReadContentsFailed _ = False
isCreateDirFailed CreateDirFailed{} = True
isCreateDirFailed _ = False
isCopyFileFailed CopyFileFailed{} = True
isCopyFileFailed _ = False
isRecreateSymlinkFailed RecreateSymlinkFailed{} = True
isRecreateSymlinkFailed _ = False
@@ -165,28 +168,28 @@ isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{}
---------------------------- ----------------------------
-- |Throws `AlreadyExists` `IOError` if file exists.
throwFileDoesExist :: Path Abs -> IO () throwFileDoesExist :: Path Abs -> IO ()
throwFileDoesExist fp = throwFileDoesExist fp =
whenM (doesFileExist fp) (throwIO . FileDoesExist whenM (doesFileExist fp)
. fromAbs $ fp) (ioError . mkIOError
alreadyExistsErrorType
"File already exists"
Nothing
$ (Just (toString $ fromAbs fp))
)
-- |Throws `AlreadyExists` `IOError` if directory exists.
throwDirDoesExist :: Path Abs -> IO () throwDirDoesExist :: Path Abs -> IO ()
throwDirDoesExist fp = throwDirDoesExist fp =
whenM (doesDirectoryExist fp) (throwIO . DirDoesExist whenM (doesDirectoryExist fp)
. fromAbs $ fp) (ioError . mkIOError
alreadyExistsErrorType
"Directory already exists"
throwFileDoesNotExist :: Path Abs -> IO () Nothing
throwFileDoesNotExist fp = $ (Just (toString $ fromAbs fp))
unlessM (doesFileExist fp) (throwIO . FileDoesNotExist )
. fromAbs $ fp)
throwDirDoesNotExist :: Path Abs -> IO ()
throwDirDoesNotExist fp =
unlessM (doesDirectoryExist fp) (throwIO . DirDoesNotExist
. fromAbs $ fp)
-- |Uses `isSameFile` and throws `SameFile` if it returns True. -- |Uses `isSameFile` and throws `SameFile` if it returns True.
@@ -271,13 +274,6 @@ canOpenDirectory fp =
return True return True
-- |Throws a `Can'tOpenDirectory` HPathIOException if the directory at the given
-- path cannot be opened.
throwCantOpenDirectory :: Path Abs -> IO ()
throwCantOpenDirectory fp =
unlessM (canOpenDirectory fp)
(throwIO . Can'tOpenDirectory . fromAbs $ fp)
-------------------------------- --------------------------------
@@ -357,3 +353,4 @@ reactOnError a ios fmios =
else y) else y)
(throwIO ex) (throwIO ex)
fmios fmios

View File

@@ -1,32 +0,0 @@
-- |
-- Module : HPath.IO.Utils
-- Copyright : © 2016 Julian Ospald
-- License : BSD3
--
-- Maintainer : Julian Ospald <hasufell@posteo.de>
-- Stability : experimental
-- Portability : portable
--
-- Random and general IO/monad utilities.
module HPath.IO.Utils where
import Control.Monad
(
when
, unless
)
-- |If the value of the first argument is True, then execute the action
-- provided in the second argument, otherwise do nothing.
whenM :: Monad m => m Bool -> m () -> m ()
whenM mb a = mb >>= (`when` a)
-- |If the value of the first argument is False, then execute the action
-- provided in the second argument, otherwise do nothing.
unlessM :: Monad m => m Bool -> m () -> m ()
unlessM mb a = mb >>= (`unless` a)

View File

@@ -10,6 +10,7 @@
-- Traversal and read operations on directories. -- Traversal and read operations on directories.
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-} {-# LANGUAGE PackageImports #-}
@@ -37,7 +38,10 @@ module System.Posix.Directory.Traversals (
, realpath , realpath
) where ) where
import Control.Applicative
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>))
#endif
import Control.Monad import Control.Monad
import System.Posix.FilePath ((</>)) import System.Posix.FilePath ((</>))
import System.Posix.Directory.Foreign import System.Posix.Directory.Foreign

View File

@@ -13,12 +13,15 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CanonicalizePathSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
setupFiles = do setupFiles = do
createRegularFile' "file" createRegularFile' "file"
@@ -37,7 +40,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.canonicalizePath" $ do describe "HPath.IO.canonicalizePath" $ do
-- successes -- -- successes --

View File

@@ -0,0 +1,247 @@
{-# LANGUAGE OverloadedStrings #-}
module HPath.IO.CopyDirRecursiveCollectFailuresSpec where
import Test.Hspec
import Data.List (sort)
import HPath.IO
import HPath.IO.Errors
import System.IO.Error
(
ioeGetErrorType
)
import GHC.IO.Exception
(
IOErrorType(..)
)
import System.Exit
import System.Process
import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CopyDirRecursiveCollectFailuresSpec"
createTmpDir
setupFiles :: IO ()
setupFiles = do
createRegularFile' "alreadyExists"
createRegularFile' "wrongInput"
createSymlink' "wrongInputSymL" "inputDir/"
createDir' "alreadyExistsD"
createDir' "noPerms"
createDir' "noWritePerm"
createDir' "inputDir"
createDir' "inputDir/bar"
createDir' "inputDir/foo"
createRegularFile' "inputDir/foo/inputFile1"
createRegularFile' "inputDir/inputFile2"
createRegularFile' "inputDir/bar/inputFile3"
writeFile' "inputDir/foo/inputFile1" "SDAADSdsada"
writeFile' "inputDir/inputFile2" "Blahfaselgagaga"
writeFile' "inputDir/bar/inputFile3"
"fdfdssdffsd3223sasdasdasdadasasddasdasdasasd4"
createDir' "inputDir1"
createDir' "inputDir1/foo2"
createDir' "inputDir1/foo2/foo3"
createDir' "inputDir1/foo2/foo4"
createRegularFile' "inputDir1/foo2/inputFile1"
createRegularFile' "inputDir1/foo2/inputFile2"
createRegularFile' "inputDir1/foo2/inputFile3"
createRegularFile' "inputDir1/foo2/foo4/inputFile4"
createRegularFile' "inputDir1/foo2/foo4/inputFile6"
createRegularFile' "inputDir1/foo2/foo3/inputFile5"
noPerms "inputDir1/foo2/foo3"
createDir' "outputDir1"
createDir' "outputDir1/foo2"
createDir' "outputDir1/foo2/foo4"
createDir' "outputDir1/foo2/foo4/inputFile4"
createRegularFile' "outputDir1/foo2/foo4/inputFile6"
noPerms "outputDir1/foo2/foo4/inputFile4"
noPerms "outputDir1/foo2/foo4"
noPerms "noPerms"
noWritableDirPerms "noWritePerm"
cleanupFiles :: IO ()
cleanupFiles = do
normalDirPerms "noPerms"
normalDirPerms "noWritePerm"
normalDirPerms "inputDir1/foo2/foo3"
deleteFile' "inputDir1/foo2/foo4/inputFile4"
deleteFile' "inputDir1/foo2/foo4/inputFile6"
deleteFile' "inputDir1/foo2/inputFile1"
deleteFile' "inputDir1/foo2/inputFile2"
deleteFile' "inputDir1/foo2/inputFile3"
deleteFile' "inputDir1/foo2/foo3/inputFile5"
deleteDir' "inputDir1/foo2/foo3"
deleteDir' "inputDir1/foo2/foo4"
deleteDir' "inputDir1/foo2"
deleteDir' "inputDir1"
normalDirPerms "outputDir1/foo2/foo4"
normalDirPerms "outputDir1/foo2/foo4/inputFile4"
deleteFile' "outputDir1/foo2/foo4/inputFile6"
deleteDir' "outputDir1/foo2/foo4/inputFile4"
deleteDir' "outputDir1/foo2/foo4"
deleteDir' "outputDir1/foo2"
deleteDir' "outputDir1"
deleteFile' "alreadyExists"
deleteFile' "wrongInput"
deleteFile' "wrongInputSymL"
deleteDir' "alreadyExistsD"
deleteDir' "noPerms"
deleteDir' "noWritePerm"
deleteFile' "inputDir/foo/inputFile1"
deleteFile' "inputDir/inputFile2"
deleteFile' "inputDir/bar/inputFile3"
deleteDir' "inputDir/foo"
deleteDir' "inputDir/bar"
deleteDir' "inputDir"
spec :: Spec
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.copyDirRecursive" $ do
-- successes --
it "copyDirRecursive (Strict, CollectFailures), all fine and compare" $ do
tmpDir' <- getRawTmpDir
copyDirRecursive' "inputDir"
"outputDir"
Strict
CollectFailures
(system $ "diff -r --no-dereference "
++ toString tmpDir' ++ "inputDir" ++ " "
++ toString tmpDir' ++ "outputDir")
`shouldReturn` ExitSuccess
removeDirIfExists "outputDir"
-- posix failures --
it "copyDirRecursive (Strict, CollectFailures), source directory does not exist" $
copyDirRecursive' "doesNotExist"
"outputDir"
Strict
CollectFailures
`shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing)
it "copyDirRecursive (Strict, CollectFailures), cannot open source dir" $
copyDirRecursive' "noPerms/inputDir"
"foo"
Strict
CollectFailures
`shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied)
-- custom failures
it "copyDirRecursive (Overwrite, CollectFailures), various failures" $ do
copyDirRecursive' "inputDir1/foo2"
"outputDir1/foo2"
Overwrite
CollectFailures
`shouldThrow`
(\(RecursiveFailure ex@[_, _]) ->
any (\(h, e) -> ioeGetErrorType e == InappropriateType
&& isCopyFileFailed h) ex &&
any (\(h, e) -> ioeGetErrorType e == PermissionDenied
&& isReadContentsFailed h) ex)
normalDirPerms "outputDir1/foo2/foo4"
normalDirPerms "outputDir1/foo2/foo4/inputFile4"
c <- allDirectoryContents' "outputDir1"
tmpDir' <- getRawTmpDir
let shouldC = (fmap (\x -> tmpDir' `BS.append` x)
["outputDir1"
,"outputDir1/foo2"
,"outputDir1/foo2/inputFile1"
,"outputDir1/foo2/inputFile2"
,"outputDir1/foo2/inputFile3"
,"outputDir1/foo2/foo4"
,"outputDir1/foo2/foo4/inputFile6"
,"outputDir1/foo2/foo4/inputFile4"])
deleteFile' "outputDir1/foo2/inputFile1"
deleteFile' "outputDir1/foo2/inputFile2"
deleteFile' "outputDir1/foo2/inputFile3"
sort c `shouldBe` sort shouldC
it "copyDirRecursive (Strict, CollectFailures), no write permission on output dir" $
copyDirRecursive' "inputDir"
"noWritePerm/foo"
Strict
CollectFailures
`shouldThrow`
(\(RecursiveFailure [(CreateDirFailed{}, e)]) -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursive (Strict, CollectFailures), cannot open output dir" $
copyDirRecursive' "inputDir"
"noPerms/foo"
Strict
CollectFailures
`shouldThrow`
isRecursiveFailure
it "copyDirRecursive (Strict, CollectFailures), destination dir already exists" $
copyDirRecursive' "inputDir"
"alreadyExistsD"
Strict
CollectFailures
`shouldThrow`
(\(RecursiveFailure [(CreateDirFailed{}, e)]) -> ioeGetErrorType e == AlreadyExists)
it "copyDirRecursive (Strict, CollectFailures), destination already exists and is a file" $
copyDirRecursive' "inputDir"
"alreadyExists"
Strict
CollectFailures
`shouldThrow`
isRecursiveFailure
it "copyDirRecursive (Strict, CollectFailures), wrong input (regular file)" $
copyDirRecursive' "wrongInput"
"outputDir"
Strict
CollectFailures
`shouldThrow`
(\(RecursiveFailure [(ReadContentsFailed{}, e)]) -> ioeGetErrorType e == InappropriateType)
it "copyDirRecursive (Strict, CollectFailures), wrong input (symlink to directory)" $
copyDirRecursive' "wrongInputSymL"
"outputDir"
Strict
CollectFailures
`shouldThrow`
(\(RecursiveFailure [(ReadContentsFailed{}, e)]) -> ioeGetErrorType e == InvalidArgument)
it "copyDirRecursive (Strict, CollectFailures), destination in source" $
copyDirRecursive' "inputDir"
"inputDir/foo"
Strict
CollectFailures
`shouldThrow`
isDestinationInSource
it "copyDirRecursive (Strict, CollectFailures), destination and source same directory" $
copyDirRecursive' "inputDir"
"inputDir"
Strict
CollectFailures
`shouldThrow`
isSameFile

View File

@@ -5,6 +5,7 @@ module HPath.IO.CopyDirRecursiveOverwriteSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -17,11 +18,16 @@ import GHC.IO.Exception
import System.Exit import System.Exit
import System.Process import System.Process
import Utils import Utils
import qualified Data.ByteString as BS import Data.ByteString.UTF8 (toString)
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CopyDirRecursiveOverwriteSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
setupFiles = do setupFiles = do
createRegularFile' "alreadyExists" createRegularFile' "alreadyExists"
@@ -81,89 +87,116 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.copyDirRecursiveOverwrite" $ do describe "HPath.IO.copyDirRecursive" $ do
-- successes -- -- successes --
it "copyDirRecursiveOverwrite, all fine" $ do it "copyDirRecursive (Overwrite, FailEarly), all fine" $ do
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"outputDir" "outputDir"
Overwrite
FailEarly
removeDirIfExists "outputDir" removeDirIfExists "outputDir"
it "copyDirRecursiveOverwrite, all fine and compare" $ do it "copyDirRecursive (Overwrite, FailEarly), all fine and compare" $ do
copyDirRecursiveOverwrite' "inputDir" tmpDir' <- getRawTmpDir
"outputDir" copyDirRecursive' "inputDir"
"outputDir"
Overwrite
FailEarly
(system $ "diff -r --no-dereference " (system $ "diff -r --no-dereference "
++ toString tmpDir ++ "inputDir" ++ " " ++ toString tmpDir' ++ "inputDir" ++ " "
++ toString tmpDir ++ "outputDir") ++ toString tmpDir' ++ "outputDir")
`shouldReturn` ExitSuccess `shouldReturn` ExitSuccess
removeDirIfExists "outputDir" removeDirIfExists "outputDir"
it "copyDirRecursiveOverwrite, destination dir already exists" $ do it "copyDirRecursive (Overwrite, FailEarly), destination dir already exists" $ do
tmpDir' <- getRawTmpDir
(system $ "diff -r --no-dereference " (system $ "diff -r --no-dereference "
++ toString tmpDir ++ "inputDir" ++ " " ++ toString tmpDir' ++ "inputDir" ++ " "
++ toString tmpDir ++ "alreadyExistsD") ++ toString tmpDir' ++ "alreadyExistsD")
`shouldReturn` (ExitFailure 1) `shouldReturn` (ExitFailure 1)
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"alreadyExistsD" "alreadyExistsD"
Overwrite
FailEarly
(system $ "diff -r --no-dereference " (system $ "diff -r --no-dereference "
++ toString tmpDir ++ "inputDir" ++ " " ++ toString tmpDir' ++ "inputDir" ++ " "
++ toString tmpDir ++ "alreadyExistsD") ++ toString tmpDir' ++ "alreadyExistsD")
`shouldReturn` ExitSuccess `shouldReturn` ExitSuccess
removeDirIfExists "outputDir" removeDirIfExists "outputDir"
-- posix failures -- -- posix failures --
it "copyDirRecursiveOverwrite, source directory does not exist" $ it "copyDirRecursive, source directory does not exist" $
copyDirRecursiveOverwrite' "doesNotExist" copyDirRecursive' "doesNotExist"
"outputDir" "outputDir"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing) (\e -> ioeGetErrorType e == NoSuchThing)
it "copyDirRecursiveOverwrite, no write permission on output dir" $ it "copyDirRecursive, no write permission on output dir" $
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"noWritePerm/foo" "noWritePerm/foo"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursiveOverwrite, cannot open output dir" $ it "copyDirRecursive, cannot open output dir" $
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"noPerms/foo" "noPerms/foo"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursiveOverwrite, cannot open source dir" $ it "copyDirRecursive, cannot open source dir" $
copyDirRecursiveOverwrite' "noPerms/inputDir" copyDirRecursive' "noPerms/inputDir"
"foo" "foo"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursiveOverwrite, destination already exists and is a file" $ it "copyDirRecursive, destination already exists and is a file" $
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"alreadyExists" "alreadyExists"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InappropriateType) (\e -> ioeGetErrorType e == InappropriateType)
it "copyDirRecursiveOverwrite, wrong input (regular file)" $ it "copyDirRecursive, wrong input (regular file)" $
copyDirRecursiveOverwrite' "wrongInput" copyDirRecursive' "wrongInput"
"outputDir" "outputDir"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InappropriateType) (\e -> ioeGetErrorType e == InappropriateType)
it "copyDirRecursiveOverwrite, wrong input (symlink to directory)" $ it "copyDirRecursive, wrong input (symlink to directory)" $
copyDirRecursiveOverwrite' "wrongInputSymL" copyDirRecursive' "wrongInputSymL"
"outputDir" "outputDir"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument) (\e -> ioeGetErrorType e == InvalidArgument)
-- custom failures -- custom failures
it "copyDirRecursiveOverwrite, destination in source" $ it "copyDirRecursive (Overwrite, FailEarly), destination in source" $
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"inputDir/foo" "inputDir/foo"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
isDestinationInSource isDestinationInSource
it "copyDirRecursiveOverwrite, destination and source same directory" $ it "copyDirRecursive (Overwrite, FailEarly), destination and source same directory" $
copyDirRecursiveOverwrite' "inputDir" copyDirRecursive' "inputDir"
"inputDir" "inputDir"
Overwrite
FailEarly
`shouldThrow` `shouldThrow`
isSameFile isSameFile

View File

@@ -5,6 +5,7 @@ module HPath.IO.CopyDirRecursiveSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -17,12 +18,14 @@ import GHC.IO.Exception
import System.Exit import System.Exit
import System.Process import System.Process
import Utils import Utils
import qualified Data.ByteString as BS import Data.ByteString.UTF8 (toString)
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CopyDirRecursiveSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
setupFiles = do setupFiles = do
@@ -69,82 +72,109 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.copyDirRecursive" $ do describe "HPath.IO.copyDirRecursive" $ do
-- successes -- -- successes --
it "copyDirRecursive, all fine" $ do it "copyDirRecursive (Strict, FailEarly), all fine" $ do
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"outputDir" "outputDir"
Strict
FailEarly
removeDirIfExists "outputDir" removeDirIfExists "outputDir"
it "copyDirRecursive, all fine and compare" $ do it "copyDirRecursive (Strict, FailEarly), all fine and compare" $ do
tmpDir' <- getRawTmpDir
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"outputDir" "outputDir"
Strict
FailEarly
(system $ "diff -r --no-dereference " (system $ "diff -r --no-dereference "
++ toString tmpDir ++ "inputDir" ++ " " ++ toString tmpDir' ++ "inputDir" ++ " "
++ toString tmpDir ++ "outputDir") ++ toString tmpDir' ++ "outputDir")
`shouldReturn` ExitSuccess `shouldReturn` ExitSuccess
removeDirIfExists "outputDir" removeDirIfExists "outputDir"
-- posix failures -- -- posix failures --
it "copyDirRecursive, source directory does not exist" $ it "copyDirRecursive (Strict, FailEarly), source directory does not exist" $
copyDirRecursive' "doesNotExist" copyDirRecursive' "doesNotExist"
"outputDir" "outputDir"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing) (\e -> ioeGetErrorType e == NoSuchThing)
it "copyDirRecursive, no write permission on output dir" $ it "copyDirRecursive (Strict, FailEarly), no write permission on output dir" $
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"noWritePerm/foo" "noWritePerm/foo"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursive, cannot open output dir" $ it "copyDirRecursive (Strict, FailEarly), cannot open output dir" $
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"noPerms/foo" "noPerms/foo"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursive, cannot open source dir" $ it "copyDirRecursive (Strict, FailEarly), cannot open source dir" $
copyDirRecursive' "noPerms/inputDir" copyDirRecursive' "noPerms/inputDir"
"foo" "foo"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyDirRecursive, destination dir already exists" $ it "copyDirRecursive (Strict, FailEarly), destination dir already exists" $
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"alreadyExistsD" "alreadyExistsD"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists) (\e -> ioeGetErrorType e == AlreadyExists)
it "copyDirRecursive, destination already exists and is a file" $ it "copyDirRecursive (Strict, FailEarly), destination already exists and is a file" $
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"alreadyExists" "alreadyExists"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists) (\e -> ioeGetErrorType e == AlreadyExists)
it "copyDirRecursive, wrong input (regular file)" $ it "copyDirRecursive (Strict, FailEarly), wrong input (regular file)" $
copyDirRecursive' "wrongInput" copyDirRecursive' "wrongInput"
"outputDir" "outputDir"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InappropriateType) (\e -> ioeGetErrorType e == InappropriateType)
it "copyDirRecursive, wrong input (symlink to directory)" $ it "copyDirRecursive (Strict, FailEarly), wrong input (symlink to directory)" $
copyDirRecursive' "wrongInputSymL" copyDirRecursive' "wrongInputSymL"
"outputDir" "outputDir"
Strict
FailEarly
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument) (\e -> ioeGetErrorType e == InvalidArgument)
-- custom failures -- custom failures
it "copyDirRecursive, destination in source" $ it "copyDirRecursive (Strict, FailEarly), destination in source" $
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"inputDir/foo" "inputDir/foo"
Strict
FailEarly
`shouldThrow` `shouldThrow`
isDestinationInSource isDestinationInSource
it "copyDirRecursive, destination and source same directory" $ it "copyDirRecursive (Strict, FailEarly), destination and source same directory" $
copyDirRecursive' "inputDir" copyDirRecursive' "inputDir"
"inputDir" "inputDir"
Strict
FailEarly
`shouldThrow` `shouldThrow`
isSameFile isSameFile

View File

@@ -4,6 +4,7 @@ module HPath.IO.CopyFileOverwriteSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -16,8 +17,14 @@ import GHC.IO.Exception
import System.Exit import System.Exit
import System.Process import System.Process
import Utils import Utils
import qualified Data.ByteString as BS import Data.ByteString.UTF8 (toString)
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CopyFileOverwriteSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -51,79 +58,91 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.copyFileOverwrite" $ do describe "HPath.IO.copyFile" $ do
-- successes -- -- successes --
it "copyFileOverwrite, everything clear" $ do it "copyFile (Overwrite), everything clear" $ do
copyFileOverwrite' "inputFile" copyFile' "inputFile"
"outputFile" "outputFile"
Overwrite
removeFileIfExists "outputFile" removeFileIfExists "outputFile"
it "copyFileOverwrite, output file already exists, all clear" $ do it "copyFile (Overwrite), output file already exists, all clear" $ do
copyFile' "alreadyExists" "alreadyExists.bak" tmpDir' <- getRawTmpDir
copyFileOverwrite' "inputFile" copyFile' "alreadyExists" "alreadyExists.bak" Strict
"alreadyExists" copyFile' "inputFile" "alreadyExists" Overwrite
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " " (system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
++ toString tmpDir ++ "alreadyExists") ++ toString tmpDir' ++ "alreadyExists")
`shouldReturn` ExitSuccess `shouldReturn` ExitSuccess
removeFileIfExists "alreadyExists" removeFileIfExists "alreadyExists"
copyFile' "alreadyExists.bak" "alreadyExists" copyFile' "alreadyExists.bak" "alreadyExists" Strict
removeFileIfExists "alreadyExists.bak" removeFileIfExists "alreadyExists.bak"
it "copyFileOverwrite, and compare" $ do it "copyFile (Overwrite), and compare" $ do
copyFileOverwrite' "inputFile" tmpDir' <- getRawTmpDir
copyFile' "inputFile"
"outputFile" "outputFile"
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " " Overwrite
++ toString tmpDir ++ "outputFile") (system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
++ toString tmpDir' ++ "outputFile")
`shouldReturn` ExitSuccess `shouldReturn` ExitSuccess
removeFileIfExists "outputFile" removeFileIfExists "outputFile"
-- posix failures -- -- posix failures --
it "copyFileOverwrite, input file does not exist" $ it "copyFile (Overwrite), input file does not exist" $
copyFileOverwrite' "noSuchFile" copyFile' "noSuchFile"
"outputFile" "outputFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing) (\e -> ioeGetErrorType e == NoSuchThing)
it "copyFileOverwrite, no permission to write to output directory" $ it "copyFile (Overwrite), no permission to write to output directory" $
copyFileOverwrite' "inputFile" copyFile' "inputFile"
"outputDirNoWrite/outputFile" "outputDirNoWrite/outputFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyFileOverwrite, cannot open output directory" $ it "copyFile (Overwrite), cannot open output directory" $
copyFileOverwrite' "inputFile" copyFile' "inputFile"
"noPerms/outputFile" "noPerms/outputFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyFileOverwrite, cannot open source directory" $ it "copyFile (Overwrite), cannot open source directory" $
copyFileOverwrite' "noPerms/inputFile" copyFile' "noPerms/inputFile"
"outputFile" "outputFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyFileOverwrite, wrong input type (symlink)" $ it "copyFile (Overwrite), wrong input type (symlink)" $
copyFileOverwrite' "inputFileSymL" copyFile' "inputFileSymL"
"outputFile" "outputFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument) (\e -> ioeGetErrorType e == InvalidArgument)
it "copyFileOverwrite, wrong input type (directory)" $ it "copyFile (Overwrite), wrong input type (directory)" $
copyFileOverwrite' "wrongInput" copyFile' "wrongInput"
"outputFile" "outputFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InappropriateType) (\e -> ioeGetErrorType e == InappropriateType)
it "copyFileOverwrite, output file already exists and is a dir" $ it "copyFile (Overwrite), output file already exists and is a dir" $
copyFileOverwrite' "inputFile" copyFile' "inputFile"
"alreadyExistsD" "alreadyExistsD"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InappropriateType) (\e -> ioeGetErrorType e == InappropriateType)
-- custom failures -- -- custom failures --
it "copyFileOverwrite, output and input are same file" $ it "copyFile (Overwrite), output and input are same file" $
copyFileOverwrite' "inputFile" copyFile' "inputFile"
"inputFile" "inputFile"
Overwrite
`shouldThrow` isSameFile `shouldThrow` isSameFile

View File

@@ -5,6 +5,7 @@ module HPath.IO.CopyFileSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -17,10 +18,15 @@ import GHC.IO.Exception
import System.Exit import System.Exit
import System.Process import System.Process
import Utils import Utils
import qualified Data.ByteString as BS import Data.ByteString.UTF8 (toString)
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CopyFileSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
setupFiles = do setupFiles = do
createRegularFile' "inputFile" createRegularFile' "inputFile"
@@ -51,75 +57,87 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.copyFile" $ do describe "HPath.IO.copyFile" $ do
-- successes -- -- successes --
it "copyFile, everything clear" $ do it "copyFile (Strict), everything clear" $ do
copyFile' "inputFile" copyFile' "inputFile"
"outputFile" "outputFile"
Strict
removeFileIfExists "outputFile" removeFileIfExists "outputFile"
it "copyFile, and compare" $ do it "copyFile (Strict), and compare" $ do
tmpDir' <- getRawTmpDir
copyFile' "inputFile" copyFile' "inputFile"
"outputFile" "outputFile"
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " " Strict
++ toString tmpDir ++ "outputFile") (system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
++ toString tmpDir' ++ "outputFile")
`shouldReturn` ExitSuccess `shouldReturn` ExitSuccess
removeFileIfExists "outputFile" removeFileIfExists "outputFile"
-- posix failures -- -- posix failures --
it "copyFile, input file does not exist" $ it "copyFile (Strict), input file does not exist" $
copyFile' "noSuchFile" copyFile' "noSuchFile"
"outputFile" "outputFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing) (\e -> ioeGetErrorType e == NoSuchThing)
it "copyFile, no permission to write to output directory" $ it "copyFile (Strict), no permission to write to output directory" $
copyFile' "inputFile" copyFile' "inputFile"
"outputDirNoWrite/outputFile" "outputDirNoWrite/outputFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyFile, cannot open output directory" $ it "copyFile (Strict), cannot open output directory" $
copyFile' "inputFile" copyFile' "inputFile"
"noPerms/outputFile" "noPerms/outputFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyFile, cannot open source directory" $ it "copyFile (Strict), cannot open source directory" $
copyFile' "noPerms/inputFile" copyFile' "noPerms/inputFile"
"outputFile" "outputFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "copyFile, wrong input type (symlink)" $ it "copyFile (Strict), wrong input type (symlink)" $
copyFile' "inputFileSymL" copyFile' "inputFileSymL"
"outputFile" "outputFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument) (\e -> ioeGetErrorType e == InvalidArgument)
it "copyFile, wrong input type (directory)" $ it "copyFile (Strict), wrong input type (directory)" $
copyFile' "wrongInput" copyFile' "wrongInput"
"outputFile" "outputFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InappropriateType) (\e -> ioeGetErrorType e == InappropriateType)
it "copyFile, output file already exists" $ it "copyFile (Strict), output file already exists" $
copyFile' "inputFile" copyFile' "inputFile"
"alreadyExists" "alreadyExists"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists) (\e -> ioeGetErrorType e == AlreadyExists)
it "copyFile, output file already exists and is a dir" $ it "copyFile (Strict), output file already exists and is a dir" $
copyFile' "inputFile" copyFile' "inputFile"
"alreadyExistsD" "alreadyExistsD"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists) (\e -> ioeGetErrorType e == AlreadyExists)
-- custom failures -- -- custom failures --
it "copyFile, output and input are same file" $ it "copyFile (Strict), output and input are same file" $
copyFile' "inputFile" copyFile' "inputFile"
"inputFile" "inputFile"
Strict
`shouldThrow` `shouldThrow`
isSameFile isSameFile

View File

@@ -0,0 +1,78 @@
{-# LANGUAGE OverloadedStrings #-}
module HPath.IO.CreateDirRecursiveSpec where
import Test.Hspec
import System.IO.Error
(
ioeGetErrorType
)
import GHC.IO.Exception
(
IOErrorType(..)
)
import Utils
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CreateDirRecursiveSpec"
createTmpDir
setupFiles :: IO ()
setupFiles = do
createDir' "alreadyExists"
createRegularFile' "alreadyExistsF"
createDir' "noPerms"
createDir' "noWritePerms"
noPerms "noPerms"
noWritableDirPerms "noWritePerms"
cleanupFiles :: IO ()
cleanupFiles = do
normalDirPerms "noPerms"
normalDirPerms "noWritePerms"
deleteDir' "alreadyExists"
deleteDir' "noPerms"
deleteDir' "noWritePerms"
deleteFile' "alreadyExistsF"
spec :: Spec
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.createDirRecursive" $ do
-- successes --
it "createDirRecursive, all fine" $ do
createDirRecursive' "newDir"
deleteDir' "newDir"
it "createDirRecursive, parent directories do not exist" $ do
createDirRecursive' "some/thing/dada"
deleteDir' "some/thing/dada"
deleteDir' "some/thing"
deleteDir' "some"
it "createDirRecursive, destination directory already exists" $
createDirRecursive' "alreadyExists"
-- posix failures --
it "createDirRecursive, destination already exists and is a file" $
createDirRecursive' "alreadyExistsF"
`shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists)
it "createDirRecursive, can't write to output directory" $
createDirRecursive' "noWritePerms/newDir"
`shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied)
it "createDirRecursive, can't open output directory" $
createDirRecursive' "noPerms/newDir"
`shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied)

View File

@@ -13,10 +13,14 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CreateDirSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
setupFiles = do setupFiles = do
createDir' "alreadyExists" createDir' "alreadyExists"
@@ -37,7 +41,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.createDir" $ do describe "HPath.IO.createDir" $ do
-- successes -- -- successes --
@@ -46,6 +50,11 @@ spec = before_ setupFiles $ after_ cleanupFiles $
removeDirIfExists "newDir" removeDirIfExists "newDir"
-- posix failures -- -- posix failures --
it "createDir, parent directories do not exist" $
createDir' "some/thing/dada"
`shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing)
it "createDir, can't write to output directory" $ it "createDir, can't write to output directory" $
createDir' "noWritePerms/newDir" createDir' "noWritePerms/newDir"
`shouldThrow` `shouldThrow`

View File

@@ -13,10 +13,14 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CreateRegularFileSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
setupFiles = do setupFiles = do
createRegularFile' "alreadyExists" createRegularFile' "alreadyExists"
@@ -25,8 +29,6 @@ setupFiles = do
noPerms "noPerms" noPerms "noPerms"
noWritableDirPerms "noWritePerms" noWritableDirPerms "noWritePerms"
cleanupFiles :: IO () cleanupFiles :: IO ()
cleanupFiles = do cleanupFiles = do
normalDirPerms "noPerms" normalDirPerms "noPerms"
@@ -37,7 +39,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.createRegularFile" $ do describe "HPath.IO.createRegularFile" $ do
-- successes -- -- successes --
@@ -46,6 +48,11 @@ spec = before_ setupFiles $ after_ cleanupFiles $
removeFileIfExists "newDir" removeFileIfExists "newDir"
-- posix failures -- -- posix failures --
it "createRegularFile, parent directories do not exist" $
createRegularFile' "some/thing/dada"
`shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing)
it "createRegularFile, can't write to destination directory" $ it "createRegularFile, can't write to destination directory" $
createRegularFile' "noWritePerms/newDir" createRegularFile' "noWritePerms/newDir"
`shouldThrow` `shouldThrow`

View File

@@ -4,7 +4,6 @@ module HPath.IO.CreateSymlinkSpec where
import Test.Hspec import Test.Hspec
import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
ioeGetErrorType ioeGetErrorType
@@ -14,8 +13,12 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "CreateSymlinkSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -37,7 +40,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.createSymlink" $ do describe "HPath.IO.createSymlink" $ do
-- successes -- -- successes --
@@ -46,6 +49,11 @@ spec = before_ setupFiles $ after_ cleanupFiles $
removeFileIfExists "newSymL" removeFileIfExists "newSymL"
-- posix failures -- -- posix failures --
it "createSymlink, parent directories do not exist" $
createSymlink' "some/thing/dada" "lala"
`shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing)
it "createSymlink, can't write to destination directory" $ it "createSymlink, can't write to destination directory" $
createSymlink' "noWritePerms/newDir" "lala" createSymlink' "noWritePerms/newDir" "lala"
`shouldThrow` `shouldThrow`

View File

@@ -17,8 +17,13 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "DeleteDirRecursiveSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -46,7 +51,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.deleteDirRecursive" $ do describe "HPath.IO.deleteDirRecursive" $ do
-- successes -- -- successes --

View File

@@ -17,8 +17,14 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "DeleteDirSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -46,7 +52,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.deleteDir" $ do describe "HPath.IO.deleteDir" $ do
-- successes -- -- successes --

View File

@@ -4,6 +4,7 @@ module HPath.IO.DeleteFileSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import System.IO.Error import System.IO.Error
( (
ioeGetErrorType ioeGetErrorType
@@ -17,8 +18,12 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "DeleteFileSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -41,7 +46,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.deleteFile" $ do describe "HPath.IO.deleteFile" $ do
-- successes -- -- successes --
@@ -55,6 +60,7 @@ spec = before_ setupFiles $ after_ cleanupFiles $
it "deleteFile, symlink, all fine" $ do it "deleteFile, symlink, all fine" $ do
recreateSymlink' "syml" recreateSymlink' "syml"
"testFile" "testFile"
Strict
deleteFile' "testFile" deleteFile' "testFile"
getSymbolicLinkStatus "testFile" getSymbolicLinkStatus "testFile"
`shouldThrow` `shouldThrow`

View File

@@ -3,18 +3,10 @@
module HPath.IO.GetDirsFilesSpec where module HPath.IO.GetDirsFilesSpec where
import Control.Applicative
(
(<$>)
)
import Data.List import Data.List
( (
sort sort
) )
import Data.Maybe
(
fromJust
)
import qualified HPath as P import qualified HPath as P
import HPath.IO import HPath.IO
import Test.Hspec import Test.Hspec
@@ -22,17 +14,17 @@ import System.IO.Error
( (
ioeGetErrorType ioeGetErrorType
) )
import System.Posix.Env.ByteString
(
getEnv
)
import GHC.IO.Exception import GHC.IO.Exception
( (
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "GetDirsFilesSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -61,7 +53,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.getDirsFiles" $ do describe "HPath.IO.getDirsFiles" $ do
-- successes -- -- successes --

View File

@@ -14,8 +14,13 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "GetFileTypeSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -42,7 +47,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.getFileType" $ do describe "HPath.IO.getFileType" $ do
-- successes -- -- successes --

View File

@@ -4,6 +4,7 @@ module HPath.IO.MoveFileOverwriteSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -14,8 +15,13 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "MoveFileOverwriteSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -45,65 +51,76 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.moveFileOverwrite" $ do describe "HPath.IO.moveFile" $ do
-- successes -- -- successes --
it "moveFileOverwrite, all fine" $ it "moveFile (Overwrite), all fine" $
moveFileOverwrite' "myFile" moveFile' "myFile"
"movedFile" "movedFile"
Overwrite
it "moveFileOverwrite, all fine" $ it "moveFile (Overwrite), all fine" $
moveFileOverwrite' "myFile" moveFile' "myFile"
"dir/movedFile" "dir/movedFile"
Overwrite
it "moveFileOverwrite, all fine on symlink" $ it "moveFile (Overwrite), all fine on symlink" $
moveFileOverwrite' "myFileL" moveFile' "myFileL"
"movedFile" "movedFile"
Overwrite
it "moveFileOverwrite, all fine on directory" $ it "moveFile (Overwrite), all fine on directory" $
moveFileOverwrite' "dir" moveFile' "dir"
"movedFile" "movedFile"
Overwrite
it "moveFileOverwrite, destination file already exists" $ it "moveFile (Overwrite), destination file already exists" $
moveFileOverwrite' "myFile" moveFile' "myFile"
"alreadyExists" "alreadyExists"
Overwrite
-- posix failures -- -- posix failures --
it "moveFileOverwrite, source file does not exist" $ it "moveFile (Overwrite), source file does not exist" $
moveFileOverwrite' "fileDoesNotExist" moveFile' "fileDoesNotExist"
"movedFile" "movedFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing) (\e -> ioeGetErrorType e == NoSuchThing)
it "moveFileOverwrite, can't write to destination directory" $ it "moveFile (Overwrite), can't write to destination directory" $
moveFileOverwrite' "myFile" moveFile' "myFile"
"noWritePerm/movedFile" "noWritePerm/movedFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "moveFileOverwrite, can't open destination directory" $ it "moveFile (Overwrite), can't open destination directory" $
moveFileOverwrite' "myFile" moveFile' "myFile"
"noPerms/movedFile" "noPerms/movedFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "moveFileOverwrite, can't open source directory" $ it "moveFile (Overwrite), can't open source directory" $
moveFileOverwrite' "noPerms/myFile" moveFile' "noPerms/myFile"
"movedFile" "movedFile"
Overwrite
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
-- custom failures -- -- custom failures --
it "moveFileOverwrite, move from file to dir" $
moveFileOverwrite' "myFile"
"alreadyExistsD"
`shouldThrow`
isDirDoesExist
it "moveFileOverwrite, source and dest are same file" $ it "moveFile (Overwrite), move from file to dir" $
moveFileOverwrite' "myFile" moveFile' "myFile"
"myFile" "alreadyExistsD"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists)
it "moveFile (Overwrite), source and dest are same file" $
moveFile' "myFile"
"myFile"
Overwrite
`shouldThrow` `shouldThrow`
isSameFile isSameFile

View File

@@ -4,6 +4,7 @@ module HPath.IO.MoveFileSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -14,8 +15,13 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "MoveFileSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -47,67 +53,77 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.moveFile" $ do describe "HPath.IO.moveFile" $ do
-- successes -- -- successes --
it "moveFile, all fine" $ it "moveFile (Strict), all fine" $
moveFile' "myFile" moveFile' "myFile"
"movedFile" "movedFile"
Strict
it "moveFile, all fine" $ it "moveFile (Strict), all fine" $
moveFile' "myFile" moveFile' "myFile"
"dir/movedFile" "dir/movedFile"
Strict
it "moveFile, all fine on symlink" $ it "moveFile (Strict), all fine on symlink" $
moveFile' "myFileL" moveFile' "myFileL"
"movedFile" "movedFile"
Strict
it "moveFile, all fine on directory" $ it "moveFile (Strict), all fine on directory" $
moveFile' "dir" moveFile' "dir"
"movedFile" "movedFile"
Strict
-- posix failures -- -- posix failures --
it "moveFile, source file does not exist" $ it "moveFile (Strict), source file does not exist" $
moveFile' "fileDoesNotExist" moveFile' "fileDoesNotExist"
"movedFile" "movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == NoSuchThing) (\e -> ioeGetErrorType e == NoSuchThing)
it "moveFile, can't write to destination directory" $ it "moveFile (Strict), can't write to destination directory" $
moveFile' "myFile" moveFile' "myFile"
"noWritePerm/movedFile" "noWritePerm/movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "moveFile, can't open destination directory" $ it "moveFile (Strict), can't open destination directory" $
moveFile' "myFile" moveFile' "myFile"
"noPerms/movedFile" "noPerms/movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "moveFile, can't open source directory" $ it "moveFile (Strict), can't open source directory" $
moveFile' "noPerms/myFile" moveFile' "noPerms/myFile"
"movedFile" "movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
-- custom failures -- -- custom failures --
it "moveFile, destination file already exists" $ it "moveFile (Strict), destination file already exists" $
moveFile' "myFile" moveFile' "myFile"
"alreadyExists" "alreadyExists"
Strict
`shouldThrow` `shouldThrow`
isFileDoesExist (\e -> ioeGetErrorType e == AlreadyExists)
it "moveFile, move from file to dir" $ it "moveFile (Strict), move from file to dir" $
moveFile' "myFile" moveFile' "myFile"
"alreadyExistsD" "alreadyExistsD"
Strict
`shouldThrow` `shouldThrow`
isDirDoesExist (\e -> ioeGetErrorType e == AlreadyExists)
it "moveFile, source and dest are same file" $ it "moveFile (Strict), source and dest are same file" $
moveFile' "myFile" moveFile' "myFile"
"myFile" "myFile"
Strict
`shouldThrow` `shouldThrow`
isSameFile isSameFile

View File

@@ -0,0 +1,139 @@
{-# LANGUAGE OverloadedStrings #-}
module HPath.IO.RecreateSymlinkOverwriteSpec where
-- TODO: exception if destination exists but is not a file + `OverWrite` CopyMode
import Test.Hspec
import HPath.IO
import HPath.IO.Errors
import System.IO.Error
(
ioeGetErrorType
)
import GHC.IO.Exception
(
IOErrorType(..)
)
import Utils
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "RecreateSymlinkOverwriteSpec"
createTmpDir
setupFiles :: IO ()
setupFiles = do
createRegularFile' "myFile"
createSymlink' "myFileL" "myFile"
createRegularFile' "alreadyExists"
createDir' "alreadyExistsD"
createDir' "dir"
createDir' "noPerms"
createDir' "noWritePerm"
createDir' "alreadyExistsD2"
createRegularFile' "alreadyExistsD2/lala"
noPerms "noPerms"
noWritableDirPerms "noWritePerm"
writeFile' "myFile" "Blahfaselgagaga"
cleanupFiles :: IO ()
cleanupFiles = do
normalDirPerms "noPerms"
normalDirPerms "noWritePerm"
deleteFile' "myFile"
deleteFile' "myFileL"
deleteFile' "alreadyExists"
deleteFile' "alreadyExistsD2/lala"
deleteDir' "alreadyExistsD"
deleteDir' "alreadyExistsD2"
deleteDir' "dir"
deleteDir' "noPerms"
deleteDir' "noWritePerm"
spec :: Spec
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.recreateSymlink" $ do
-- successes --
it "recreateSymLink (Overwrite), all fine" $ do
recreateSymlink' "myFileL"
"movedFile"
Overwrite
removeFileIfExists "movedFile"
it "recreateSymLink (Overwrite), all fine" $ do
recreateSymlink' "myFileL"
"dir/movedFile"
Overwrite
removeFileIfExists "dir/movedFile"
it "recreateSymLink (Overwrite), destination file already exists" $
recreateSymlink' "myFileL"
"alreadyExists"
Overwrite
it "recreateSymLink (Overwrite), destination already exists and is an empty dir" $ do
recreateSymlink' "myFileL"
"alreadyExistsD"
Overwrite
deleteFile' "alreadyExistsD"
createDir' "alreadyExistsD"
-- posix failures --
it "recreateSymLink (Overwrite), destination already exists and is a non-empty dir" $
recreateSymlink' "myFileL"
"alreadyExistsD2"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == UnsatisfiedConstraints)
it "recreateSymLink (Overwrite), wrong input type (file)" $
recreateSymlink' "myFile"
"movedFile"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument)
it "recreateSymLink (Overwrite), wrong input type (directory)" $
recreateSymlink' "dir"
"movedFile"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument)
it "recreateSymLink (Overwrite), can't write to destination directory" $
recreateSymlink' "myFileL"
"noWritePerm/movedFile"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied)
it "recreateSymLink (Overwrite), can't open destination directory" $
recreateSymlink' "myFileL"
"noPerms/movedFile"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied)
it "recreateSymLink (Overwrite), can't open source directory" $
recreateSymlink' "noPerms/myFileL"
"movedFile"
Overwrite
`shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied)
-- custom failures --
it "recreateSymLink (Overwrite), source and destination are the same file" $
recreateSymlink' "myFileL"
"myFileL"
Overwrite
`shouldThrow`
isSameFile

View File

@@ -3,7 +3,10 @@
module HPath.IO.RecreateSymlinkSpec where module HPath.IO.RecreateSymlinkSpec where
import Test.Hspec import Test.Hspec
import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import System.IO.Error import System.IO.Error
( (
@@ -14,8 +17,13 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "RecreateSymlinkSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -46,67 +54,77 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.recreateSymlink" $ do describe "HPath.IO.recreateSymlink" $ do
-- successes -- -- successes --
it "recreateSymLink, all fine" $ do it "recreateSymLink (Strict), all fine" $ do
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"movedFile" "movedFile"
Strict
removeFileIfExists "movedFile" removeFileIfExists "movedFile"
it "recreateSymLink, all fine" $ do it "recreateSymLink (Strict), all fine" $ do
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"dir/movedFile" "dir/movedFile"
Strict
removeFileIfExists "dir/movedFile" removeFileIfExists "dir/movedFile"
-- posix failures -- -- posix failures --
it "recreateSymLink, wrong input type (file)" $ it "recreateSymLink (Strict), wrong input type (file)" $
recreateSymlink' "myFile" recreateSymlink' "myFile"
"movedFile" "movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument) (\e -> ioeGetErrorType e == InvalidArgument)
it "recreateSymLink, wrong input type (directory)" $ it "recreateSymLink (Strict), wrong input type (directory)" $
recreateSymlink' "dir" recreateSymlink' "dir"
"movedFile" "movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == InvalidArgument) (\e -> ioeGetErrorType e == InvalidArgument)
it "recreateSymLink, can't write to destination directory" $ it "recreateSymLink (Strict), can't write to destination directory" $
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"noWritePerm/movedFile" "noWritePerm/movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "recreateSymLink, can't open destination directory" $ it "recreateSymLink (Strict), can't open destination directory" $
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"noPerms/movedFile" "noPerms/movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "recreateSymLink, can't open source directory" $ it "recreateSymLink (Strict), can't open source directory" $
recreateSymlink' "noPerms/myFileL" recreateSymlink' "noPerms/myFileL"
"movedFile" "movedFile"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == PermissionDenied) (\e -> ioeGetErrorType e == PermissionDenied)
it "recreateSymLink, destination file already exists" $ it "recreateSymLink (Strict), destination file already exists" $
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"alreadyExists" "alreadyExists"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists) (\e -> ioeGetErrorType e == AlreadyExists)
it "recreateSymLink, destination already exists and is a dir" $ it "recreateSymLink (Strict), destination already exists and is a dir" $
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"alreadyExistsD" "alreadyExistsD"
Strict
`shouldThrow` `shouldThrow`
(\e -> ioeGetErrorType e == AlreadyExists) (\e -> ioeGetErrorType e == AlreadyExists)
-- custom failures -- -- custom failures --
it "recreateSymLink, source and destination are the same file" $ it "recreateSymLink (Strict), source and destination are the same file" $
recreateSymlink' "myFileL" recreateSymlink' "myFileL"
"myFileL" "myFileL"
Strict
`shouldThrow` `shouldThrow`
isSameFile isSameFile

View File

@@ -14,8 +14,13 @@ import GHC.IO.Exception
IOErrorType(..) IOErrorType(..)
) )
import Utils import Utils
import qualified Data.ByteString as BS
import Data.ByteString.UTF8 (toString)
upTmpDir :: IO ()
upTmpDir = do
setTmpDir "RenameFileSpec"
createTmpDir
setupFiles :: IO () setupFiles :: IO ()
@@ -46,7 +51,7 @@ cleanupFiles = do
spec :: Spec spec :: Spec
spec = before_ setupFiles $ after_ cleanupFiles $ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
describe "HPath.IO.renameFile" $ do describe "HPath.IO.renameFile" $ do
-- successes -- -- successes --
@@ -96,13 +101,13 @@ spec = before_ setupFiles $ after_ cleanupFiles $
renameFile' "myFile" renameFile' "myFile"
"alreadyExists" "alreadyExists"
`shouldThrow` `shouldThrow`
isFileDoesExist (\e -> ioeGetErrorType e == AlreadyExists)
it "renameFile, move from file to dir" $ it "renameFile, move from file to dir" $
renameFile' "myFile" renameFile' "myFile"
"alreadyExistsD" "alreadyExistsD"
`shouldThrow` `shouldThrow`
isDirDoesExist (\e -> ioeGetErrorType e == AlreadyExists)
it "renameFile, source and dest are same file" $ it "renameFile, source and dest are same file" $
renameFile' "myFile" renameFile' "myFile"

View File

@@ -14,10 +14,6 @@ main :: IO ()
main = main =
hspecWith hspecWith
defaultConfig { configFormatter = Just progress } defaultConfig { configFormatter = Just progress }
$ before_ up $ beforeAll_ createBaseTmpDir
$ after_ down $ afterAll_ deleteBaseTmpDir
$ Spec.spec $ Spec.spec
where
up = createTmpDir
down = deleteTmpDir

View File

@@ -11,16 +11,33 @@ import Control.Applicative
) )
import Control.Monad import Control.Monad
( (
void forM_
, void
)
import Control.Monad.IfElse
(
whenM
)
import qualified Data.ByteString as BS
import Data.IORef
(
newIORef
, readIORef
, writeIORef
, IORef
) )
import HPath.IO import HPath.IO
import HPath.IO.Errors import HPath.IO.Errors
import HPath.IO.Utils
import Data.Maybe import Data.Maybe
( (
fromJust fromJust
) )
import qualified HPath as P import qualified HPath as P
import System.IO.Unsafe
(
unsafePerformIO
)
import qualified System.Posix.Directory.Traversals as DT
import System.Posix.Env.ByteString import System.Posix.Env.ByteString
( (
getEnv getEnv
@@ -47,8 +64,13 @@ import qualified "unix-bytestring" System.Posix.IO.ByteString as SPB
tmpDir :: ByteString baseTmpDir :: ByteString
tmpDir = "test/HPath/IO/tmp/" baseTmpDir = "test/HPath/IO/tmp/"
tmpDir :: IORef ByteString
{-# NOINLINE tmpDir #-}
tmpDir = unsafePerformIO (newIORef baseTmpDir)
@@ -57,31 +79,63 @@ tmpDir = "test/HPath/IO/tmp/"
----------------- -----------------
setTmpDir :: ByteString -> IO ()
{-# NOINLINE setTmpDir #-}
setTmpDir bs = writeIORef tmpDir (baseTmpDir `BS.append` bs)
createTmpDir :: IO () createTmpDir :: IO ()
{-# NOINLINE createTmpDir #-}
createTmpDir = do createTmpDir = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel tmpDir tmp <- P.parseRel =<< readIORef tmpDir
void $ createDir (pwd P.</> tmp) void $ createDir newDirPerms (pwd P.</> tmp)
deleteTmpDir :: IO () deleteTmpDir :: IO ()
{-# NOINLINE deleteTmpDir #-}
deleteTmpDir = do deleteTmpDir = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel tmpDir tmp <- P.parseRel =<< readIORef tmpDir
void $ deleteDir (pwd P.</> tmp)
createBaseTmpDir :: IO ()
{-# NOINLINE createBaseTmpDir #-}
createBaseTmpDir = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel baseTmpDir
void $ createDir newDirPerms (pwd P.</> tmp)
deleteBaseTmpDir :: IO ()
{-# NOINLINE deleteBaseTmpDir #-}
deleteBaseTmpDir = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel baseTmpDir
contents <- getDirsFiles (pwd P.</> tmp)
forM_ contents deleteDir
void $ deleteDir (pwd P.</> tmp) void $ deleteDir (pwd P.</> tmp)
withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a
{-# NOINLINE withRawTmpDir #-}
withRawTmpDir f = do withRawTmpDir f = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel tmpDir tmp <- P.parseRel =<< readIORef tmpDir
f (pwd P.</> tmp) f (pwd P.</> tmp)
getRawTmpDir :: IO ByteString
{-# NOINLINE getRawTmpDir #-}
getRawTmpDir = withRawTmpDir (return . flip BS.append "/" . P.fromAbs)
withTmpDir :: ByteString -> (P.Path P.Abs -> IO a) -> IO a withTmpDir :: ByteString -> (P.Path P.Abs -> IO a) -> IO a
{-# NOINLINE withTmpDir #-}
withTmpDir ip f = do withTmpDir ip f = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel tmpDir tmp <- P.parseRel =<< readIORef tmpDir
p <- (pwd P.</> tmp P.</>) <$> P.parseRel ip p <- (pwd P.</> tmp P.</>) <$> P.parseRel ip
f p f p
@@ -90,84 +144,83 @@ withTmpDir' :: ByteString
-> ByteString -> ByteString
-> (P.Path P.Abs -> P.Path P.Abs -> IO a) -> (P.Path P.Abs -> P.Path P.Abs -> IO a)
-> IO a -> IO a
{-# NOINLINE withTmpDir' #-}
withTmpDir' ip1 ip2 f = do withTmpDir' ip1 ip2 f = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
tmp <- P.parseRel tmpDir tmp <- P.parseRel =<< readIORef tmpDir
p1 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip1 p1 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip1
p2 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip2 p2 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip2
f p1 p2 f p1 p2
removeFileIfExists :: ByteString -> IO () removeFileIfExists :: ByteString -> IO ()
{-# NOINLINE removeFileIfExists #-}
removeFileIfExists bs = removeFileIfExists bs =
withTmpDir bs $ \p -> whenM (doesFileExist p) (deleteFile p) withTmpDir bs $ \p -> whenM (doesFileExist p) (deleteFile p)
removeDirIfExists :: ByteString -> IO () removeDirIfExists :: ByteString -> IO ()
{-# NOINLINE removeDirIfExists #-}
removeDirIfExists bs = removeDirIfExists bs =
withTmpDir bs $ \p -> whenM (doesDirectoryExist p) (deleteDirRecursive p) withTmpDir bs $ \p -> whenM (doesDirectoryExist p) (deleteDirRecursive p)
copyFile' :: ByteString -> ByteString -> IO () copyFile' :: ByteString -> ByteString -> CopyMode -> IO ()
copyFile' inputFileP outputFileP = {-# NOINLINE copyFile' #-}
withTmpDir' inputFileP outputFileP copyFile copyFile' inputFileP outputFileP cm =
withTmpDir' inputFileP outputFileP (\p1 p2 -> copyFile p1 p2 cm)
copyFileOverwrite' :: ByteString -> ByteString -> IO () copyDirRecursive' :: ByteString -> ByteString
copyFileOverwrite' inputFileP outputFileP = -> CopyMode -> RecursiveErrorMode -> IO ()
withTmpDir' inputFileP outputFileP copyFileOverwrite {-# NOINLINE copyDirRecursive' #-}
copyDirRecursive' inputDirP outputDirP cm rm =
withTmpDir' inputDirP outputDirP (\p1 p2 -> copyDirRecursive p1 p2 cm rm)
copyDirRecursive' :: ByteString -> ByteString -> IO ()
copyDirRecursive' inputDirP outputDirP =
withTmpDir' inputDirP outputDirP copyDirRecursive
copyDirRecursiveOverwrite' :: ByteString -> ByteString -> IO ()
copyDirRecursiveOverwrite' inputDirP outputDirP =
withTmpDir' inputDirP outputDirP copyDirRecursiveOverwrite
createDir' :: ByteString -> IO () createDir' :: ByteString -> IO ()
createDir' dest = withTmpDir dest createDir {-# NOINLINE createDir' #-}
createDir' dest = withTmpDir dest (createDir newDirPerms)
createDirRecursive' :: ByteString -> IO ()
{-# NOINLINE createDirRecursive' #-}
createDirRecursive' dest = withTmpDir dest (createDirRecursive newDirPerms)
createRegularFile' :: ByteString -> IO () createRegularFile' :: ByteString -> IO ()
createRegularFile' dest = withTmpDir dest createRegularFile {-# NOINLINE createRegularFile' #-}
createRegularFile' dest = withTmpDir dest (createRegularFile newFilePerms)
createSymlink' :: ByteString -> ByteString -> IO () createSymlink' :: ByteString -> ByteString -> IO ()
{-# NOINLINE createSymlink' #-}
createSymlink' dest sympoint = withTmpDir dest createSymlink' dest sympoint = withTmpDir dest
(\x -> createSymlink x sympoint) (\x -> createSymlink x sympoint)
renameFile' :: ByteString -> ByteString -> IO () renameFile' :: ByteString -> ByteString -> IO ()
{-# NOINLINE renameFile' #-}
renameFile' inputFileP outputFileP = renameFile' inputFileP outputFileP =
withTmpDir' inputFileP outputFileP $ \i o -> do withTmpDir' inputFileP outputFileP $ \i o -> do
renameFile i o renameFile i o
renameFile o i renameFile o i
moveFile' :: ByteString -> ByteString -> IO () moveFile' :: ByteString -> ByteString -> CopyMode -> IO ()
moveFile' inputFileP outputFileP = {-# NOINLINE moveFile' #-}
moveFile' inputFileP outputFileP cm =
withTmpDir' inputFileP outputFileP $ \i o -> do withTmpDir' inputFileP outputFileP $ \i o -> do
moveFile i o moveFile i o cm
moveFile o i moveFile o i Strict
moveFileOverwrite' :: ByteString -> ByteString -> IO () recreateSymlink' :: ByteString -> ByteString -> CopyMode -> IO ()
moveFileOverwrite' inputFileP outputFileP = {-# NOINLINE recreateSymlink' #-}
withTmpDir' inputFileP outputFileP $ \i o -> do recreateSymlink' inputFileP outputFileP cm =
moveFileOverwrite i o withTmpDir' inputFileP outputFileP (\p1 p2 -> recreateSymlink p1 p2 cm)
moveFile o i
recreateSymlink' :: ByteString -> ByteString -> IO ()
recreateSymlink' inputFileP outputFileP =
withTmpDir' inputFileP outputFileP recreateSymlink
noWritableDirPerms :: ByteString -> IO () noWritableDirPerms :: ByteString -> IO ()
{-# NOINLINE noWritableDirPerms #-}
noWritableDirPerms path = withTmpDir path $ \p -> noWritableDirPerms path = withTmpDir path $ \p ->
setFileMode (P.fromAbs p) perms setFileMode (P.fromAbs p) perms
where where
@@ -180,42 +233,58 @@ noWritableDirPerms path = withTmpDir path $ \p ->
noPerms :: ByteString -> IO () noPerms :: ByteString -> IO ()
{-# NOINLINE noPerms #-}
noPerms path = withTmpDir path $ \p -> setFileMode (P.fromAbs p) nullFileMode noPerms path = withTmpDir path $ \p -> setFileMode (P.fromAbs p) nullFileMode
normalDirPerms :: ByteString -> IO () normalDirPerms :: ByteString -> IO ()
{-# NOINLINE normalDirPerms #-}
normalDirPerms path = normalDirPerms path =
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms
getFileType' :: ByteString -> IO FileType getFileType' :: ByteString -> IO FileType
{-# NOINLINE getFileType' #-}
getFileType' path = withTmpDir path getFileType getFileType' path = withTmpDir path getFileType
getDirsFiles' :: ByteString -> IO [P.Path P.Abs] getDirsFiles' :: ByteString -> IO [P.Path P.Abs]
{-# NOINLINE getDirsFiles' #-}
getDirsFiles' path = withTmpDir path getDirsFiles getDirsFiles' path = withTmpDir path getDirsFiles
deleteFile' :: ByteString -> IO () deleteFile' :: ByteString -> IO ()
{-# NOINLINE deleteFile' #-}
deleteFile' p = withTmpDir p deleteFile deleteFile' p = withTmpDir p deleteFile
deleteDir' :: ByteString -> IO () deleteDir' :: ByteString -> IO ()
{-# NOINLINE deleteDir' #-}
deleteDir' p = withTmpDir p deleteDir deleteDir' p = withTmpDir p deleteDir
deleteDirRecursive' :: ByteString -> IO () deleteDirRecursive' :: ByteString -> IO ()
{-# NOINLINE deleteDirRecursive' #-}
deleteDirRecursive' p = withTmpDir p deleteDirRecursive deleteDirRecursive' p = withTmpDir p deleteDirRecursive
canonicalizePath' :: ByteString -> IO (P.Path P.Abs) canonicalizePath' :: ByteString -> IO (P.Path P.Abs)
{-# NOINLINE canonicalizePath' #-}
canonicalizePath' p = withTmpDir p canonicalizePath canonicalizePath' p = withTmpDir p canonicalizePath
writeFile' :: ByteString -> ByteString -> IO () writeFile' :: ByteString -> ByteString -> IO ()
{-# NOINLINE writeFile' #-}
writeFile' ip bs = writeFile' ip bs =
withTmpDir ip $ \p -> do withTmpDir ip $ \p -> do
fd <- SPI.openFd (P.fromAbs p) SPI.WriteOnly Nothing fd <- SPI.openFd (P.fromAbs p) SPI.WriteOnly Nothing
SPI.defaultFileFlags SPI.defaultFileFlags
SPB.fdWrite fd bs _ <- SPB.fdWrite fd bs
SPI.closeFd fd SPI.closeFd fd
allDirectoryContents' :: ByteString -> IO [ByteString]
{-# NOINLINE allDirectoryContents' #-}
allDirectoryContents' ip =
withTmpDir ip $ \p -> DT.allDirectoryContents' (P.fromAbs p)