First off, I'm just learning Haskell and I'm not proficient in that language yet.
I wrote this Haskell module to look for files in a Linux filesystem. It can use regular expressions as search patterns. I wrote two variants: one of them sends the search results to stdout, the other (shown here) gives a list with the search results.
For example (in GHCi): searchToList "conf" "." >>= return . take 1
(being "." my home directory) would give [".config"]
I made the function searchWriterT recursive using mapM_, but I know there is mapWriterT in the module Control.Monad.Writer.
Could anyone give me a hint about what I shall do to replace mapM_ with mapWriterT (if it's possible)?
Thanks in advance.
module FileSearch where
import System.Directory (getDirectoryContents, doesDirectoryExist)
import Control.Monad (filterM, unless)
import Data.List ((\\))
import Text.Regex.Posix ((=~))
import Control.Monad.Writer
data Dir = Dir { path :: FilePath -- path of the current directory
, files :: [FilePath] -- files in the directory
, subdirs :: [FilePath] } -- subdirectories
deriving (Show)
dontSearch :: [FilePath]
dontSearch = ["/proc", "/sys"]
-- listDir takes the name of a directory and gives a Dir with
-- that name, a list of the files in the directory and a list of
-- its subdirectories, all prefixed with the path of the current
-- directory.
-- listDir will list neither /proc nor /sys
listDir :: FilePath -> IO Dir
listDir f
| f `elem` dontSearch = return Dir { path = f
, files = []
, subdirs = [] }
| otherwise = do
c' <- getDirectoryContents f
let c = map (appendPath f) (deleteDot c')
d <- filterM doesDirectoryExist c
return Dir { path = f
, files = (c \\ d)
, subdirs = d }
appendPath :: FilePath -> FilePath -> FilePath
-- if the path is "/", do not add another "/"
appendPath "/" y = concat ["/", y]
appendPath x y = concat [x, "/", y]
-- remove "." and ".." entries in directories
deleteDot :: [FilePath] -> [FilePath]
deleteDot = filter (`notElem` [".", ".."])
searchWriterT :: String -> FilePath -> WriterT [FilePath] IO ()
searchWriterT s f = do
d <- liftIO $ listDir f
let found = filter (=~ s) (files d)
tell found
unless (null (subdirs d)) $
mapM_ (searchWriterT s) (subdirs d)
searchToList :: String -> FilePath -> IO [FilePath]
searchToList s f = execWriterT (searchWriterT s f)