module System.Directory.PathWalk
( Callback
, pathWalk
, WalkStatus(..)
, pathWalkInterruptible
, pathWalkAccumulate
, pathWalkLazy
) where
#if __GLASGOW_HASKELL__ < 710
import Data.Monoid (Monoid)
#endif
import Control.Monad (forM, forM_, filterM)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Writer.Lazy (runWriterT, tell)
import System.Directory (doesDirectoryExist, doesFileExist, getDirectoryContents)
import System.FilePath ((</>))
import System.IO.Unsafe (unsafeInterleaveIO)
type Callback m a = FilePath -> [FilePath] -> [FilePath] -> m a
pathWalk :: MonadIO m => FilePath -> Callback m () -> m ()
pathWalk root callback = do
pathWalkInterruptible root $ \dir dirs files -> do
callback dir dirs files
return Continue
data WalkStatus
= Continue
| StopRecursing
| Stop
deriving (Show, Eq)
readDirsAndFiles :: FilePath -> IO ([FilePath], [FilePath])
readDirsAndFiles root = do
names <- getDirectoryContents root
let properNames = filter (`notElem` [".", ".."]) names
dirs <- filterM (\n -> doesDirectoryExist $ root </> n) properNames
files <- filterM (\n -> doesFileExist $ root </> n) properNames
return (dirs, files)
pathWalkInternal :: MonadIO m => FilePath -> Callback m WalkStatus -> m (Maybe ())
pathWalkInternal root callback = do
(dirs, files) <- liftIO $ readDirsAndFiles root
result <- callback root dirs files
case result of
Continue -> do
runMaybeT $ do
forM_ dirs $ \dir -> do
MaybeT $ pathWalkInternal (root </> dir) callback
StopRecursing -> do
return $ Just ()
Stop -> do
return Nothing
pathWalkInterruptible :: MonadIO m => FilePath -> Callback m WalkStatus -> m ()
pathWalkInterruptible root callback = do
_ <- pathWalkInternal root callback
return ()
pathWalkAccumulate :: (MonadIO m, Monoid o) => FilePath -> Callback m o -> m o
pathWalkAccumulate root callback = do
((), result) <- runWriterT $ do
pathWalk root $ \dir dirs files -> do
r <- lift $ callback dir dirs files
tell r
return result
pathWalkLazy :: MonadIO m => FilePath -> m [(FilePath, [FilePath], [FilePath])]
pathWalkLazy root = liftIO $ unsafeInterleaveIO $ do
(dirs, files) <- readDirsAndFiles root
next <- unsafeInterleaveIO $ do
allsubs <- forM dirs $ \dir -> do
pathWalkLazy $ root </> dir
return $ concat allsubs
return $ (root, dirs, files) : next