fsnotify-0.0.1: src/System/FSNotify/Path.hs
--
-- Copyright (c) 2012 Mark Dittmer - http://www.markdittmer.org
-- Developed for a Google Summer of Code project - http://gsoc2012.markdittmer.org
--
{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
module System.FSNotify.Path
( fp
, findFiles
, findDirs
, canonicalizeDirPath
, canonicalizePath
) where
import Prelude hiding (FilePath)
import Control.Monad
-- import Filesystem
-- import Filesystem.Path hiding (concat)
import Filesystem.Path.CurrentOS hiding (concat)
import qualified Filesystem as FS
import qualified Filesystem.Path as FP
-- This will ensure than any calls to fp for type coercion in FSNotify will not
-- break when/if the dependent package moves from using String to the more
-- efficient Filesystem.Path.FilePath
class ConvertFilePath a b where
fp :: a -> b
instance ConvertFilePath FilePath String where fp = encodeString
instance ConvertFilePath String FilePath where fp = decodeString
instance ConvertFilePath String String where fp = id
instance ConvertFilePath FilePath FilePath where fp = id
getDirectoryContentsPath :: FilePath -> IO [FilePath]
getDirectoryContentsPath path = fmap (map (path </>)) $ FS.listDirectory path
fileDirContents :: FilePath -> IO ([FilePath],[FilePath])
fileDirContents path = do
contents <- getDirectoryContentsPath path
files <- filterM FS.isFile contents
dirs <- filterM FS.isDirectory contents
return (files, dirs)
findAllFiles :: FilePath -> IO [FilePath]
findAllFiles path = do
(files, dirs) <- fileDirContents path
nestedFiles <- mapM findAllFiles dirs
return (files ++ concat nestedFiles)
findImmediateFiles, findImmediateDirs :: FilePath -> IO [FilePath]
findImmediateFiles = getDirectoryContentsPath >=> filterM FS.isFile >=> canonicalize
where
canonicalize :: [FilePath] -> IO [FilePath]
canonicalize files = mapM FS.canonicalizePath files
findImmediateDirs = getDirectoryContentsPath >=> filterM FS.isDirectory >=> canonicalize
where
canonicalize :: [FilePath] -> IO [FilePath]
canonicalize dirs = mapM canonicalizeDirPath dirs
findAllDirs :: FilePath -> IO [FilePath]
findAllDirs path = do
dirs <- findImmediateDirs path
nestedDirs <- mapM findAllDirs dirs
return (dirs ++ concat nestedDirs)
findFiles :: Bool -> FilePath -> IO [FilePath]
findFiles True path = findAllFiles =<< canonicalizeDirPath path
findFiles False path = findImmediateFiles =<< canonicalizeDirPath path
findDirs :: Bool -> FilePath -> IO [FilePath]
findDirs True path = findAllDirs =<< canonicalizeDirPath path
findDirs False path = findImmediateDirs =<< canonicalizeDirPath path
-- | add a trailing slash to ensure the path indicates a directory
addTrailingSlash :: FilePath -> FilePath
addTrailingSlash p =
if FP.null (FP.filename p) then p else
p FP.</> FP.empty
canonicalizeDirPath :: FilePath -> IO FilePath
canonicalizeDirPath path = addTrailingSlash `fmap` FS.canonicalizePath path
-- | bugfix older version of canonicalizePath (system-fileio <= 0.3.7) loses trailing slash
canonicalizePath :: FilePath -> IO FilePath
canonicalizePath path = let was_dir = FP.null (FP.filename path) in
if not was_dir then FS.canonicalizePath path
else canonicalizeDirPath path