posix-paths 0.2.1.6 → 0.3.0.0
raw patch · 2 files changed
+78/−41 lines, 2 filesdep +unliftioPVP ok
version bump matches the API change (PVP)
Dependencies added: unliftio
API changes (from Hackage documentation)
+ System.Posix.Directory.Traversals: traverseDirectoryContents :: MonadUnliftIO m => (s -> (DirType, RawFilePath) -> m s) -> s -> RawFilePath -> m s
- System.Posix.Directory.Traversals: traverseDirectory :: (s -> RawFilePath -> IO s) -> s -> RawFilePath -> IO s
+ System.Posix.Directory.Traversals: traverseDirectory :: MonadUnliftIO m => (s -> RawFilePath -> m s) -> s -> RawFilePath -> m s
Files
posix-paths.cabal view
@@ -1,5 +1,5 @@ name: posix-paths-version: 0.2.1.6+version: 0.3.0.0 license: BSD3 license-file: LICENSE maintainer: jwlato@gmail.com@@ -27,7 +27,8 @@ System.Posix.FilePath build-depends: base >= 4.2 && < 5, bytestring >= 0.9.2.0,- unix >= 2.5.1.0+ unix >= 2.5.1.0,+ unliftio >= 0.2 test-suite doctests default-language: Haskell2010
src/System/Posix/Directory/Traversals.hs view
@@ -7,6 +7,7 @@ module System.Posix.Directory.Traversals ( getDirectoryContents+, traverseDirectoryContents , allDirectoryContents , allDirectoryContents'@@ -22,16 +23,18 @@ import Control.Applicative import Control.Monad+import Control.Monad.IO.Class (liftIO) import System.Posix.FilePath ((</>)) import System.Posix.Directory.Foreign import qualified System.Posix as Posix import System.IO.Error-import Control.Exception import qualified Data.ByteString.Char8 as BS import System.Posix.ByteString.FilePath import System.Posix.Directory.ByteString as PosixBS import System.Posix.Files.ByteString+import UnliftIO (MonadUnliftIO, withRunInIO)+import UnliftIO.Exception import System.IO.Unsafe import Unsafe.Coerce (unsafeCoerce)@@ -71,20 +74,25 @@ -- this uses traverseDirectory because it's more efficient than forcing the -- lazy version. --- | Recursively apply the 'action' to the parent directory and all+-- | Recursively apply the 'action' to the parent file or directory and all -- files/subdirectories. --+-- Like UNIX @find@, this includes the parent file/directory!+--+-- As for @find@, emitted file paths of subdirectories contain slashes,+-- starting with the parent directory.+-- -- This function allows for memory-efficient traversals.-traverseDirectory :: (s -> RawFilePath -> IO s) -> s -> RawFilePath -> IO s-traverseDirectory act s0 topdir = toploop+traverseDirectory :: (MonadUnliftIO m) => (s -> RawFilePath -> m s) -> s -> RawFilePath -> m s+traverseDirectory act s0 topDirOrFile = toploop where toploop = do- isDir <- isDirectory <$> getFileStatus topdir- s' <- act s0 topdir- if isDir then actOnDirContents topdir s' loop+ isDir <- liftIO $ isDirectory <$> getFileStatus topDirOrFile+ s' <- act s0 topDirOrFile+ if isDir then actOnDirContents topDirOrFile s' loop else return s' loop typ path acc = do- isDir <- case () of+ isDir <- liftIO $ case () of () | typ == dtDir -> return True | typ == dtUnknown -> isDirectory <$> getFileStatus path | otherwise -> return False@@ -92,27 +100,22 @@ then act acc path >>= \acc' -> actOnDirContents path acc' loop else act acc path -actOnDirContents :: RawFilePath+actOnDirContents :: (MonadUnliftIO m)+ => RawFilePath -> b- -> (DirType -> RawFilePath -> b -> IO b)- -> IO b+ -> (DirType -> RawFilePath -> b -> m b)+ -> m b actOnDirContents pathRelToTop b f =- modifyIOError ((`ioeSetFileName` (BS.unpack pathRelToTop)) .- (`ioeSetLocation` "findBSTypRel")) $ do- bracket- (openDirStream pathRelToTop)- (Posix.closeDirStream)- (\dirp -> loop dirp b)- where- loop dirp b' = do- (typ,e) <- readDirEnt dirp- if (e == "")- then return b'- else do- if (e == "." || e == "..")- then loop dirp b'- else f typ (pathRelToTop </> e) b' >>= loop dirp+ traverseDirectoryContents+ (\b' (typ, e) -> f typ (pathRelToTop </> e) b')+ b+ pathRelToTop +-- | `withRunInIO` lifted to `MonadUnliftIO`.+modifyIOErrorUnliftIO :: (MonadUnliftIO m) => (IOError -> IOError) -> m a -> m a+modifyIOErrorUnliftIO f action =+ withRunInIO $ \runInIO -> do+ modifyIOError f (runInIO action) ---------------------------------------------------------- -- dodgy stuff@@ -131,7 +134,13 @@ -- the __hscore_* functions are defined in the unix package. We can import them and let -- the linker figure it out.-foreign import ccall unsafe "__hscore_readdir"+--+-- In contrast to current `unix` we use `safe` calls for anything that+-- does file system IO, because it can take a substantial amount of time+-- on spinning disks or networked file systems, and `unsafe` calls block+-- a capability.+-- See https://github.com/haskell/unix/issues/34.+foreign import ccall safe "__hscore_readdir" c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt foreign import ccall unsafe "__hscore_free_dirent"@@ -176,20 +185,47 @@ then return (dtUnknown,BS.empty) else throwErrno "readDirEnt" +-- | Apply the 'action' to the given directory (must be a directory),+-- without recursing into subdirectories.+--+-- This function does filter out the @.@ and @..@ entries.+--+-- Emitted file paths are the directory entry names,+-- thus not prefixed with the given parent directory.+--+-- This function allows for memory-efficient traversals.+--+-- Use this if you want to implement your own recursive subdirectory+-- traversal, deciding e.g. into which directories or symlinks to traverse.+--+-- You SHOULD check if the obtained 'DirType'+-- is 'System.Posix.Directory.Foreign.dtUnknown'+-- (see comments in @man 3 readdir@ on @d_type@),+-- and do a 'System.Posix.Files.ByteString.getFileStatus'+-- in that case (results in @stat()@), as not all file systems+-- implement obtaining a 'DirType'.+traverseDirectoryContents :: (MonadUnliftIO m) => (s -> (DirType, RawFilePath) -> m s) -> s -> RawFilePath -> m s+traverseDirectoryContents act state path =+ modifyIOErrorUnliftIO+ ((`ioeSetFileName` (BS.unpack path)) .+ (`ioeSetLocation` "System.Posix.Directory.Traversals.traverseDirectoryContents")) $ do+ bracket+ (liftIO $ PosixBS.openDirStream path)+ (liftIO . PosixBS.closeDirStream)+ (\dirp -> loop state dirp)+ where+ loop state0 dirp = do+ t@(_typ,e) <- liftIO $ readDirEnt dirp+ if BS.null e then return state0 else do+ if (e == "." || e == "..")+ then loop state0 dirp+ else do+ state1 <- act state0 t+ loop state1 dirp+ getDirectoryContents :: RawFilePath -> IO [(DirType, RawFilePath)] getDirectoryContents path =- modifyIOError ((`ioeSetFileName` (BS.unpack path)) .- (`ioeSetLocation` "System.Posix.Directory.Traversals.getDirectoryContents")) $ do- bracket- (PosixBS.openDirStream path)- PosixBS.closeDirStream- loop- where- loop dirp = do- t@(_typ,e) <- readDirEnt dirp- if BS.null e then return [] else do- es <- loop dirp- return (t:es)+ traverseDirectoryContents (\l e -> pure (e:l)) [] path -- | return the canonicalized absolute pathname --