{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
module System.FSNotify.Path
( findFiles
, findDirs
, findFilesAndDirs
, canonicalizeDirPath
, canonicalizePath
, hasThisExtension
) where
import Control.Monad
import qualified Data.Text as T
import Prelude hiding (FilePath)
import qualified System.Directory as D
import System.FilePath
import System.PosixCompat.Files as PF
getDirectoryContentsPath :: FilePath -> IO [FilePath]
getDirectoryContentsPath :: FilePath -> IO [FilePath]
getDirectoryContentsPath FilePath
path =
(((FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (FilePath
path FilePath -> FilePath -> FilePath
</>)) ([FilePath] -> [FilePath])
-> ([FilePath] -> [FilePath]) -> [FilePath] -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath -> Bool) -> [FilePath] -> [FilePath]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (FilePath -> Bool) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Bool
dots) ([FilePath] -> [FilePath]) -> IO [FilePath] -> IO [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO [FilePath]
D.getDirectoryContents FilePath
path) IO [FilePath] -> ([FilePath] -> IO [FilePath]) -> IO [FilePath]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (FilePath -> IO Bool) -> [FilePath] -> IO [FilePath]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM FilePath -> IO Bool
exists
where
exists :: FilePath -> IO Bool
exists FilePath
x = Bool -> Bool -> Bool
(||) (Bool -> Bool -> Bool) -> IO Bool -> IO (Bool -> Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO Bool
D.doesFileExist FilePath
x IO (Bool -> Bool) -> IO Bool -> IO Bool
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> FilePath -> IO Bool
D.doesDirectoryExist FilePath
x
dots :: FilePath -> Bool
dots FilePath
"." = Bool
True
dots FilePath
".." = Bool
True
dots FilePath
_ = Bool
False
fileDirContents :: FilePath -> IO ([FilePath], [FilePath])
fileDirContents :: FilePath -> IO ([FilePath], [FilePath])
fileDirContents FilePath
path = do
[FilePath]
contents <- FilePath -> IO [FilePath]
getDirectoryContentsPath FilePath
path
[FileStatus]
stats <- (FilePath -> IO FileStatus) -> [FilePath] -> IO [FileStatus]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FilePath -> IO FileStatus
getFileStatus [FilePath]
contents
let pairs :: [(FileStatus, FilePath)]
pairs = [FileStatus] -> [FilePath] -> [(FileStatus, FilePath)]
forall a b. [a] -> [b] -> [(a, b)]
zip [FileStatus]
stats [FilePath]
contents
let files :: [FilePath]
files = [ FilePath
f | (FileStatus
s, FilePath
f) <- [(FileStatus, FilePath)]
pairs, FileStatus -> Bool
PF.isRegularFile FileStatus
s]
let dirs :: [FilePath]
dirs = [ FilePath
d | (FileStatus
s, FilePath
d) <- [(FileStatus, FilePath)]
pairs, FileStatus -> Bool
PF.isDirectory FileStatus
s]
([FilePath], [FilePath]) -> IO ([FilePath], [FilePath])
forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath]
files, [FilePath]
dirs)
findAllFiles :: FilePath -> IO [FilePath]
findAllFiles :: FilePath -> IO [FilePath]
findAllFiles FilePath
path = do
([FilePath]
files, [FilePath]
dirs) <- FilePath -> IO ([FilePath], [FilePath])
fileDirContents FilePath
path
[[FilePath]]
nestedFiles <- (FilePath -> IO [FilePath]) -> [FilePath] -> IO [[FilePath]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FilePath -> IO [FilePath]
findAllFiles [FilePath]
dirs
[FilePath] -> IO [FilePath]
forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath]
files [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [[FilePath]] -> [FilePath]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[FilePath]]
nestedFiles)
findImmediateFiles, findImmediateDirs :: FilePath -> IO [FilePath]
findImmediateFiles :: FilePath -> IO [FilePath]
findImmediateFiles = FilePath -> IO ([FilePath], [FilePath])
fileDirContents (FilePath -> IO ([FilePath], [FilePath]))
-> (([FilePath], [FilePath]) -> IO [FilePath])
-> FilePath
-> IO [FilePath]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> (FilePath -> IO FilePath) -> [FilePath] -> IO [FilePath]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FilePath -> IO FilePath
D.canonicalizePath ([FilePath] -> IO [FilePath])
-> (([FilePath], [FilePath]) -> [FilePath])
-> ([FilePath], [FilePath])
-> IO [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([FilePath], [FilePath]) -> [FilePath]
forall a b. (a, b) -> a
fst
findImmediateDirs :: FilePath -> IO [FilePath]
findImmediateDirs = FilePath -> IO ([FilePath], [FilePath])
fileDirContents (FilePath -> IO ([FilePath], [FilePath]))
-> (([FilePath], [FilePath]) -> IO [FilePath])
-> FilePath
-> IO [FilePath]
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> (FilePath -> IO FilePath) -> [FilePath] -> IO [FilePath]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FilePath -> IO FilePath
D.canonicalizePath ([FilePath] -> IO [FilePath])
-> (([FilePath], [FilePath]) -> [FilePath])
-> ([FilePath], [FilePath])
-> IO [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([FilePath], [FilePath]) -> [FilePath]
forall a b. (a, b) -> b
snd
findAllDirs :: FilePath -> IO [FilePath]
findAllDirs :: FilePath -> IO [FilePath]
findAllDirs FilePath
path = do
[FilePath]
dirs <- FilePath -> IO [FilePath]
findImmediateDirs FilePath
path
[[FilePath]]
nestedDirs <- (FilePath -> IO [FilePath]) -> [FilePath] -> IO [[FilePath]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FilePath -> IO [FilePath]
findAllDirs [FilePath]
dirs
[FilePath] -> IO [FilePath]
forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath]
dirs [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [[FilePath]] -> [FilePath]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[FilePath]]
nestedDirs)
findFiles :: Bool -> FilePath -> IO [FilePath]
findFiles :: Bool -> FilePath -> IO [FilePath]
findFiles Bool
True FilePath
path = FilePath -> IO [FilePath]
findAllFiles (FilePath -> IO [FilePath]) -> IO FilePath -> IO [FilePath]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< FilePath -> IO FilePath
canonicalizeDirPath FilePath
path
findFiles Bool
False FilePath
path = FilePath -> IO [FilePath]
findImmediateFiles (FilePath -> IO [FilePath]) -> IO FilePath -> IO [FilePath]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< FilePath -> IO FilePath
canonicalizeDirPath FilePath
path
findDirs :: Bool -> FilePath -> IO [FilePath]
findDirs :: Bool -> FilePath -> IO [FilePath]
findDirs Bool
True FilePath
path = FilePath -> IO [FilePath]
findAllDirs (FilePath -> IO [FilePath]) -> IO FilePath -> IO [FilePath]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< FilePath -> IO FilePath
canonicalizeDirPath FilePath
path
findDirs Bool
False FilePath
path = FilePath -> IO [FilePath]
findImmediateDirs (FilePath -> IO [FilePath]) -> IO FilePath -> IO [FilePath]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< FilePath -> IO FilePath
canonicalizeDirPath FilePath
path
findFilesAndDirs :: Bool -> FilePath -> IO [FilePath]
findFilesAndDirs :: Bool -> FilePath -> IO [FilePath]
findFilesAndDirs Bool
False FilePath
path = FilePath -> IO [FilePath]
getDirectoryContentsPath (FilePath -> IO [FilePath]) -> IO FilePath -> IO [FilePath]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< FilePath -> IO FilePath
canonicalizeDirPath FilePath
path
findFilesAndDirs Bool
True FilePath
path = do
([FilePath]
files, [FilePath]
dirs) <- FilePath -> IO ([FilePath], [FilePath])
fileDirContents FilePath
path
[FilePath]
nestedFilesAndDirs <- [[FilePath]] -> [FilePath]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[FilePath]] -> [FilePath]) -> IO [[FilePath]] -> IO [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((FilePath -> IO [FilePath]) -> [FilePath] -> IO [[FilePath]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (Bool -> FilePath -> IO [FilePath]
findFilesAndDirs Bool
False) [FilePath]
dirs)
[FilePath] -> IO [FilePath]
forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath]
files [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
dirs [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
nestedFilesAndDirs)
addTrailingSlash :: FilePath -> FilePath
addTrailingSlash :: FilePath -> FilePath
addTrailingSlash = FilePath -> FilePath
addTrailingPathSeparator
canonicalizeDirPath :: FilePath -> IO FilePath
canonicalizeDirPath :: FilePath -> IO FilePath
canonicalizeDirPath FilePath
path = FilePath -> FilePath
addTrailingSlash (FilePath -> FilePath) -> IO FilePath -> IO FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` FilePath -> IO FilePath
D.canonicalizePath FilePath
path
canonicalizePath :: FilePath -> IO FilePath
canonicalizePath :: FilePath -> IO FilePath
canonicalizePath FilePath
path = let was_dir :: Bool
was_dir = FilePath -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (FilePath -> FilePath
takeFileName FilePath
path) in
if Bool -> Bool
not Bool
was_dir then FilePath -> IO FilePath
D.canonicalizePath FilePath
path
else FilePath -> IO FilePath
canonicalizeDirPath FilePath
path
hasThisExtension :: FilePath -> T.Text -> Bool
hasThisExtension :: FilePath -> Text -> Bool
hasThisExtension FilePath
p Text
ext = FilePath -> FilePath
takeExtension FilePath
p FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> FilePath
T.unpack Text
ext