packages feed

path-io 1.8.0 → 1.8.1

raw patch · 4 files changed

+100/−81 lines, 4 filesdep ~basedep ~dlistdep ~hspec

Dependency ranges changed: base, dlist, hspec

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ *Path IO follows [SemVer](https://semver.org/).* +## Path IO 1.8.1++* Fixed a “permission denied” error occurring when `copyDirRecur` is used to+  copy a tree with non-empty read-only directories. [PR+  82](https://github.com/mrkkrp/path-io/pull/82).+ ## Path IO 1.8.0  * Added instances of `AnyPath` for `SomeBase`. [PR
Path/IO.hs view
@@ -175,14 +175,14 @@ -- * 'InappropriateType' -- The path refers to an existing non-directory object. -- @[EEXIST]@-createDir :: MonadIO m => Path b Dir -> m ()+createDir :: (MonadIO m) => Path b Dir -> m () createDir = liftD D.createDirectory  -- | @'createDirIfMissing' parents dir@ creates a new directory @dir@ if it -- doesn't exist. If the first argument is 'True' the function will also -- create all parent directories if they are missing. createDirIfMissing ::-  MonadIO m =>+  (MonadIO m) =>   -- | Create its parents too?   Bool ->   -- | The path to the directory you want to make@@ -196,7 +196,7 @@ -- > ensureDir = createDirIfMissing True -- -- @since 0.3.1-ensureDir :: MonadIO m => Path b Dir -> m ()+ensureDir :: (MonadIO m) => Path b Dir -> m () ensureDir = createDirIfMissing True  -- | @'removeDir' dir@ removes an existing directory @dir@. The@@ -236,13 +236,13 @@ -- * 'InappropriateType' -- The operand refers to an existing non-directory object. -- @[ENOTDIR]@-removeDir :: MonadIO m => Path b Dir -> m ()+removeDir :: (MonadIO m) => Path b Dir -> m () removeDir = liftD D.removeDirectory  -- | @'removeDirRecur' dir@ removes an existing directory @dir@ together -- with its contents and sub-directories. Within this directory, symbolic -- links are removed without affecting their targets.-removeDirRecur :: MonadIO m => Path b Dir -> m ()+removeDirRecur :: (MonadIO m) => Path b Dir -> m () removeDirRecur = liftD D.removeDirectoryRecursive  -- | Remove a file or directory at /path/ together with its contents and@@ -263,7 +263,7 @@ -- exception.  The first exception that it encountered is re-thrown. -- -- @since 1.7.0-removePathForcibly :: MonadIO m => Path b t -> m ()+removePathForcibly :: (MonadIO m) => Path b t -> m () removePathForcibly = liftD D.removePathForcibly  -- | @'renameDir' old new@ changes the name of an existing directory from@@ -312,7 +312,7 @@ --  Either path refers to an existing non-directory object. --  @[ENOTDIR, EISDIR]@ renameDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Old name   Path b0 Dir ->   -- | New name@@ -362,7 +362,7 @@ -- @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@ -- -- @since 1.7.0-renamePath :: MonadIO m => Path b0 t -> Path b1 t -> m ()+renamePath :: (MonadIO m) => Path b0 t -> Path b1 t -> m () renamePath = liftD2 D.renamePath  -- | @'listDir' dir@ returns a list of /all/ entries in @dir@ without the@@ -394,7 +394,7 @@ --   The path refers to an existing non-directory object. --   @[ENOTDIR]@ listDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to list   Path b Dir ->   -- | Sub-directories and files@@ -411,7 +411,7 @@ -- -- @since 1.4.0 listDirRel ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to list   Path b Dir ->   -- | Sub-directories and files@@ -431,7 +431,7 @@ -- -- __Note__: before version /1.3.0/, this function followed symlinks. listDirRecur ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to list   Path b Dir ->   -- | Sub-directories and files@@ -453,7 +453,7 @@ -- -- @since 1.4.2 listDirRecurRel ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to list   Path b Dir ->   -- | Sub-directories and files@@ -524,7 +524,7 @@ -- to preserve directory permissions or not. /Does not/ follow symbolic -- links. Internal function. copyDirRecurGen ::-  MonadIO m =>+  (MonadIO m) =>   -- | Should we preserve directory permissions?   Bool ->   -- | Source@@ -545,7 +545,7 @@         (new </>)           <$> stripProperPrefix old path   ensureDir bdest-  forM_ dirs $ \srcDir -> do+  copyPermissionsIOs <- forM dirs $ \srcDir -> do     destDir <- swapParent bsrc bdest srcDir     dirIsSymlink <- isSymlink srcDir     if dirIsSymlink@@ -554,8 +554,7 @@         D.createDirectoryLink target $           toFilePath' destDir       else ensureDir destDir-    when preserveDirPermissions $-      ignoringIOErrors (copyPermissions srcDir destDir)+    pure $ ignoringIOErrors (copyPermissions srcDir destDir)   forM_ files $ \srcFile -> do     destFile <- swapParent bsrc bdest srcFile     fileIsSymlink <- isSymlink srcFile@@ -564,8 +563,9 @@         target <- getSymlinkTarget srcFile         D.createFileLink target (toFilePath destFile)       else copyFile srcFile destFile-  when preserveDirPermissions $+  when preserveDirPermissions $ do     ignoringIOErrors (copyPermissions bsrc bdest)+    sequence_ copyPermissionsIOs  ---------------------------------------------------------------------------- -- Walking directory trees@@ -626,7 +626,7 @@ -- -- @since 1.2.0 walkDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Handler (@dir -> subdirs -> files -> 'WalkAction'@)   (Path Abs Dir -> [Path Abs Dir] -> [Path Abs File] -> m (WalkAction Abs)) ->   -- | Directory where traversal begins@@ -668,7 +668,7 @@ -- -- @since 1.4.2 walkDirRel ::-  MonadIO m =>+  (MonadIO m) =>   -- | Handler (@dir -> subdirs -> files -> 'WalkAction'@)   ( Path Rel Dir ->     [Path Rel Dir] ->@@ -809,7 +809,7 @@ -- -- * 'UnsupportedOperation' -- The operating system has no notion of current working directory.-getCurrentDir :: MonadIO m => m (Path Abs Dir)+getCurrentDir :: (MonadIO m) => m (Path Abs Dir) getCurrentDir = liftIO $ D.getCurrentDirectory >>= parseAbsDir  -- | Change the working directory to the given path.@@ -844,7 +844,7 @@ -- * 'InappropriateType' -- The path refers to an existing non-directory object. -- @[ENOTDIR]@-setCurrentDir :: MonadIO m => Path b Dir -> m ()+setCurrentDir :: (MonadIO m) => Path b Dir -> m () setCurrentDir = liftD D.setCurrentDirectory  -- | Run an 'IO' action with the given working directory and restore the@@ -884,7 +884,7 @@ -- * 'isDoesNotExistError' -- The home directory for the current user does not exist, or -- cannot be found.-getHomeDir :: MonadIO m => m (Path Abs Dir)+getHomeDir :: (MonadIO m) => m (Path Abs Dir) getHomeDir = liftIO D.getHomeDirectory >>= resolveDir'  -- | Obtain the path to a special directory for storing user-specific@@ -911,7 +911,7 @@ --   The home directory for the current user does not exist, or cannot be --   found. getAppUserDataDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Name of application (used in path construction)   String ->   m (Path Abs Dir)@@ -935,7 +935,7 @@ -- * 'isDoesNotExistError' -- The document directory for the current user does not exist, or -- cannot be found.-getUserDocsDir :: MonadIO m => m (Path Abs Dir)+getUserDocsDir :: (MonadIO m) => m (Path Abs Dir) getUserDocsDir = liftIO $ D.getUserDocumentsDirectory >>= parseAbsDir  -- | Return the current directory for temporary files.@@ -963,7 +963,7 @@ -- The operating system has no notion of temporary directory. -- -- The function doesn't verify whether the path exists.-getTempDir :: MonadIO m => m (Path Abs Dir)+getTempDir :: (MonadIO m) => m (Path Abs Dir) getTempDir = liftIO D.getTemporaryDirectory >>= resolveDir'  -- | Obtain the paths to special directories for storing user-specific@@ -983,7 +983,7 @@ -- -- @since 1.2.1 getXdgDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Which special directory   D.XdgDirectory ->   -- | A relative path that is appended to the path; if 'Nothing', the@@ -1003,7 +1003,7 @@ -- -- @since 1.5.0 getXdgDirList ::-  MonadIO m =>+  (MonadIO m) =>   -- | Which special directory list   D.XdgDirectoryList ->   m [Path Abs Dir]@@ -1051,7 +1051,7 @@   -- Please note that before version 1.2.3.0 of the @directory@ package,   -- this function had unpredictable behavior on non-existent paths.   canonicalizePath ::-    MonadIO m =>+    (MonadIO m) =>     path ->     m (AbsPath path) @@ -1061,7 +1061,7 @@   -- If the path is already absolute, the operation never fails. Otherwise,   -- the operation may fail with the same exceptions as 'getCurrentDir'.   makeAbsolute ::-    MonadIO m =>+    (MonadIO m) =>     path ->     m (AbsPath path) @@ -1069,7 +1069,7 @@   --   -- @since 0.3.0   makeRelative ::-    MonadThrow m =>+    (MonadThrow m) =>     -- | Base directory     Path Abs Dir ->     -- | Path that will be made relative to base directory@@ -1080,7 +1080,7 @@   --   -- @since 0.3.0   makeRelativeToCurrentDir ::-    MonadIO m =>+    (MonadIO m) =>     path ->     m (RelPath path) @@ -1149,7 +1149,7 @@ -- -- @since 0.3.0 resolveFile ::-  MonadIO m =>+  (MonadIO m) =>   -- | Base directory   Path Abs Dir ->   -- | Path to resolve@@ -1161,7 +1161,7 @@ -- -- @since 0.3.0 resolveFile' ::-  MonadIO m =>+  (MonadIO m) =>   -- | Path to resolve   FilePath ->   m (Path Abs File)@@ -1171,7 +1171,7 @@ -- -- @since 0.3.0 resolveDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Base directory   Path Abs Dir ->   -- | Path to resolve@@ -1183,7 +1183,7 @@ -- -- @since 0.3.0 resolveDir' ::-  MonadIO m =>+  (MonadIO m) =>   -- | Path to resolve   FilePath ->   m (Path Abs Dir)@@ -1222,7 +1222,7 @@ -- * 'InappropriateType' -- The operand refers to an existing directory. -- @[EPERM, EINVAL]@-removeFile :: MonadIO m => Path b File -> m ()+removeFile :: (MonadIO m) => Path b File -> m () removeFile = liftD D.removeFile  -- | @'renameFile' old new@ changes the name of an existing file system@@ -1266,7 +1266,7 @@ -- Either path refers to an existing directory. -- @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@ renameFile ::-  MonadIO m =>+  (MonadIO m) =>   -- | Original location   Path b0 File ->   -- | New location@@ -1279,7 +1279,7 @@ -- file. Neither path may refer to an existing directory. The permissions of -- @old@ are copied to @new@, if possible. copyFile ::-  MonadIO m =>+  (MonadIO m) =>   -- | Original location   Path b0 File ->   -- | Where to put copy@@ -1290,7 +1290,7 @@ -- | Obtain the size of a file in bytes. -- -- @since 1.7.0-getFileSize :: MonadIO m => Path b File -> m Integer+getFileSize :: (MonadIO m) => Path b File -> m Integer getFileSize = liftD D.getFileSize  -- | Given an executable file name, search for such file in the directories@@ -1308,7 +1308,7 @@ -- the directory containing the current executable. See -- <http://msdn.microsoft.com/en-us/library/aa365527.aspx> for more details. findExecutable ::-  MonadIO m =>+  (MonadIO m) =>   -- | Executable file name   Path Rel File ->   -- | Path to found executable@@ -1317,7 +1317,7 @@  -- | Search through the given set of directories for the given file. findFile ::-  MonadIO m =>+  (MonadIO m) =>   -- | Set of directories to search in   [Path b Dir] ->   -- | Filename of interest@@ -1335,7 +1335,7 @@ -- | Search through the given set of directories for the given file and -- return a list of paths where the given file exists. findFiles ::-  MonadIO m =>+  (MonadIO m) =>   -- | Set of directories to search in   [Path b Dir] ->   -- | Filename of interest@@ -1348,7 +1348,7 @@ -- the given property (usually permissions) and return a list of paths where -- the given file exists and has the property. findFilesWith ::-  MonadIO m =>+  (MonadIO m) =>   -- | How to test the files   (Path Abs File -> m Bool) ->   -- | Set of directories to search in@@ -1397,7 +1397,7 @@ -- -- @since 1.5.0 createFileLink ::-  MonadIO m =>+  (MonadIO m) =>   -- | Path to the target file   Path b0 File ->   -- | Path to the link to be created@@ -1434,7 +1434,7 @@ -- -- @since 1.5.0 createDirLink ::-  MonadIO m =>+  (MonadIO m) =>   -- | Path to the target directory   Path b0 Dir ->   -- | Path to the link to be created@@ -1454,7 +1454,7 @@ -- -- @since 1.5.0 removeDirLink ::-  MonadIO m =>+  (MonadIO m) =>   -- | Path to the link to be removed   Path b Dir ->   m ()@@ -1473,7 +1473,7 @@ -- -- @since 1.5.0 getSymlinkTarget ::-  MonadIO m =>+  (MonadIO m) =>   -- | Symlink path   Path b t ->   m FilePath@@ -1491,7 +1491,7 @@ -- | Check if the given path is a symbolic link. -- -- @since 1.3.0-isSymlink :: MonadIO m => Path b t -> m Bool+isSymlink :: (MonadIO m) => Path b t -> m Bool isSymlink = liftD D.pathIsSymbolicLink  ----------------------------------------------------------------------------@@ -1589,7 +1589,7 @@ -- -- @since 0.2.0 openTempFile ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to create file in   Path b Dir ->   -- | File name template; if the template is "foo.ext" then the created@@ -1612,7 +1612,7 @@ -- -- @since 0.2.0 openBinaryTempFile ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to create file in   Path b Dir ->   -- | File name template, see 'openTempFile'@@ -1632,7 +1632,7 @@ -- -- @since 0.2.0 createTempDir ::-  MonadIO m =>+  (MonadIO m) =>   -- | Directory to create file in   Path b Dir ->   -- | Directory name template, see 'openTempFile'@@ -1652,22 +1652,22 @@ -- this function may return false even if the file does actually exist. -- -- @since 1.7.0-doesPathExist :: MonadIO m => Path b t -> m Bool+doesPathExist :: (MonadIO m) => Path b t -> m Bool doesPathExist = liftD D.doesPathExist  -- | The operation 'doesFileExist' returns 'True' if the argument file -- exists and is not a directory, and 'False' otherwise.-doesFileExist :: MonadIO m => Path b File -> m Bool+doesFileExist :: (MonadIO m) => Path b File -> m Bool doesFileExist = liftD D.doesFileExist  -- | The operation 'doesDirExist' returns 'True' if the argument file exists -- and is either a directory or a symbolic link to a directory, and 'False' -- otherwise.-doesDirExist :: MonadIO m => Path b Dir -> m Bool+doesDirExist :: (MonadIO m) => Path b Dir -> m Bool doesDirExist = liftD D.doesDirectoryExist  -- | Check if there is a file or directory on specified path.-isLocationOccupied :: MonadIO m => Path b t -> m Bool+isLocationOccupied :: (MonadIO m) => Path b t -> m Bool isLocationOccupied path = do   let fp = toFilePath path   file <- liftIO (D.doesFileExist fp)@@ -1704,7 +1704,7 @@ --   the permissions; or -- -- * 'isDoesNotExistError' if the file or directory does not exist.-getPermissions :: MonadIO m => Path b t -> m D.Permissions+getPermissions :: (MonadIO m) => Path b t -> m D.Permissions getPermissions = liftD D.getPermissions  -- | The 'setPermissions' operation sets the permissions for the file or@@ -1716,13 +1716,13 @@ --   the permissions; or -- -- * 'isDoesNotExistError' if the file or directory does not exist.-setPermissions :: MonadIO m => Path b t -> D.Permissions -> m ()+setPermissions :: (MonadIO m) => Path b t -> D.Permissions -> m () setPermissions = liftD2' D.setPermissions  -- | Set permissions for the object found on second given path so they match -- permissions of the object on the first path. copyPermissions ::-  MonadIO m =>+  (MonadIO m) =>   -- | From where to copy   Path b0 t0 ->   -- | What to modify@@ -1748,7 +1748,7 @@ -- -- Note: this is a piece of conditional API, only available if -- @directory-1.2.3.0@ or later is used.-getAccessTime :: MonadIO m => Path b t -> m UTCTime+getAccessTime :: (MonadIO m) => Path b t -> m UTCTime getAccessTime = liftD D.getAccessTime  -- | Change the time at which the file or directory was last accessed.@@ -1775,7 +1775,7 @@ -- -- Note: this is a piece of conditional API, only available if -- @directory-1.2.3.0@ or later is used.-setAccessTime :: MonadIO m => Path b t -> UTCTime -> m ()+setAccessTime :: (MonadIO m) => Path b t -> UTCTime -> m () setAccessTime = liftD2' D.setAccessTime  -- | Change the time at which the file or directory was last modified.@@ -1802,7 +1802,7 @@ -- -- Note: this is a piece of conditional API, only available if -- @directory-1.2.3.0@ or later is used.-setModificationTime :: MonadIO m => Path b t -> UTCTime -> m ()+setModificationTime :: (MonadIO m) => Path b t -> UTCTime -> m () setModificationTime = liftD2' D.setModificationTime  -- | Obtain the time at which the file or directory was last modified.@@ -1817,7 +1817,7 @@ -- Caveat for POSIX systems: This function returns a timestamp with -- sub-second resolution only if this package is compiled against -- @unix-2.6.0.0@ or later and the underlying filesystem supports them.-getModificationTime :: MonadIO m => Path b t -> m UTCTime+getModificationTime :: (MonadIO m) => Path b t -> m UTCTime getModificationTime = liftD D.getModificationTime  ----------------------------------------------------------------------------@@ -1826,7 +1826,7 @@ -- | Lift an action in 'IO' that takes 'FilePath' into an action in slightly -- more abstract monad that takes 'Path'. liftD ::-  MonadIO m =>+  (MonadIO m) =>   -- | Original action   (FilePath -> IO a) ->   -- | 'Path' argument@@ -1838,7 +1838,7 @@  -- | Similar to 'liftD' but for functions with arity 2. liftD2 ::-  MonadIO m =>+  (MonadIO m) =>   -- | Original action   (FilePath -> FilePath -> IO a) ->   -- | First 'Path' argument@@ -1852,7 +1852,7 @@ -- | Similar to 'liftD2', but allows us to pass second argument of arbitrary -- type. liftD2' ::-  MonadIO m =>+  (MonadIO m) =>   -- | Original action   (FilePath -> v -> IO a) ->   -- | First 'Path' argument@@ -1871,5 +1871,5 @@ ignoringIOErrors :: IO () -> IO () ignoringIOErrors ioe = ioe `catch` handler   where-    handler :: Monad m => IOError -> m ()+    handler :: (Monad m) => IOError -> m ()     handler = const (return ())
path-io.cabal view
@@ -1,11 +1,11 @@ cabal-version:   2.4 name:            path-io-version:         1.8.0+version:         1.8.1 license:         BSD-3-Clause license-file:    LICENSE.md maintainer:      Mark Karpov <markkarpov92@gmail.com> author:          Mark Karpov <markkarpov92@gmail.com>-tested-with:     ghc ==9.0.2 ghc ==9.2.4 ghc ==9.4.1+tested-with:     ghc ==9.0.2 ghc ==9.2.5 ghc ==9.4.4 homepage:        https://github.com/mrkkrp/path-io bug-reports:     https://github.com/mrkkrp/path-io/issues synopsis:        Interface to ‘directory’ package for users of ‘path’
tests/Main.hs view
@@ -274,12 +274,21 @@       pdir = root </> $(mkRelDir "pdir")       withinSandbox = (pdir </>)   ensureDir pdir-  ensureDir $ withinSandbox $(mkRelDir "b")+  let b = withinSandbox $(mkRelDir "b")+  ensureDir b   ensureDir $ withinSandbox $(mkRelDir "b/c")-  -- to verify that we do not follow symbolic links. We should not list b's-  -- tree under 'a'.+  -- Create a read-only directory with a file inside to test that the code+  -- that copies directory permissions can handle that gracefully.+  --+  -- See: https://github.com/mrkkrp/path-io/pull/82+  let readonlyDir = withinSandbox $(mkRelDir "readonly-dir")+  ensureDir readonlyDir+  -- We should not list b's tree under 'a' in order to verify that we do not+  -- follow symbolic links.   createSymbolicLink "b" (toFilePath $ withinSandbox $(mkRelFile "a"))   forM_ files $ (`writeFile` "") . toFilePath . withinSandbox+  getPermissions readonlyDir+    >>= setPermissions readonlyDir . setOwnerWritable False   return pdir  -- | Get the inner structure of a directory. Items are sorted, so it's@@ -312,11 +321,13 @@ populatedDirStructure =   ( [ $(mkRelDir "a"),       $(mkRelDir "b"),-      $(mkRelDir "b/c")+      $(mkRelDir "b/c"),+      $(mkRelDir "readonly-dir")     ],     [ $(mkRelFile "b/c/three.txt"),       $(mkRelFile "b/two.txt"),-      $(mkRelFile "one.txt")+      $(mkRelFile "one.txt"),+      $(mkRelFile "readonly-dir/two.txt")     ]   ) @@ -359,26 +370,28 @@   createSymbolicLink "../../e" (toFilePath $ withinSandbox $(mkRelFile "e/f/g"))   return pdir --- | Top-level structure of populated directory as it should be scanned by--- the 'listDir' function.+-- | Top-level structure of the populated directory as it should be scanned+-- by the 'listDir' function. populatedDirTop :: ([Path Rel Dir], [Path Rel File]) populatedDirTop =   ( [ $(mkRelDir "a"),-      $(mkRelDir "b")+      $(mkRelDir "b"),+      $(mkRelDir "readonly-dir")     ],     [ $(mkRelFile "one.txt")     ]   ) --- | Structure of populated directory as it should be scanned by--- 'listDirRecurWith' function using predicates to filter out dir 'c' and the--- file 'two.txt'+-- | Structure of the populated directory as it should be scanned by+-- 'listDirRecurWith' function using predicates to filter out dir 'c' and+-- the file @two.txt@. populatedDirRecurWith :: ([Path Rel Dir], [Path Rel File]) populatedDirRecurWith =   ( [ $(mkRelDir "a"),-      $(mkRelDir "b")+      $(mkRelDir "b"),+      $(mkRelDir "readonly-dir")     ],-    [ $(mkRelFile "a/c/three.txt"), -- via symbolic link+    [ $(mkRelFile "a/c/three.txt"), -- via a symbolic link       $(mkRelFile "b/c/three.txt"),       $(mkRelFile "one.txt")     ]